[hibernate-commits] Hibernate SVN: r10678 - in branches/Lucene_Integration/HibernateExt/metadata/src: java/org/hibernate/lucene java/org/hibernate/lucene/query test/org/hibernate/lucene/test/query

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Nov 1 16:32:13 EST 2006


Author: epbernard
Date: 2006-11-01 16:32:11 -0500 (Wed, 01 Nov 2006)
New Revision: 10678

Modified:
   branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java
   branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/IteratorImpl.java
   branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/LuceneQueryImpl.java
   branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/query/LuceneQueryTest.java
Log:
ANN-460 filter by classes on queries

Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java	2006-11-01 19:07:24 UTC (rev 10677)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java	2006-11-01 21:32:11 UTC (rev 10678)
@@ -219,7 +219,7 @@
         }
         {
             Field classField =
-                    new Field( CLASS_FIELDNAME, instanceClass.getName(), Field.Store.YES, Field.Index.NO );
+                    new Field( CLASS_FIELDNAME, instanceClass.getName(), Field.Store.YES, Field.Index.UN_TOKENIZED );
             doc.add( classField );
             idBridge.set( idKeywordName, id, doc, Field.Store.YES, Field.Index.UN_TOKENIZED, idBoost );
         }

Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/IteratorImpl.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/IteratorImpl.java	2006-11-01 19:07:24 UTC (rev 10677)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/IteratorImpl.java	2006-11-01 21:32:11 UTC (rev 10678)
@@ -2,6 +2,7 @@
 package org.hibernate.lucene.query;
 
 import java.util.Iterator;
+import java.util.List;
 
 import org.hibernate.Session;
 
