[hibernate-commits] Hibernate SVN: r19270 - in search/trunk/hibernate-search/src: main/java/org/hibernate/search/query/dsl/v2/impl and 1 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Apr 21 13:17:22 EDT 2010


Author: epbernard
Date: 2010-04-21 13:17:21 -0400 (Wed, 21 Apr 2010)
New Revision: 19270

Added:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsTermQueryBuilder.java
Removed:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedSingleTermQueryBuilder.java
Modified:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermMatchingContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermMatchingContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/FieldContext.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/DSLTest.java
Log:
HSEARCH-508 Support query on multiple fields

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermContext.java	2010-04-21 15:42:33 UTC (rev 19269)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermContext.java	2010-04-21 17:17:21 UTC (rev 19270)
@@ -11,4 +11,6 @@
 	 */
 	TermMatchingContext onField(String field);
 
+	TermMatchingContext onFields(String... field);
+
 }

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermMatchingContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermMatchingContext.java	2010-04-21 15:42:33 UTC (rev 19269)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermMatchingContext.java	2010-04-21 17:17:21 UTC (rev 19270)
@@ -9,8 +9,8 @@
 	 */
 	TermTermination matches(String text);
 
-//	/**
-//	 * field / property the term query is executed on
-//	 */
-//	TermMatchingContext onField(String field);
+	/**
+	 * field / property the term query is executed on
+	 */
+	TermMatchingContext andField(String field);
 }

Copied: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsTermQueryBuilder.java (from rev 19264, search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedSingleTermQueryBuilder.java)
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsTermQueryBuilder.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsTermQueryBuilder.java	2010-04-21 17:17:21 UTC (rev 19270)
@@ -0,0 +1,138 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.tokenattributes.TermAttribute;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.FuzzyQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.WildcardQuery;
+
+import org.hibernate.annotations.common.AssertionFailure;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.query.dsl.v2.TermTermination;
+
+/**
+* @author Emmanuel Bernard
+*/
+public class ConnectedMultiFieldsTermQueryBuilder implements TermTermination {
+	private final String text;
+	private final Analyzer queryAnalyzer;
+	private final QueryCustomizer queryCustomizer;
+	private final QueryContext queryContext;
+	private final List<FieldContext> fieldContexts;
+
+	public ConnectedMultiFieldsTermQueryBuilder(QueryContext queryContext,
+												String text,
+												List<FieldContext> fieldContexts,
+												QueryCustomizer queryCustomizer,
+												Analyzer queryAnalyzer,
+												SearchFactory factory) {
+		this.queryContext = queryContext;
+		this.text = text;
+		this.queryAnalyzer = queryAnalyzer;
+		this.queryCustomizer = queryCustomizer;
+		this.fieldContexts = fieldContexts;
+	}
+
+	public Query createQuery() {
+		final int size = fieldContexts.size();
+		if ( size == 1 ) {
+			return queryCustomizer.setWrappedQuery( createQuery( fieldContexts.get( 0 ) ) ).createQuery();
+		}
+		else {
+			BooleanQuery aggregatedFieldsQuery = new BooleanQuery( );
+			for ( FieldContext fieldContext : fieldContexts ) {
+				aggregatedFieldsQuery.add( createQuery( fieldContext ), BooleanClause.Occur.SHOULD );
+			}
+			return  queryCustomizer.setWrappedQuery( aggregatedFieldsQuery ).createQuery();
+		}
+	}
+
+	public Query createQuery(FieldContext fieldContext) {
+		final Query perFieldQuery;
+		if ( fieldContext.isIgnoreAnalyzer() ) {
+			perFieldQuery = createTermQuery( fieldContext, text );
+		}
+		else {
+			List<String> terms;
+			try {
+				terms = getAllTermsFromText( fieldContext.getField(), text, queryAnalyzer );
+			}
+			catch ( IOException e ) {
+				throw new AssertionFailure("IO exception while reading String stream??", e);
+			}
+			if ( terms.size() == 0 ) {
+				throw new SearchException( "try to search with an empty string: " + fieldContext.getField() );
+			}
+			else if (terms.size() == 1 ) {
+				perFieldQuery = createTermQuery( fieldContext, terms.get( 0 ) );
+			}
+			else {
+				BooleanQuery booleanQuery = new BooleanQuery();
+				for (String term : terms) {
+					Query termQuery = createTermQuery(fieldContext, term);
+					booleanQuery.add( termQuery, BooleanClause.Occur.SHOULD );
+				}
+				perFieldQuery = booleanQuery;
+			}
+		}
+		return fieldContext.getFieldCustomizer().setWrappedQuery( perFieldQuery ).createQuery();
+	}
+
+	private Query createTermQuery(FieldContext fieldContext, String term) {
+		Query query;
+		switch ( queryContext.getApproximation() ) {
+			case EXACT:
+				query = new TermQuery( new Term( fieldContext.getField(), term ) );
+				break;
+			case WILDCARD:
+				query = new WildcardQuery( new Term( fieldContext.getField(), term ) );
+				break;
+			case FUZZY:
+				query = new FuzzyQuery(
+						new Term( fieldContext.getField(), term ),
+						queryContext.getThreshold(),
+						queryContext.getPrefixLength() );
+				break;
+			default:
+				throw new AssertionFailure( "Unknown approximation: " + queryContext.getApproximation());
+		}
+		return query;
+	}
+
+	private List<String> getAllTermsFromText(String fieldName, String text, Analyzer analyzer) throws IOException {
+		//it's better not to apply the analyzer with windcards as * and ? can be mistakenly removed
+		List<String> terms = new ArrayList<String>();
+		if ( queryContext.getApproximation() == QueryContext.Approximation.WILDCARD ) {
+			terms.add( text );
+		}
+		else {
+			Reader reader = new StringReader(text);
+			TokenStream stream = analyzer.reusableTokenStream( fieldName, reader);
+			TermAttribute attribute = (TermAttribute) stream.addAttribute( TermAttribute.class );
+			stream.reset();
+
+			while ( stream.incrementToken() ) {
+				if ( attribute.termLength() > 0 ) {
+					String term = attribute.term();
+					terms.add( term );
+				}
+			}
+			stream.end();
+			stream.close();
+		}
+		return terms;
+	}
+
+}

