[hibernate-commits] Hibernate SVN: r19242 - 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
Fri Apr 16 12:06:42 EDT 2010


Author: epbernard
Date: 2010-04-16 12:06:40 -0400 (Fri, 16 Apr 2010)
New Revision: 19242

Added:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/FieldCustomization.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/FuzzyContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermTermination.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/Termination.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/WildcardContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedFuzzyContext.java
   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/ConnectedTermMatchingContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedWildcardContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryContext.java
Removed:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermCustomization.java
Modified:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/BooleanJunction.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryBuilder.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryCustomization.java
   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/TermFuzzy.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/ConnectedQueryBuilder.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/QueryCustomizer.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/DSLTest.java
Log:
HSEARCH-414 use new approach asking for the type of query first

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/BooleanJunction.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/BooleanJunction.java	2010-04-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/BooleanJunction.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -7,7 +7,7 @@
  *
  * @author Emmanuel Bernard
  */
-public interface BooleanJunction<T extends BooleanJunction> extends QueryCustomization<T> {
+public interface BooleanJunction<T extends BooleanJunction> extends QueryCustomization<T>, Termination {
 	/**
 	 * The boolean query results should match the subquery
 	 */
@@ -18,9 +18,4 @@
 	 * Call the .not() method to ensure results of the boolean query do NOT match the subquery.
 	 */
 	MustJunction must(Query query);
-
-	/**
-	 * Return the lucene query representing the boolean operation
-	 */
-	Query createQuery();
 }

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/FieldCustomization.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/FieldCustomization.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/FieldCustomization.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,23 @@
+package org.hibernate.search.query.dsl.v2;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface FieldCustomization<T> {
+	/**
+	 * Boost the field to a given value
+	 * Most of the time positive float:
+	 *  - lower than 1 to diminish the weight
+	 *  - higher than 1 to increase the weight
+	 *
+	 * Could be negative but not unless you understand what is going on (advanced)
+	 */
+	T boostedTo(float boost);
+
+	/**
+	 * Advanced
+	 * Do not execute the analyzer on the text.
+	 * (It is usually a good idea to apply the analyzer)
+	 */
+	T ignoreAnalyzer();
+}

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/FuzzyContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/FuzzyContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/FuzzyContext.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,26 @@
+package org.hibernate.search.query.dsl.v2;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface FuzzyContext extends QueryCustomization<FuzzyContext> {
+	/**
+	 * field / property the term query is executed on
+	 */
+	TermMatchingContext onField(String field);
+
+	/**
+	 * Threshold above which two terms are considered similar enough.
+	 * Value between 0 and 1 (1 excluded)
+	 * Defaults to .5
+	 */
+	FuzzyContext threshold(float threshold);
+
+	/**
+	 * Size of the prefix ignored by the fuzzyness.
+	 * A non zero value is recommended if the index contains a huge amount of distinct terms
+	 *
+	 * Defaults to 0
+	 */
+	FuzzyContext prefixLength(int prefixLength);
+}

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryBuilder.java	2010-04-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryBuilder.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -7,10 +7,23 @@
 	/**
 	 * build a term query
 	 */
-	TermContext term();
+	TermContext exact();
 
+	/**
+	 * Use a fuzzy search approximation (aka edit distance)
+	 */
+	FuzzyContext fuzzy();
 
 	/**
+	 * Treat the query as a wildcard:
+	 *  - ? represents any single character
+	 *  - * represents any character sequence
+	 * For faster results, it is recommended that the query text does not
+	 * start with ? or *
+	 */
+	WildcardContext wildcard();
+
+	/**
 	 * Boolean query
 	 */
 	BooleanJunction<BooleanJunction> bool();

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryCustomization.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryCustomization.java	2010-04-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryCustomization.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -32,9 +32,4 @@
 	T filter(Filter filter);
 
 	//TODO filter(String) + parameters
-
-	/**
-	 * Create a Lucene query
-	 */
-	Query createQuery();
 }

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-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermContext.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -5,10 +5,10 @@
 /**
  * @author Emmanuel Bernard
  */