@@ -10,15 +11,15 @@
  */
 public class IteratorImpl implements Iterator {
 
-	private final EntityInfo[] entityInfos;
+	private final List<EntityInfo> entityInfos;
 	private final Session session;
 	private int index = 0;
 	private final int size;
 
-	public IteratorImpl(EntityInfo[] entityInfos, Session session) {
+	public IteratorImpl(List<EntityInfo> entityInfos, Session session) {
 		this.entityInfos = entityInfos;
 		this.session = session;
-		this.size = entityInfos.length;
+		this.size = entityInfos.size();
 	}
 
 	public boolean hasNext() {
@@ -26,13 +27,13 @@
 	}
 
 	public Object next() {
-		Object object = session.get( entityInfos[index].clazz, entityInfos[index].id );
+		Object object = session.get( entityInfos.get(index).clazz, entityInfos.get(index).id );
 		index++;
 		return object;
 	}
 
 	public void remove() {
 		//TODO this is theorically doable
-		throw new UnsupportedOperationException( "Cannot remove from a lucene query interator" );
+		throw new UnsupportedOperationException( "Cannot remove from a lucene query iterator" );
 	}
 }

Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/LuceneQueryImpl.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/LuceneQueryImpl.java	2006-11-01 19:07:24 UTC (rev 10677)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/LuceneQueryImpl.java	2006-11-01 21:32:11 UTC (rev 10678)
@@ -18,7 +18,11 @@
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.MultiSearcher;
 import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.store.Directory;
+import org.apache.lucene.index.Term;
 import org.hibernate.Hibernate;
 import org.hibernate.HibernateException;
 import org.hibernate.LockMode;
@@ -41,6 +45,7 @@
 	private static final Log log = LogFactory.getLog( LuceneQueryImpl.class );
 	private org.apache.lucene.search.Query luceneQuery;
 	private Class[] classes;
+	private Set<Class> classesAndSubclasses;
 	private Integer firstResult;
 	private Integer maxResults;
 	private int resultSize;
@@ -71,18 +76,18 @@
 		Searcher searcher = buildSearcher( listener );
 		Hits hits;
 		try {
-			hits = searcher.search( luceneQuery );
+			org.apache.lucene.search.Query query = filterQueryByClasses(luceneQuery);
+			hits = searcher.search( query );
 			setResultSize( hits );
 			int first = first();
 			int max = max( first, hits );
-			EntityInfo[] entityInfos = new EntityInfo[max - first + 1];
+			List<EntityInfo> entityInfos = new ArrayList<EntityInfo>(max - first + 1);
 			for ( int index = first; index <= max; index++ ) {
 				Document document = hits.doc( index );
 				EntityInfo entityInfo = new EntityInfo();
 				entityInfo.clazz = DocumentBuilder.getDocumentClass( document );
-				//FIXME should check that clazz match classes but this complexify a lot the firstResult/maxResult
 				entityInfo.id = DocumentBuilder.getDocumentId( listener, entityInfo.clazz, document );
-				entityInfos[index - first] = entityInfo;
+				entityInfos.add(entityInfo);
 			}
 			return new IteratorImpl( entityInfos, (Session) this.session );
 		}
@@ -106,7 +111,8 @@
 		Searcher searcher = buildSearcher( listener );
 		Hits hits;
 		try {
-			hits = searcher.search( luceneQuery );
+			org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
+			hits = searcher.search( query );
 			setResultSize( hits );
 			int first = first();
 			int max = max( first, hits );
@@ -134,7 +140,9 @@
 		Searcher searcher = buildSearcher( listener );
 		Hits hits;
 		try {
-			hits = searcher.search( luceneQuery );
+			org.apache.lucene.search.Query query = filterQueryByClasses(luceneQuery);
+			log.warn(query.toString());
+			hits = searcher.search( query );
 			setResultSize( hits );
 			int first = first();
 			int max = max( first, hits );
@@ -143,10 +151,10 @@
 			for ( int index = first; index <= max; index++ ) {
 				Document document = hits.doc( index );
 				Class clazz = DocumentBuilder.getDocumentClass( document );
-				//FIXME should check that clazz match classes but this complexify a lot the firstResult/maxResult
 				Serializable id = DocumentBuilder.getDocumentId( listener, clazz, document );
 				result.add( sess.load( clazz, id ) );
-				//use load to benefit from the batch-size (but facing some proxy casting issues...
+				//use load to benefit from the batch-size
+				//we don't face proxy casting issues since the exact class is extracted from the index
 			}
 			//then initialize the objects
 			for ( Object element : result ) {
@@ -167,11 +175,33 @@
 		}
 	}
 
+	private org.apache.lucene.search.Query filterQueryByClasses(org.apache.lucene.search.Query luceneQuery) {
+		//A query filter is more practical than a manual class filtering post query (esp on scrollable resultsets)
+		//it also probably minimise the memory footprint
+		if (classesAndSubclasses == null) {
+			return luceneQuery;
+		}
+		else {
+			BooleanQuery classFilter = new BooleanQuery();
+			//annihilate the scoring impact of DocumentBuilder.CLASS_FIELDNAME
+			classFilter.setBoost( 0 );
+			for (Class clazz : classesAndSubclasses) {
+				Term t = new Term( DocumentBuilder.CLASS_FIELDNAME, clazz.getName() );
+				TermQuery termQuery = new TermQuery( t );
+				classFilter.add( termQuery, BooleanClause.Occur.SHOULD );
+			}
+			BooleanQuery filteredQuery = new BooleanQuery();
+			filteredQuery.add( luceneQuery, BooleanClause.Occur.MUST );
+			filteredQuery.add( classFilter, BooleanClause.Occur.MUST );
+			return filteredQuery;
+		}
+	}
+
 	private int max(int first, Hits hits) {
 		return maxResults == null ?
-				first + hits.length() - 1 :
+				hits.length() - 1 :
 				maxResults + first < hits.length() ?
-						first + maxResults :
+						first + maxResults - 1 :
 						hits.length() - 1;
 	}
 
@@ -181,6 +211,7 @@
 				0;
 	}
 
+	//TODO change classesAndSubclasses by side effect, which is a mismatch with the Searcher return, fix that.
 	private Searcher buildSearcher(LuceneEventListener listener) {
 		Map<Class, DocumentBuilder<Object>> builders = listener.getDocumentBuilders();
 		Set<Directory> directories = new HashSet<Directory>();
@@ -189,6 +220,7 @@
 			for ( DocumentBuilder builder : builders.values() ) {
 				directories.add( builder.getDirectoryProvider().getDirectory() );
 			}
+			classesAndSubclasses = null;
 		}
 		else {
 			Set<Class> involvedClasses = new HashSet<Class>( classes.length );
@@ -203,6 +235,7 @@
 				if ( builder == null ) throw new HibernateException( "Not a mapped entity: " + clazz );
 				directories.add( builder.getDirectoryProvider().getDirectory() );
 			}
+			classesAndSubclasses = involvedClasses;
 		}
 
 		//set up the searcher

Modified: branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/query/LuceneQueryTest.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/query/LuceneQueryTest.java	2006-11-01 19:07:24 UTC (rev 10677)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/query/LuceneQueryTest.java	2006-11-01 21:32:11 UTC (rev 10678)
@@ -56,11 +56,57 @@
 			assertTrue( Hibernate.isInitialized( element ) );
 			s.delete( element );
 		}
-		for (Object element : s.createQuery( "from Book" ).list() ) s.delete( element );
+		for (Object element : s.createQuery( "from java.lang.Object" ).list() ) s.delete( element );
 		tx.commit();
 		s.close();
 	}
 