Deleted: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedSingleTermQueryBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedSingleTermQueryBuilder.java	2010-04-21 15:42:33 UTC (rev 19269)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedSingleTermQueryBuilder.java	2010-04-21 17:17:21 UTC (rev 19270)
@@ -1,127 +0,0 @@
-package org.hibernate.search.query.dsl.v2.impl;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.TokenStream;
-import org.apache.lucene.analysis.tokenattributes.TermAttribute;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.FuzzyQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.WildcardQuery;
-
-import org.hibernate.annotations.common.AssertionFailure;
-import org.hibernate.search.SearchException;
-import org.hibernate.search.SearchFactory;
-import org.hibernate.search.query.dsl.v2.TermTermination;
-
-/**
-* @author Emmanuel Bernard
-*/
-public class ConnectedSingleTermQueryBuilder implements TermTermination {
-	private final SearchFactory factory;
-	private final String field;
-	private final String text;
-	private final Analyzer queryAnalyzer;
-	private final QueryCustomizer queryCustomizer;
-	private boolean ignoreAnalyzer;
-	private final QueryContext context;
-
-	public ConnectedSingleTermQueryBuilder(
-			QueryContext context,
-			boolean ignoreAnalyzer,
-			String text,
-			String field,
-			QueryCustomizer queryCustomizer,
-			Analyzer queryAnalyzer,
-			SearchFactory factory) {
-		this.context = context;
-		this.factory = factory;
-		this.field = field;
-		this.text = text;
-		this.queryAnalyzer = queryAnalyzer;
-		this.queryCustomizer = queryCustomizer;
-		this.ignoreAnalyzer = ignoreAnalyzer;
-	}
-
-	public Query createQuery() {
-		final Query result;
-		if ( ignoreAnalyzer ) {
-			result = createTermQuery( text );
-		}
-		else {
-			List<String> terms;
-			try {
-				terms = getAllTermsFromText( field, text, queryAnalyzer );
-			}
-			catch ( IOException e ) {
-				throw new AssertionFailure("IO exception while reading String stream??", e);
-			}
-			if ( terms.size() == 0 ) {
-				throw new SearchException("try to search with an empty string: " + field);
-			}
-			else if (terms.size() == 1 ) {
-				result = createTermQuery( terms.get( 0 ) );
-			}
-			else {
-				BooleanQuery booleanQuery = new BooleanQuery();
-				for (String term : terms) {
-					Query termQuery = createTermQuery(term);
-					booleanQuery.add( termQuery, BooleanClause.Occur.SHOULD );
-				}
-				result = booleanQuery;
-			}
-		}
-		return queryCustomizer.setWrappedQuery( result ).createQuery();
-	}
-
-	private Query createTermQuery(String term) {
-		Query query;
-		switch ( context.getApproximation() ) {
-			case EXACT:
-				query = new TermQuery( new Term(field, term) );
-				break;
-			case WILDCARD:
-				query = new WildcardQuery( new Term(field, term) );
-				break;
-			case FUZZY:
-				query = new FuzzyQuery( new Term(field, term), context.getThreshold(), context.getPrefixLength() );
-				break;
-			default:
-				throw new AssertionFailure( "Unknown approximation: " + context.getApproximation());
-		}
-		return query;
-	}
-
-	private List<String> getAllTermsFromText(String fieldName, String text, Analyzer analyzer) throws IOException {
-		//it's better not to apply the analyzer with windcards as * and ? can be mistakenly removed
-		List<String> terms = new ArrayList<String>();
-		if ( context.getApproximation() == QueryContext.Approximation.WILDCARD ) {
-			terms.add( text );
-		}
-		else {
-			Reader reader = new StringReader(text);
-			TokenStream stream = analyzer.reusableTokenStream( fieldName, reader);
-			TermAttribute attribute = (TermAttribute) stream.addAttribute( TermAttribute.class );
-			stream.reset();
-
-			while ( stream.incrementToken() ) {
-				if ( attribute.termLength() > 0 ) {
-					String term = attribute.term();
-					terms.add( term );
-				}
-			}
-			stream.end();
-			stream.close();
-		}
-		return terms;
-	}
-
-}

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermContext.java	2010-04-21 15:42:33 UTC (rev 19269)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermContext.java	2010-04-21 17:17:21 UTC (rev 19270)
@@ -27,6 +27,10 @@
 		return new ConnectedTermMatchingContext(context, field, queryCustomizer, queryAnalyzer, factory);
 	}
 