-public interface TermContext {
+public interface TermContext extends QueryCustomization<TermContext> {
 	/**
 	 * field / property the term query is executed on
 	 */
-	TermMatchingContext on(String field);
+	TermMatchingContext onField(String field);
 
 }

Deleted: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermCustomization.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermCustomization.java	2010-04-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermCustomization.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -1,31 +0,0 @@
-package org.hibernate.search.query.dsl.v2;
-
-import org.apache.lucene.search.Query;
-
-/**
-* @author Emmanuel Bernard
-*/
-public interface TermCustomization extends QueryCustomization<TermCustomization> {
-	/**
-	 * Advanced
-	 * Do not execute the analyzer on the text.
-	 * (It is usually a good idea to apply the analyzer)
-	 */
-	TermCustomization ignoreAnalyzer();
-
-	/**
-	 * Use a fuzzy search approximation (aka edit distance)
-	 */
-	TermFuzzy fuzzy();
-
-	/**
-	 * Treat the query as a wildcard:
-	 *  - ? represents any single character
-	 *  - * represents any character sequence
-	 * For faster results, it is recommended that the query text does not
-	 * start with ? or *
-	 */
-	//TODO make it mutually exclusive with fuzzy use (but that's much more complex)
-	TermCustomization wildcard();
-
-}

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermFuzzy.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermFuzzy.java	2010-04-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermFuzzy.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -3,7 +3,7 @@
 /**
 * @author Emmanuel Bernard
 */
-public interface TermFuzzy extends TermCustomization {
+public interface TermFuzzy extends TermTermination {
 	/**
 	 * Threshold above which two terms are considered similar enough.
 	 * Value between 0 and 1 (1 excluded)

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-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermMatchingContext.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -3,9 +3,9 @@
 /**
 * @author Emmanuel Bernard
 */
-public interface TermMatchingContext {
+public interface TermMatchingContext extends FieldCustomization<TermMatchingContext> {
 	/**
 	 * text searched in the term query (the term is pre-analyzer unless ignoreAnalyzer is called)
 	 */
-	TermCustomization matches(String text);
+	TermTermination matches(String text);
 }

Copied: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermTermination.java (from rev 19212, search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermCustomization.java)
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermTermination.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermTermination.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,7 @@
+package org.hibernate.search.query.dsl.v2;
+
+/**
+* @author Emmanuel Bernard
+*/
+public interface TermTermination extends Termination<TermTermination> {
+}

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/Termination.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/Termination.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/Termination.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,13 @@
+package org.hibernate.search.query.dsl.v2;
+
+import org.apache.lucene.search.Query;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface Termination<T> {
+	/**
+	 * Return the lucene query representing the operation
+	 */
+	Query createQuery();
+}

Copied: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/WildcardContext.java (from rev 19212, 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/WildcardContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/WildcardContext.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,12 @@
+package org.hibernate.search.query.dsl.v2;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface WildcardContext extends QueryCustomization<WildcardContext> {
+	/**
+	 * field / property the term query is executed on
+	 */
+	TermMatchingContext onField(String field);
+
+}
\ No newline at end of file

Copied: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedFuzzyContext.java (from rev 19212, 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/ConnectedFuzzyContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedFuzzyContext.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,56 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.search.Filter;
+
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.query.dsl.v2.FuzzyContext;
+import org.hibernate.search.query.dsl.v2.TermContext;
+import org.hibernate.search.query.dsl.v2.TermMatchingContext;
+
+/**
+ * @author Emmanuel Bernard
+ */
+class ConnectedFuzzyContext implements FuzzyContext {
+	private final SearchFactory factory;
+	private final Analyzer queryAnalyzer;
+	private final QueryCustomizer queryCustomizer;
+	private final QueryContext context;
+
+	public ConnectedFuzzyContext(Analyzer queryAnalyzer, SearchFactory factory) {
+		this.factory = factory;
+		this.queryAnalyzer = queryAnalyzer;
+		this.queryCustomizer = new QueryCustomizer();
+		this.context = new QueryContext( QueryContext.Approximation.FUZZY);
+	}
+
+	public TermMatchingContext onField(String field) {
+		return new ConnectedTermMatchingContext(context, field, queryCustomizer, queryAnalyzer, factory);
+	}
+
+	public ConnectedFuzzyContext threshold(float threshold) {
+		context.setThreshold( threshold );
+		return this;
+	}
+
+	public ConnectedFuzzyContext prefixLength(int prefixLength) {
+		context.setPrefixLength( prefixLength );
+		return this;
+	}
+
+	public FuzzyContext boostedTo(float boost) {
+		queryCustomizer.boostedTo( boost );
+		return this;
+	}
+
+	public FuzzyContext constantScore() {
+		queryCustomizer.constantScore();
+		return this;
+	}
+
+	public FuzzyContext filter(Filter filter) {
+		queryCustomizer.filter(filter);
+		return this;
+	}
+
+}
\ No newline at end of file

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedQueryBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedQueryBuilder.java	2010-04-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedQueryBuilder.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -4,8 +4,10 @@
 
 import org.hibernate.search.SearchFactory;
 import org.hibernate.search.query.dsl.v2.BooleanJunction;
+import org.hibernate.search.query.dsl.v2.FuzzyContext;
 import org.hibernate.search.query.dsl.v2.QueryBuilder;
 import org.hibernate.search.query.dsl.v2.TermContext;
+import org.hibernate.search.query.dsl.v2.WildcardContext;
 
 /**
  * Assuming connection with the search factory
@@ -21,10 +23,19 @@
 		this.factory = factory;
 	}
 
-	public TermContext term() {
-		return new ConnectedTermContext( queryAnalyzer, factory);
+	public TermContext exact() {
+		return new ConnectedTermContext(queryAnalyzer, factory);
 	}
 
+	public FuzzyContext fuzzy() {
+		return new ConnectedFuzzyContext(queryAnalyzer, factory);
+	}
+
+	public WildcardContext wildcard() {
+		return new ConnectedWildcardContext(queryAnalyzer, factory);
+	}
+
+	//fixme Have to use raw types but would be nice to not have to
 	public BooleanJunction bool() {
 		return new BooleanQueryBuilder();
 	}

Added: 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	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedSingleTermQueryBuilder.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,127 @@
+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-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermContext.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -1,29 +1,10 @@
 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.Filter;
-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.TermContext;
-import org.hibernate.search.query.dsl.v2.TermCustomization;
-import org.hibernate.search.query.dsl.v2.TermFuzzy;
 import org.hibernate.search.query.dsl.v2.TermMatchingContext;
 
 /**
@@ -32,177 +13,32 @@
 class ConnectedTermContext implements TermContext {
 	private final SearchFactory factory;
 	private final Analyzer queryAnalyzer;
+	private final QueryCustomizer queryCustomizer;
+	private final QueryContext context;
 
 	public ConnectedTermContext(Analyzer queryAnalyzer, SearchFactory factory) {
 		this.factory = factory;
 		this.queryAnalyzer = queryAnalyzer;
+		this.queryCustomizer = new QueryCustomizer();
+		this.context = new QueryContext( QueryContext.Approximation.EXACT);
 	}
 
-	public TermMatchingContext on(String field) {
-		return new ConnectedTermMatchingContext(field, queryAnalyzer, factory);
+	public TermMatchingContext onField(String field) {
+		return new ConnectedTermMatchingContext(context, field, queryCustomizer, queryAnalyzer, factory);
 	}
 
-	public static class ConnectedTermMatchingContext implements TermMatchingContext {
-		private final SearchFactory factory;
-		private final String field;
-		private final Analyzer queryAnalyzer;
+	public TermContext boostedTo(float boost) {
+		queryCustomizer.boostedTo( boost );
+		return this;
+	}
 
-		public ConnectedTermMatchingContext(String field, Analyzer queryAnalyzer, SearchFactory factory) {
-			this.factory = factory;
-			this.field = field;
-			this.queryAnalyzer = queryAnalyzer;
-		}
-
-		public TermCustomization matches(String text) {
-			return new ConnectedTermCustomization(text, field, queryAnalyzer, factory);
-		}
+	public TermContext constantScore() {
+		queryCustomizer.constantScore();
+		return this;
 	}
 
-	public static class ConnectedTermCustomization implements TermCustomization, TermFuzzy {
-		private final SearchFactory factory;
-		private final String field;
-		private final String text;
-		private final Analyzer queryAnalyzer;
-		private final QueryCustomizer queryCustomizer;
-
-		private boolean ignoreAnalyzer;
-		private Approximation approximation = Approximation.EXACT;
-		private float threshold = .5f;
-		private int prefixLength = 0;
-
-		public ConnectedTermCustomization(String text, String field, Analyzer queryAnalyzer, SearchFactory factory) {
-			this.factory = factory;
-			this.field = field;
-			this.text = text;
-			this.queryAnalyzer = queryAnalyzer;
-			this.queryCustomizer = new QueryCustomizer();
-		}
-
-		public TermCustomization ignoreAnalyzer() {
-			ignoreAnalyzer = true;
-			return this;
-		}
-
-		public TermFuzzy fuzzy() {
-			if (approximation != Approximation.EXACT) {
-				throw new IllegalStateException( "Cannot call fuzzy() and wildcard() on the same term query" );
-			}
-			approximation = Approximation.FUZZY;
-			return this;
-		}
-
-		public TermCustomization wildcard() {
-			if (approximation != Approximation.EXACT) {
-				throw new IllegalStateException( "Cannot call fuzzy() and wildcard() on the same term query" );
-			}
-			approximation = Approximation.WILDCARD;
-			return this;
-		}
-
-		public TermCustomization boostedTo(float boost) {
-			queryCustomizer.boostedTo( boost );
-			return this;
-		}
-
-		public TermCustomization constantScore() {
-			queryCustomizer.constantScore();
-			return this;
-		}
-
-		public TermCustomization filter(Filter filter) {
-			queryCustomizer.filter( filter );
-			return this;
-		}
-
-		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);
-						//termQuery.setBoost( boost );
-						booleanQuery.add( termQuery, BooleanClause.Occur.SHOULD );
-					}
-					result = booleanQuery;
-				}
-			}
-			return queryCustomizer.setWrappedQuery( result ).createQuery();
-		}
-
-		private Query createTermQuery(String term) {
-			Query query;
-			switch ( approximation ) {
-				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), threshold, prefixLength );
-					break;
-				default:
-					throw new AssertionFailure( "Unknown approximation: " + approximation);
-
-			}
-			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 ( approximation == 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;
-		}
-
-		public TermFuzzy threshold(float threshold) {
-			this.threshold = threshold;
-			return this;
-		}
-
-		public TermFuzzy prefixLength(int prefixLength) {
-			this.prefixLength = prefixLength;
-			return this;
-		}
-
-		private static enum Approximation {
-			EXACT,
-			WILDCARD,
-			FUZZY
-		}
+	public TermContext filter(Filter filter) {
+		queryCustomizer.filter(filter);
+		return this;
 	}
 }

Added: 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	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermMatchingContext.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,42 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+import org.apache.lucene.analysis.Analyzer;
+
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.query.dsl.v2.TermMatchingContext;
+import org.hibernate.search.query.dsl.v2.TermTermination;
+
+/**
+* @author Emmanuel Bernard
+*/
+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 context;
+
+	public ConnectedTermMatchingContext(QueryContext context,
+			String field, QueryCustomizer queryCustomizer, Analyzer queryAnalyzer, SearchFactory factory) {
+		this.factory = factory;
+		this.field = field;
+		this.queryAnalyzer = queryAnalyzer;
+		this.queryCustomizer = queryCustomizer;
+		this.context = context;
+	}
+
+	public TermTermination matches(String text) {
+		return new ConnectedSingleTermQueryBuilder(context, ignoreAnalyzer, text, field, queryCustomizer, queryAnalyzer, factory);
+	}
+
+	public TermMatchingContext boostedTo(float boost) {
+		queryCustomizer.boostedTo( boost );
+		return this;
+	}
+
+	public TermMatchingContext ignoreAnalyzer() {
+		this.ignoreAnalyzer = true;
+		return this;
+	}
+}

Copied: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedWildcardContext.java (from rev 19212, 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/ConnectedWildcardContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedWildcardContext.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,45 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.search.Filter;
+
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.query.dsl.v2.TermContext;
+import org.hibernate.search.query.dsl.v2.TermMatchingContext;
+import org.hibernate.search.query.dsl.v2.WildcardContext;
+
+/**
+ * @author Emmanuel Bernard
+ */
+class ConnectedWildcardContext implements WildcardContext {
+	private final SearchFactory factory;
+	private final Analyzer queryAnalyzer;
+	private final QueryCustomizer queryCustomizer;
+	private final QueryContext context;
+
+	public ConnectedWildcardContext(Analyzer queryAnalyzer, SearchFactory factory) {
+		this.factory = factory;
+		this.queryAnalyzer = queryAnalyzer;
+		this.queryCustomizer = new QueryCustomizer();
+		this.context = new QueryContext( QueryContext.Approximation.WILDCARD);
+	}
+
+	public TermMatchingContext onField(String field) {
+		return new ConnectedTermMatchingContext(context, field, queryCustomizer, queryAnalyzer, factory);
+	}
+
+	public WildcardContext boostedTo(float boost) {
+		queryCustomizer.boostedTo( boost );
+		return this;
+	}
+
+	public WildcardContext constantScore() {
+		queryCustomizer.constantScore();
+		return this;
+	}
+
+	public WildcardContext filter(Filter filter) {
+		queryCustomizer.filter(filter);
+		return this;
+	}
+}
\ No newline at end of file

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryContext.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -0,0 +1,40 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+/**
+* @author Emmanuel Bernard
+*/
+public class QueryContext {
+	private final Approximation approximation;
+	private float threshold = .5f;
+	private int prefixLength = 0;
+
+	public QueryContext(Approximation approximation) {
+		this.approximation = approximation;
+	}
+
+	public void setThreshold(float threshold) {
+		this.threshold = threshold;
+	}
+
+	public void setPrefixLength(int prefixLength) {
+		this.prefixLength = prefixLength;
+	}
+
+	public Approximation getApproximation() {
+		return approximation;
+	}
+
+	public float getThreshold() {
+		return threshold;
+	}
+
+	public int getPrefixLength() {
+		return prefixLength;
+	}
+
+	public static enum Approximation {
+		EXACT,
+		WILDCARD,
+		FUZZY
+	}
+}

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryCustomizer.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryCustomizer.java	2010-04-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryCustomizer.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -20,7 +20,7 @@
 	private Filter filter;
 
 	public QueryCustomizer boostedTo(float boost) {
-		this.boost = boost;
+		this.boost = boost * this.boost;
 		return this;
 	}
 
@@ -44,7 +44,7 @@
 		if (wrappedQuery == null) {
 			throw new AssertionFailure( "wrapped query not set" );
 		}
-		finalQuery.setBoost( boost );
+		finalQuery.setBoost( boost * finalQuery.getBoost() );
 		if (filter != null) {
 			finalQuery = new FilteredQuery(finalQuery, filter);
 		}

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-16 10:11:32 UTC (rev 19241)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/DSLTest.java	2010-04-16 16:06:40 UTC (rev 19242)
@@ -35,31 +35,31 @@
 				.buildQueryBuilder().forEntity( Month.class ).get();
 		Query 
 		//regular term query
-		query = monthQb.term().on( "mythology" ).matches( "cold" ).createQuery();
+		query = monthQb.exact().onField( "mythology" ).matches( "cold" ).createQuery();
 
 		assertEquals( 0, fts.createFullTextQuery( query, Month.class ).getResultSize() );
 
 		//term query based on several words
-		query = monthQb.term().on( "mythology" ).matches( "colder darker" ).createQuery();
+		query = monthQb.exact().onField( "mythology" ).matches( "colder darker" ).createQuery();
 
 		assertEquals( 1, fts.createFullTextQuery( query, Month.class ).getResultSize() );
 
 		//term query applying the analyzer and generating one term per word
-		query = monthQb.term().on( "mythology_stem" ).matches( "snowboard" ).createQuery();
+		query = monthQb.exact().onField( "mythology_stem" ).matches( "snowboard" ).createQuery();
 
 		assertEquals( 1, fts.createFullTextQuery( query, Month.class ).getResultSize() );
 
 		//term query applying the analyzer and generating several terms per word
-		query = monthQb.term().on( "mythology_ngram" ).matches( "snobored" ).createQuery();
+		query = monthQb.exact().onField( "mythology_ngram" ).matches( "snobored" ).createQuery();
 
 		assertEquals( 1, fts.createFullTextQuery( query, Month.class ).getResultSize() );
 
 		//term query not using analyzers
-		query = monthQb.term().on( "mythology" ).matches( "Month" ).ignoreAnalyzer().createQuery();
+		query = monthQb.exact().onField( "mythology" ).ignoreAnalyzer().matches( "Month" ).createQuery();
 
 		assertEquals( 0, fts.createFullTextQuery( query, Month.class ).getResultSize() );
 
-		query = monthQb.term().on( "mythology" ).matches( "Month" ).createQuery();
+		query = monthQb.exact().onField( "mythology" ).matches( "Month" ).createQuery();
 
 		transaction.commit();
 
@@ -73,22 +73,25 @@
 		final QueryBuilder monthQb = fts.getSearchFactory()
 				.buildQueryBuilder().forEntity( Month.class ).get();
 		Query
+
 		//fuzzy search with custom threshold and prefix
 		query = monthQb
-				.term().on( "mythology" ).matches( "calder" )
-					.fuzzy()
-						.threshold( .8f )
-						.prefixLength( 1 )
-				.createQuery();
+				.fuzzy()
+					.threshold( .8f )
+					.prefixLength( 1 )
+					.onField( "mythology" )
+						.matches( "calder" )
+						.createQuery();
 
 		assertEquals( 1, fts.createFullTextQuery( query, Month.class ).getResultSize() );
 
 		//wildcard query
 		query = monthQb
-				.term().on( "mythology" ).matches( "mon*" )
-					.wildcard()
-				.createQuery();
-		System.out.println(query.toString(  ));
+				.wildcard()
+					.onField( "mythology" )
+						.matches( "mon*" )
+						.createQuery();
+
 		assertEquals( 2, fts.createFullTextQuery( query, Month.class ).getResultSize() );
 
 		transaction.commit();
@@ -107,8 +110,8 @@
 		//combined query, January and february both contain whitening but February in a longer text
 		query = monthQb
 				.bool()
-					.should( monthQb.term().on( "mythology" ).matches( "whitening" ).createQuery() )
-					.should( monthQb.term().on( "history" ).matches( "whitening" ).createQuery() )
+					.should( monthQb.exact().onField( "mythology" ).matches( "whitening" ).createQuery() )
+					.should( monthQb.exact().onField( "history" ).matches( "whitening" ).createQuery() )
 				.createQuery();
 
 		List<Month> results = fts.createFullTextQuery( query, Month.class ).list();
@@ -119,8 +122,8 @@
 		//since history is boosted, February should come first though
 		query = monthQb
 				.bool()
-					.should( monthQb.term().on( "mythology" ).matches( "whitening" ).createQuery() )
-					.should( monthQb.term().on( "history" ).matches( "whitening" ).boostedTo( 30 ).createQuery() )
+					.should( monthQb.exact().onField( "mythology" ).matches( "whitening" ).createQuery() )
+					.should( monthQb.exact().onField( "history" ).boostedTo( 30 ).matches( "whitening" ).createQuery() )
 				.createQuery();
 
 		results = fts.createFullTextQuery( query, Month.class ).list();
@@ -134,6 +137,114 @@
 		cleanData( fts );
 	}
 
+
+//	public void testTermQueryOnAnalyzer() throws Exception {
+//		FullTextSession fts = initData();
+//
+//		Transaction transaction = fts.beginTransaction();
+//		final QueryBuilder monthQb = fts.getSearchFactory()
+//				.buildQueryBuilder().forEntity( Month.class ).get();
+//		Query
+//		//regular term query
+//		query = monthQb.term().on( "mythology" ).matches( "cold" ).createQuery();
+//
+//		assertEquals( 0, fts.createFullTextQuery( query, Month.class ).getResultSize() );
+//
+//		//term query based on several words
+//		query = monthQb.term().on( "mythology" ).matches( "colder darker" ).createQuery();
+//
+//		assertEquals( 1, fts.createFullTextQuery( query, Month.class ).getResultSize() );
+//
+//		//term query applying the analyzer and generating one term per word
+//		query = monthQb.term().on( "mythology_stem" ).matches( "snowboard" ).createQuery();
+//
+//		assertEquals( 1, fts.createFullTextQuery( query, Month.class ).getResultSize() );
+//
+//		//term query applying the analyzer and generating several terms per word
+//		query = monthQb.term().on( "mythology_ngram" ).matches( "snobored" ).createQuery();
+//
+//		assertEquals( 1, fts.createFullTextQuery( query, Month.class ).getResultSize() );
+//
+//		//term query not using analyzers
+//		query = monthQb.term().on( "mythology" ).matches( "Month" ).ignoreAnalyzer().createQuery();
+//
+//		assertEquals( 0, fts.createFullTextQuery( query, Month.class ).getResultSize() );
+//
+//		query = monthQb.term().on( "mythology" ).matches( "Month" ).createQuery();
+//
+//		transaction.commit();
+//
+//		cleanData( fts );
+//	}
+//
+//	public void testFuzzyAndWildcardQuery() throws Exception {
+//		FullTextSession fts = initData();
+//
+//		Transaction transaction = fts.beginTransaction();
+//		final QueryBuilder monthQb = fts.getSearchFactory()
+//				.buildQueryBuilder().forEntity( Month.class ).get();
+//		Query
+//		//fuzzy search with custom threshold and prefix
+//		query = monthQb
+//				.term().on( "mythology" ).matches( "calder" )
+//					.fuzzy()
+//						.threshold( .8f )
+//						.prefixLength( 1 )
+//				.createQuery();
+//
+//		assertEquals( 1, fts.createFullTextQuery( query, Month.class ).getResultSize() );
+//
+//		//wildcard query
+//		query = monthQb
+//				.term().on( "mythology" ).matches( "mon*" )
+//					.wildcard()
+//				.createQuery();
+//		System.out.println(query.toString(  ));
+//		assertEquals( 2, fts.createFullTextQuery( query, Month.class ).getResultSize() );
+//
+//		transaction.commit();
+//
+//		cleanData( fts );
+//	}
+//
+//	public void testQueryCustomization() 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
+//				.bool()
+//					.should( monthQb.term().on( "mythology" ).matches( "whitening" ).createQuery() )
+//					.should( monthQb.term().on( "history" ).matches( "whitening" ).createQuery() )
+//				.createQuery();
+//
+//		List<Month> 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
+//				.bool()
+//					.should( monthQb.term().on( "mythology" ).matches( "whitening" ).createQuery() )
+//					.should( monthQb.term().on( "history" ).matches( "whitening" ).boostedTo( 30 ).createQuery() )
+//				.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 );
+//	}
+
 	//FIXME add boolean tests
 
 	private void cleanData(FullTextSession fts) {



More information about the hibernate-commits mailing list