+	public void testFirstMax() throws Exception {
+		LuceneSession s = new LuceneSession( openSession() );
+		Transaction tx = s.beginTransaction();
+		Clock clock = new Clock(1, "Seiko");
+		s.save( clock );
+		clock = new Clock( 2, "Festina");
+		s.save( clock );
+		Book book = new Book(1, "La chute de la petite reine a travers les yeux de Festina", "La chute de la petite reine a travers les yeux de Festina, blahblah");
+		s.save(book);
+		book = new Book(2, "La gloire de mon père", "Les deboires de mon père en vélo");
+		s.save(book);
+		tx.commit();
+		s.clear();
+		tx = s.beginTransaction();
+		QueryParser parser = new QueryParser("title", new StopAnalyzer() );
+
+		Query query = parser.parse( "summary:Festina Or brand:Seiko" );
+		org.hibernate.Query hibQuery = s.createLuceneQuery( query, Clock.class, Book.class );
+		hibQuery.setFirstResult( 1 );
+		List result = hibQuery.list();
+		assertNotNull( result );
+		assertEquals( "first result no max result", 1, result.size() );
+
+		hibQuery.setFirstResult( 0 );
+		hibQuery.setMaxResults( 1 );
+		result = hibQuery.list();
+		assertNotNull( result );
+		assertEquals( "max result set", 1, result.size() );
+
+		hibQuery.setFirstResult( 0 );
+		hibQuery.setMaxResults( 3 );
+		result = hibQuery.list();
+		assertNotNull( result );
+		assertEquals( "max result out of limit", 2, result.size() );
+
+		hibQuery.setFirstResult( 2 );
+		hibQuery.setMaxResults( 3 );
+		result = hibQuery.list();
+		assertNotNull( result );
+		assertEquals( "first result out of limit", 0, result.size() );
+		
+		for (Object element : s.createQuery( "from java.lang.Object" ).list() ) s.delete( element );
+		tx.commit();
+		s.close();
+	}
+
 	public void testIterator() throws Exception {
 		LuceneSession s = new LuceneSession( openSession() );
 		Transaction tx = s.beginTransaction();
@@ -93,7 +139,7 @@
 			s.delete( result.next() );
 		}
 		assertEquals( 2, index );
-		for (Object element : s.createQuery( "from Book" ).list() ) s.delete( element );
+		for (Object element : s.createQuery( "from java.lang.Object" ).list() ) s.delete( element );
 		tx.commit();
 		s.close();
 	}
@@ -136,7 +182,7 @@
 		while ( result.next() ) {
 			s.delete( result.get()[0] );
 		}
-		for (Object element : s.createQuery( "from Book" ).list() ) s.delete( element );
+		for (Object element : s.createQuery( "from java.lang.Object" ).list() ) s.delete( element );
 		tx.commit();
 		s.close();
 	}
@@ -144,6 +190,8 @@
 	public void testMultipleEntityPerIndex() throws Exception {
 		LuceneSession s = new LuceneSession( openSession() );
 		Transaction tx = s.beginTransaction();
+		Clock clock = new Clock(1, "Seiko");
+		s.save( clock );
 		Book book = new Book(1, "La chute de la petite reine a travers les yeux de Festina", "La chute de la petite reine a travers les yeux de Festina, blahblah");
 		s.save(book);
 		AlternateBook alternateBook = new AlternateBook(1, "La chute de la petite reine a travers les yeux de Festina");
@@ -158,8 +206,30 @@
 		List result = hibQuery.list();
 		assertNotNull( result );
 		assertEquals( "Query with explicit class filter", 1, result.size() );
+		
+		query = parser.parse( "summary:Festina" );
+		hibQuery = s.createLuceneQuery( query, Clock.class, Book.class );
+		Iterator it = hibQuery.iterate();
+		assertTrue( it.hasNext() );
+		assertNotNull( it.next() );
+		assertFalse( it.hasNext() );
 
-        query = parser.parse( "summary:Festina" );
+		query = parser.parse( "summary:Festina" );
+		hibQuery = s.createLuceneQuery( query, Clock.class, Book.class );
+		ScrollableResults sr = hibQuery.scroll();
+		assertTrue( sr.first() );
+		assertNotNull( sr.get() );
+		assertFalse( sr.next() );
+		sr.close();
+
+		query = parser.parse( "summary:Festina OR brand:seiko" );
+		hibQuery = s.createLuceneQuery( query, Clock.class, Book.class );
+		hibQuery.setMaxResults( 2 );
+		result = hibQuery.list();
+		assertNotNull( result );
+		assertEquals( "Query with explicit class filter and limit", 2, result.size() );
+
+		query = parser.parse( "summary:Festina" );
 		hibQuery = s.createLuceneQuery( query );
 		result = hibQuery.list();
 		assertNotNull( result );




More information about the hibernate-commits mailing list