+	public TermMatchingContext onFields(String... fields) {
+		return new ConnectedTermMatchingContext(context, fields, queryCustomizer, queryAnalyzer, factory);
+	}
+
 	public TermContext boostedTo(float boost) {
 		queryCustomizer.boostedTo( boost );
 		return this;

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermMatchingContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermMatchingContext.java	2010-04-21 15:42:33 UTC (rev 19269)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermMatchingContext.java	2010-04-21 17:17:21 UTC (rev 19270)
@@ -14,36 +14,61 @@
 */
 public class ConnectedTermMatchingContext implements TermMatchingContext {
 	private final SearchFactory factory;
-	private final String field;
 	private final Analyzer queryAnalyzer;
 	private final QueryCustomizer queryCustomizer;
-	private boolean ignoreAnalyzer;
 	private final QueryContext queryContext;
-	private final FieldContext fieldContext;
-	private final List<ConnectedTermMatchingContext> otherFields;
+	private final List<FieldContext> fieldContexts;
+	//when a varargs of fields are passed, apply the same customization for all.
+	//keep the index of the first context in this queue
+	private int firstOfContext = 0;
 
 	public ConnectedTermMatchingContext(QueryContext queryContext,
 			String field, QueryCustomizer queryCustomizer, Analyzer queryAnalyzer, SearchFactory factory) {
 		this.factory = factory;
-		this.field = field;
 		this.queryAnalyzer = queryAnalyzer;
 		this.queryCustomizer = queryCustomizer;
 		this.queryContext = queryContext;
-		this.otherFields = new ArrayList<ConnectedTermMatchingContext>();
-		this.fieldContext = null;
+		this.fieldContexts = new ArrayList<FieldContext>(4);
+		this.fieldContexts.add( new FieldContext( field ) );
 	}
 
+	public ConnectedTermMatchingContext(QueryContext queryContext,
+			String[] fields, QueryCustomizer queryCustomizer, Analyzer queryAnalyzer, SearchFactory factory) {
+		this.factory = factory;
+		this.queryAnalyzer = queryAnalyzer;
+		this.queryCustomizer = queryCustomizer;
+		this.queryContext = queryContext;
+		this.fieldContexts = new ArrayList<FieldContext>(fields.length);
+		for (String field : fields) {
+			this.fieldContexts.add( new FieldContext( field ) );
+		}
+	}
+
 	public TermTermination matches(String text) {
-		return new ConnectedSingleTermQueryBuilder( queryContext, ignoreAnalyzer, text, field, queryCustomizer, queryAnalyzer, factory);
+		return new ConnectedMultiFieldsTermQueryBuilder( queryContext, text, fieldContexts, queryCustomizer, queryAnalyzer, factory);
 	}
 
+	public TermMatchingContext andField(String field) {
+		this.fieldContexts.add( new FieldContext( field ) );
+		this.firstOfContext = fieldContexts.size() - 1;
+		return this;
+	}
+
 	public TermMatchingContext boostedTo(float boost) {
-		queryCustomizer.boostedTo( boost );
+		for ( FieldContext fieldContext : getCurrentFieldContexts() ) {
+			fieldContext.getFieldCustomizer().boostedTo( boost );
+		}
 		return this;
 	}
 
+	private List<FieldContext> getCurrentFieldContexts() {
+		return fieldContexts.subList( firstOfContext, fieldContexts.size() );
+	}
+
 	public TermMatchingContext ignoreAnalyzer() {
-		this.ignoreAnalyzer = true;
+		for ( FieldContext fieldContext : getCurrentFieldContexts() ) {
+			fieldContext.setIgnoreAnalyzer( true );
+		}
 		return this;
 	}
 }

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/FieldContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/FieldContext.java	2010-04-21 15:42:33 UTC (rev 19269)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/FieldContext.java	2010-04-21 17:17:21 UTC (rev 19270)
@@ -7,28 +7,18 @@
  */
 public class FieldContext {
 	private final String field;
-	private final Analyzer queryAnalyzer;
-	private final QueryCustomizer queryCustomizer;
 	private boolean ignoreAnalyzer;
+	private final QueryCustomizer fieldCustomizer;
 
-	public FieldContext(String field, Analyzer queryAnalyzer, QueryCustomizer queryCustomizer) {
+	public FieldContext(String field) {
 		this.field = field;
-		this.queryAnalyzer = queryAnalyzer;
-		this.queryCustomizer = queryCustomizer;
+		this.fieldCustomizer = new QueryCustomizer();
 	}
 
 	public String getField() {
 		return field;
 	}
 
-	public Analyzer getQueryAnalyzer() {
-		return queryAnalyzer;
-	}
-
-	public QueryCustomizer getQueryCustomizer() {
-		return queryCustomizer;
-	}
-
 	public boolean isIgnoreAnalyzer() {
 		return ignoreAnalyzer;
 	}
@@ -36,4 +26,8 @@
 	public void setIgnoreAnalyzer(boolean ignoreAnalyzer) {
 		this.ignoreAnalyzer = ignoreAnalyzer;
 	}
+
+	public QueryCustomizer getFieldCustomizer() {
+		return fieldCustomizer;
+	}
 }

Modified: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/DSLTest.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/DSLTest.java	2010-04-21 15:42:33 UTC (rev 19269)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/DSLTest.java	2010-04-21 17:17:21 UTC (rev 19270)
@@ -14,7 +14,6 @@
 import org.hibernate.Transaction;
 import org.hibernate.cfg.Configuration;
 import org.hibernate.search.Environment;
-import org.hibernate.search.FullTextQuery;
 import org.hibernate.search.FullTextSession;
 import org.hibernate.search.Search;
 import org.hibernate.search.annotations.Factory;
@@ -137,6 +136,54 @@
 		cleanData( fts );
 	}
 
+	public void testMultipleFields() throws Exception {
+		FullTextSession fts = initData();
+
+		Transaction transaction = fts.beginTransaction();
+		final QueryBuilder monthQb = fts.getSearchFactory()
+				.buildQueryBuilder().forEntity( Month.class ).get();
+		Query
+
+		//combined query, January and february both contain whitening but February in a longer text
+		query = monthQb.exact()
+						.onField( "mythology" )
+						.andField( "history" )
+						.matches( "whitening" ).createQuery();
+
+		List<Month> results = fts.createFullTextQuery( query, Month.class ).list();
+		assertEquals( 2, results.size() );
+		assertEquals( "January", results.get( 0 ).getName() );
+
+		//combined query, January and february both contain whitening but February in a longer text
+		query = monthQb.exact()
+						.onFields( "mythology", "history" )
+							.boostedTo( 30 )
+						.matches( "whitening" ).createQuery();
+
+		results = fts.createFullTextQuery( query, Month.class ).list();
+		assertEquals( 2, results.size() );
+		assertEquals( "January", results.get( 0 ).getName() );
+
+		//boosted query, January and february both contain whitening but February in a longer text
+		//since history is boosted, February should come first though
+		query = monthQb.exact()
+							.onField( "mythology" )
+							.andField( "history" )
+								.boostedTo( 30 )
+							.matches( "whitening" )
+							.createQuery();
+
+		results = fts.createFullTextQuery( query, Month.class ).list();
+		assertEquals( 2, results.size() );
+		assertEquals( "February", results.get( 0 ).getName() );
+
+		//FIXME add other method tests besides boostedTo
+
+		transaction.commit();
+
+		cleanData( fts );
+	}
+
 	public void testBoolean() throws Exception {
 		FullTextSession fts = initData();
 



More information about the hibernate-commits mailing list