[hibernate-commits] Hibernate SVN: r19589 - 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
Sun May 23 05:11:50 EDT 2010


Author: epbernard
Date: 2010-05-23 05:11:49 -0400 (Sun, 23 May 2010)
New Revision: 19589

Added:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeMatchingContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeTerminationExcludable.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsRangeQueryBuilder.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedRangeContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedRangeMatchingContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/Helper.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/RangeQueryContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/TermQueryContext.java
Removed:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryContext.java
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/impl/ConnectedFuzzyContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsTermQueryBuilder.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/ConnectedTermMatchingContext.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedWildcardContext.java
   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/Month.java
Log:
HSEARCH-414 Add support for range query

Add support for range queries including the special case of below and above
range().onField("property").from(a).exclude().to(b).createQuery()
range().onField("property").above(a).exclude().createQuery()

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-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryBuilder.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -31,6 +31,11 @@
 	BooleanJunction<BooleanJunction> bool();
 
 	/**
+	 * find matching elements within a range
+	 */
+	RangeContext range();
+
+	/**
 	 * Query matching all documents
 	 * Typically mixed with a boolean query.
 	 */

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -0,0 +1,11 @@
+package org.hibernate.search.query.dsl.v2;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface RangeContext extends QueryCustomization<RangeContext> {
+	/**
+	 * field / property the term query is executed on
+	 */
+	RangeMatchingContext onField(String fieldName);
+}

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeMatchingContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeMatchingContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeMatchingContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -0,0 +1,34 @@
+package org.hibernate.search.query.dsl.v2;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface RangeMatchingContext extends FieldCustomization<RangeMatchingContext> {
+	/**
+	 * field / property the term query is executed on
+	 */
+	RangeMatchingContext andField(String field);
+
+	//TODO what about numeric range query, I guess we can detect it automatically based on the field bridge
+	//TODO get info on precisionStepDesc (index time info)
+	//FIXME: Is <T> correct or should we specialize to String and Numeric (or all the numeric types?
+	<T> FromRangeContext<T> from(T from);
+
+	public interface FromRangeContext<T> {
+		RangeTerminationExcludable to(T to);
+		FromRangeContext<T> exclude();
+	}
+
+	/**
+	 * The field value must be below <code>below</code>
+	 * You can exclude the value <code>below</code> by calling <code>.exclude()</code>
+	 */
+	RangeTerminationExcludable below(Object below);
+
+	/**
+	 * The field value must be above <code>above</code>
+	 * You can exclude the value <code>above</code> by calling <code>.exclude()</code>
+	 */
+	RangeTerminationExcludable above(Object above);
+
+}

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeTerminationExcludable.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeTerminationExcludable.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/RangeTerminationExcludable.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -0,0 +1,8 @@
+package org.hibernate.search.query.dsl.v2;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface RangeTerminationExcludable extends Termination<RangeTerminationExcludable> {
+	RangeTerminationExcludable exclude();
+}

Modified: 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/ConnectedFuzzyContext.java	2010-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedFuzzyContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -5,7 +5,6 @@
 
 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;
 
 /**
@@ -15,13 +14,13 @@
 	private final SearchFactory factory;
 	private final Analyzer queryAnalyzer;
 	private final QueryCustomizer queryCustomizer;
-	private final QueryContext context;
+	private final TermQueryContext context;
 
 	public ConnectedFuzzyContext(Analyzer queryAnalyzer, SearchFactory factory) {
 		this.factory = factory;
 		this.queryAnalyzer = queryAnalyzer;
 		this.queryCustomizer = new QueryCustomizer();
-		this.context = new QueryContext( QueryContext.Approximation.FUZZY);
+		this.context = new TermQueryContext( TermQueryContext.Approximation.FUZZY);
 	}
 
 	public TermMatchingContext onField(String field) {

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsRangeQueryBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsRangeQueryBuilder.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsRangeQueryBuilder.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -0,0 +1,82 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.lucene.analysis.Analyzer;
+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.TermRangeQuery;
+import org.apache.lucene.search.WildcardQuery;
+
+import org.hibernate.annotations.common.AssertionFailure;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.query.dsl.v2.RangeTerminationExcludable;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ConnectedMultiFieldsRangeQueryBuilder implements RangeTerminationExcludable {
+	private final RangeQueryContext queryContext;
+	private final Analyzer queryAnalyzer;
+	private final QueryCustomizer queryCustomizer;
+	private final List<FieldContext> fieldContexts;
+
+	public ConnectedMultiFieldsRangeQueryBuilder(RangeQueryContext queryContext, Analyzer queryAnalyzer, QueryCustomizer queryCustomizer, List<FieldContext> fieldContexts) {
+		this.queryContext = queryContext;
+		this.queryAnalyzer = queryAnalyzer;
+		this.queryCustomizer = queryCustomizer;
+		this.fieldContexts = fieldContexts;
+	}
+
+	public RangeTerminationExcludable exclude() {
+		if ( queryContext.getFrom() != null && queryContext.getTo() != null ) {
+			queryContext.setExcludeTo( true );
+		}
+		else if ( queryContext.getFrom() != null ) {
+			queryContext.setExcludeTo( true );
+		}
+		else if ( queryContext.getTo() != null ) {
+			queryContext.setExcludeTo( true );
+		}
+		else {
+			throw new AssertionFailure( "Both from and to clause of a range query are null" );
+		}
+		return this;
+	}
+
+	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;
+		final String fieldName = fieldContext.getField();
+		final Object from = queryContext.getFrom();
+		final String lowerTerm = from == null ? null : Helper.getAnalyzedTerm( fieldName, from, "from", queryAnalyzer );
+		final Object to = queryContext.getTo();
+		final String upperTerm = to == null ? null : Helper.getAnalyzedTerm( fieldName, to, "to", queryAnalyzer );
+		perFieldQuery = new TermRangeQuery(
+				fieldName,
+				lowerTerm,
+				upperTerm,
+				queryContext.isExcludeFrom(),
+				queryContext.isExcludeTo()
+		);
+		return fieldContext.getFieldCustomizer().setWrappedQuery( perFieldQuery ).createQuery();
+	}
+}

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsTermQueryBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsTermQueryBuilder.java	2010-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedMultiFieldsTermQueryBuilder.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -1,14 +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;
@@ -29,10 +25,10 @@
 	private final String text;
 	private final Analyzer queryAnalyzer;
 	private final QueryCustomizer queryCustomizer;
-	private final QueryContext queryContext;
+	private final TermQueryContext queryContext;
 	private final List<FieldContext> fieldContexts;
 
-	public ConnectedMultiFieldsTermQueryBuilder(QueryContext queryContext,
+	public ConnectedMultiFieldsTermQueryBuilder(TermQueryContext queryContext,
 												String text,
 												List<FieldContext> fieldContexts,
 												QueryCustomizer queryCustomizer,
@@ -80,8 +76,8 @@
 			}
 			else {
 				BooleanQuery booleanQuery = new BooleanQuery();
-				for (String term : terms) {
-					Query termQuery = createTermQuery(fieldContext, term);
+				for (String localTerm : terms) {
+					Query termQuery = createTermQuery(fieldContext, localTerm);
 					booleanQuery.add( termQuery, BooleanClause.Occur.SHOULD );
 				}
 				perFieldQuery = booleanQuery;
@@ -92,45 +88,34 @@
 
 	private Query createTermQuery(FieldContext fieldContext, String term) {
 		Query query;
+		final String fieldName = fieldContext.getField();
 		switch ( queryContext.getApproximation() ) {
 			case EXACT:
-				query = new TermQuery( new Term( fieldContext.getField(), term ) );
+				query = new TermQuery( new Term( fieldName, term ) );
 				break;
 			case WILDCARD:
-				query = new WildcardQuery( new Term( fieldContext.getField(), term ) );
+				query = new WildcardQuery( new Term( fieldName, term ) );
 				break;
 			case FUZZY:
 				query = new FuzzyQuery(
-						new Term( fieldContext.getField(), term ),
+						new Term( fieldName, term ),
 						queryContext.getThreshold(),
 						queryContext.getPrefixLength() );
 				break;
 			default:
-				throw new AssertionFailure( "Unknown approximation: " + queryContext.getApproximation());
+				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
+	private List<String> getAllTermsFromText(String fieldName, String localText, Analyzer analyzer) throws IOException {
+		//it's better not to apply the analyzer with wildcard as * and ? can be mistakenly removed
 		List<String> terms = new ArrayList<String>();
-		if ( queryContext.getApproximation() == QueryContext.Approximation.WILDCARD ) {
-			terms.add( text );
+		if ( queryContext.getApproximation() == TermQueryContext.Approximation.WILDCARD ) {
+			terms.add( localText );
 		}
 		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();
+			terms = Helper.getAllTermsFromText( fieldName, localText, analyzer );
 		}
 		return terms;
 	}

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-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedQueryBuilder.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -7,6 +7,7 @@
 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.RangeContext;
 import org.hibernate.search.query.dsl.v2.TermContext;
 import org.hibernate.search.query.dsl.v2.WildcardContext;
 
@@ -36,6 +37,10 @@
 		return new ConnectedWildcardContext(queryAnalyzer, factory);
 	}
 
+	public RangeContext range() {
+		return new ConnectedRangeContext( 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/ConnectedRangeContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedRangeContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedRangeContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -0,0 +1,136 @@
+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.RangeContext;
+import org.hibernate.search.query.dsl.v2.RangeMatchingContext;
+
+/**
+ * @author Emmanuel Bernard
+ */
+class ConnectedRangeContext implements RangeContext {
+	private final SearchFactory factory;
+	private final Analyzer queryAnalyzer;
+	private final QueryCustomizer queryCustomizer;
+
+	public ConnectedRangeContext(Analyzer queryAnalyzer, SearchFactory factory) {
+		this.factory = factory;
+		this.queryAnalyzer = queryAnalyzer;
+		this.queryCustomizer = new QueryCustomizer();
+	}
+
+	public RangeMatchingContext onField(String fieldName) {
+		return new ConnectedRangeMatchingContext(fieldName, queryCustomizer, queryAnalyzer, factory);
+	}
+
+	public RangeContext boostedTo(float boost) {
+		queryCustomizer.boostedTo( boost );
+		return this;
+	}
+
+	public RangeContext constantScore() {
+		queryCustomizer.constantScore();
+		return this;
+	}
+
+	public RangeContext filter(Filter filter) {
+		queryCustomizer.filter(filter);
+		return this;
+	}
+
+
+//
+//	public <T> FromRangeContext<T> from(T from) {
+//		context.setFrom( from );
+//		return new ConnectedFromRangeContext<T>(this);
+//	}
+//
+//
+//
+//	SearchFactory getFactory() {
+//		return factory;
+//	}
+//
+//	Analyzer getQueryAnalyzer() {
+//		return queryAnalyzer;
+//	}
+//
+//	QueryCustomizer getQueryCustomizer() {
+//		return queryCustomizer;
+//	}
+//
+//	static class ConnectedFromRangeContext<T> implements FromRangeContext<T> {
+//		private ConnectedRangeContext mother;
+//
+//		public ConnectedFromRangeContext(ConnectedRangeContext mother) {
+//			this.mother = mother;
+//		}
+//
+//		public ToRangeContext to(Object to) {
+//			mother.getContext().setTo( to );
+//			return new ConnectedToRangeContext(mother);
+//		}
+//
+//		public FromRangeContext<T> exclude() {
+//			mother.getContext().setExcludeFrom( true );
+//			return this;
+//		}
+//
+//		public FromRangeContext<T> boostedTo(float boost) {
+//			mother.boostedTo( boost );
+//			return this;
+//		}
+//
+//		public FromRangeContext<T> constantScore() {
+//			mother.constantScore();
+//			return this;
+//		}
+//
+//		public FromRangeContext<T> filter(Filter filter) {
+//			mother.filter( filter );
+//			return this;
+//		}
+//	}
+//
+//	static class ConnectedToRangeContext implements ToRangeContext {
+//		private ConnectedRangeContext mother;
+//
+//		public ConnectedToRangeContext(ConnectedRangeContext mother) {
+//			this.mother = mother;
+//		}
+//
+//		public TermMatchingContext onField(String field) {
+//			return new ConnectedTermMatchingContext(
+//					mother.getContext(),
+//					field,
+//					mother.getQueryCustomizer(),
+//					mother.getQueryAnalyzer(),
+//					mother.getFactory()
+//			);
+//		}
+//
+//		public ToRangeContext exclude() {
+//			mother.getContext().setExcludeTo( true );
+//			return this;
+//		}
+//
+//		public ToRangeContext boostedTo(float boost) {
+//			mother.boostedTo( boost );
+//			return this;
+//		}
+//
+//		public ToRangeContext constantScore() {
+//			mother.constantScore();
+//			return this;
+//		}
+//
+//		public ToRangeContext filter(Filter filter) {
+//			mother.filter( filter );
+//			return this;
+//		}
+//	}
+
+
+}

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedRangeMatchingContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedRangeMatchingContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedRangeMatchingContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -0,0 +1,97 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lucene.analysis.Analyzer;
+
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.query.dsl.v2.RangeMatchingContext;
+import org.hibernate.search.query.dsl.v2.RangeTerminationExcludable;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ConnectedRangeMatchingContext implements RangeMatchingContext {
+	private final SearchFactory factory;
+	private final Analyzer queryAnalyzer;
+	private final QueryCustomizer queryCustomizer;
+	private final RangeQueryContext queryContext;
+	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 ConnectedRangeMatchingContext(String fieldName,
+										 QueryCustomizer queryCustomizer,
+										 Analyzer queryAnalyzer,
+										 SearchFactory factory) {
+		this.factory = factory;
+		this.queryAnalyzer = queryAnalyzer;
+		this.queryCustomizer = queryCustomizer;
+		this.queryContext = new RangeQueryContext();
+		this.fieldContexts = new ArrayList<FieldContext>(4);
+		this.fieldContexts.add( new FieldContext( fieldName ) );
+	}
+
+	public RangeMatchingContext andField(String field) {
+		this.fieldContexts.add( new FieldContext( field ) );
+		this.firstOfContext = fieldContexts.size() - 1;
+		return this;
+	}
+
+	public <T> FromRangeContext<T> from(T from) {
+		queryContext.setFrom( from );
+		return new ConnectedFromRangeContext<T>(this);
+	}
+
+	static class ConnectedFromRangeContext<T> implements FromRangeContext<T> {
+		private ConnectedRangeMatchingContext mother;
+
+		ConnectedFromRangeContext(ConnectedRangeMatchingContext mother) {
+			this.mother = mother;
+		}
+
+		public RangeTerminationExcludable to(T to) {
+			mother.queryContext.setTo(to);
+			return new ConnectedMultiFieldsRangeQueryBuilder(
+					mother.queryContext,
+					mother.queryAnalyzer,
+					mother.queryCustomizer,
+					mother.fieldContexts);
+		}
+
+		public FromRangeContext<T> exclude() {
+			mother.queryContext.setExcludeFrom( true );
+			return this;
+		}
+	}
+
+	public RangeTerminationExcludable below(Object below) {
+		queryContext.setTo( below );
+		return new ConnectedMultiFieldsRangeQueryBuilder(queryContext, queryAnalyzer, queryCustomizer, fieldContexts);
+	}
+
+	public RangeTerminationExcludable above(Object above) {
+		queryContext.setFrom( above );
+		return new ConnectedMultiFieldsRangeQueryBuilder(queryContext, queryAnalyzer, queryCustomizer, fieldContexts);
+	}
+
+	public RangeMatchingContext boostedTo(float boost) {
+		for ( FieldContext fieldContext : getCurrentFieldContexts() ) {
+			fieldContext.getFieldCustomizer().boostedTo( boost );
+		}
+		return this;
+	}
+
+	private List<FieldContext> getCurrentFieldContexts() {
+		return fieldContexts.subList( firstOfContext, fieldContexts.size() );
+	}
+
+	public RangeMatchingContext ignoreAnalyzer() {
+		for ( FieldContext fieldContext : getCurrentFieldContexts() ) {
+			fieldContext.setIgnoreAnalyzer( true );
+		}
+		return this;
+	}
+}

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-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -14,13 +14,13 @@
 	private final SearchFactory factory;
 	private final Analyzer queryAnalyzer;
 	private final QueryCustomizer queryCustomizer;
-	private final QueryContext context;
+	private final TermQueryContext context;
 
 	public ConnectedTermContext(Analyzer queryAnalyzer, SearchFactory factory) {
 		this.factory = factory;
 		this.queryAnalyzer = queryAnalyzer;
 		this.queryCustomizer = new QueryCustomizer();
-		this.context = new QueryContext( QueryContext.Approximation.EXACT);
+		this.context = new TermQueryContext( TermQueryContext.Approximation.EXACT);
 	}
 
 	public TermMatchingContext onField(String field) {

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-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermMatchingContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -16,13 +16,13 @@
 	private final SearchFactory factory;
 	private final Analyzer queryAnalyzer;
 	private final QueryCustomizer queryCustomizer;
-	private final QueryContext queryContext;
+	private final TermQueryContext queryContext;
 	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,
+	public ConnectedTermMatchingContext(TermQueryContext queryContext,
 			String field, QueryCustomizer queryCustomizer, Analyzer queryAnalyzer, SearchFactory factory) {
 		this.factory = factory;
 		this.queryAnalyzer = queryAnalyzer;
@@ -32,7 +32,7 @@
 		this.fieldContexts.add( new FieldContext( field ) );
 	}
 
-	public ConnectedTermMatchingContext(QueryContext queryContext,
+	public ConnectedTermMatchingContext(TermQueryContext queryContext,
 			String[] fields, QueryCustomizer queryCustomizer, Analyzer queryAnalyzer, SearchFactory factory) {
 		this.factory = factory;
 		this.queryAnalyzer = queryAnalyzer;

Modified: 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/ConnectedWildcardContext.java	2010-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedWildcardContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -4,7 +4,6 @@
 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;
 
@@ -15,13 +14,13 @@
 	private final SearchFactory factory;
 	private final Analyzer queryAnalyzer;
 	private final QueryCustomizer queryCustomizer;
-	private final QueryContext context;
+	private final TermQueryContext context;
 
 	public ConnectedWildcardContext(Analyzer queryAnalyzer, SearchFactory factory) {
 		this.factory = factory;
 		this.queryAnalyzer = queryAnalyzer;
 		this.queryCustomizer = new QueryCustomizer();
-		this.context = new QueryContext( QueryContext.Approximation.WILDCARD);
+		this.context = new TermQueryContext( TermQueryContext.Approximation.WILDCARD);
 	}
 
 	public TermMatchingContext onField(String field) {

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/Helper.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/Helper.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/Helper.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -0,0 +1,57 @@
+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.hibernate.annotations.common.AssertionFailure;
+import org.hibernate.search.SearchException;
+
+/**
+ * @author Emmanuel Bernard
+ */
+class Helper {
+	/**
+	 * return the analyzed value for a given field. If several terms are created, an exception is raised.
+	 */
+	static String getAnalyzedTerm(String fieldName, Object value, String name, Analyzer queryAnalyzer) {
+		try {
+			final List<String> termsFromText = getAllTermsFromText(
+					fieldName, value.toString(), queryAnalyzer
+			);
+			if (termsFromText.size() > 1) {
+				throw new SearchException( "The " + name + " parameter leads to several terms when analyzed");
+			}
+			return termsFromText.size() == 0 ? null : termsFromText.get( 0 );
+		}
+		catch ( IOException e ) {
+			throw new AssertionFailure("IO exception while reading String stream??", e);
+		}
+	}
+
+	static List<String> getAllTermsFromText(String fieldName, String localText, Analyzer analyzer) throws IOException {
+		//it's better not to apply the analyzer with wildcard as * and ? can be mistakenly removed
+		List<String> terms = new ArrayList<String>();
+
+		Reader reader = new StringReader(localText);
+		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/QueryContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryContext.java	2010-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/QueryContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -1,40 +0,0 @@
-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
-	}
-}

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/RangeQueryContext.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/RangeQueryContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/RangeQueryContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -0,0 +1,44 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class RangeQueryContext {
+	//RANGE
+	private Object from;
+	private Object to;
+	private boolean excludeFrom;
+	private boolean excludeTo;
+
+	public Object getFrom() {
+		return from;
+	}
+
+	public void setFrom(Object from) {
+		this.from = from;
+	}
+
+	public Object getTo() {
+		return to;
+	}
+
+	public void setTo(Object to) {
+		this.to = to;
+	}
+
+	public boolean isExcludeFrom() {
+		return excludeFrom;
+	}
+
+	public void setExcludeFrom(boolean excludeFrom) {
+		this.excludeFrom = excludeFrom;
+	}
+
+	public boolean isExcludeTo() {
+		return excludeTo;
+	}
+
+	public void setExcludeTo(boolean excludeTo) {
+		this.excludeTo = excludeTo;
+	}
+}

Copied: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/TermQueryContext.java (from rev 19569, 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/TermQueryContext.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/TermQueryContext.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -0,0 +1,43 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+/**
+* @author Emmanuel Bernard
+*/
+class TermQueryContext {
+	private final Approximation approximation;
+	//FUZZY
+	private float threshold = .5f;
+
+	//WILDCARD
+	private int prefixLength = 0;
+
+	public TermQueryContext(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/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-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/DSLTest.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -1,6 +1,11 @@
 package org.hibernate.search.test.query.dsl;
 
+import java.text.Format;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.List;
+import java.util.TimeZone;
 
 import org.apache.lucene.search.Query;
 import org.apache.solr.analysis.LowerCaseFilterFactory;
@@ -14,6 +19,7 @@
 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;
@@ -237,7 +243,60 @@
 		cleanData( fts );
 	}
 
+	public void testRangeQuery() throws Exception {
+		FullTextSession fts = initData();
 
+		Transaction transaction = fts.beginTransaction();
+		final QueryBuilder monthQb = fts.getSearchFactory()
+				.buildQueryBuilder().forEntity( Month.class ).get();
+
+		final Calendar calendar = Calendar.getInstance();
+		calendar.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
+		calendar.set(0 + 1900, 2, 12, 0, 0, 0);
+		Date from = calendar.getTime();
+		calendar.set(10 + 1900, 2, 12, 0, 0, 0);
+		Date to = calendar.getTime();
+		final SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyyMMdd" );
+
+		Query
+
+		query = monthQb.
+				range()
+					.onField( "estimatedCreation" )
+					.andField( "justfortest" )
+					.from( dateFormat.format( from ) )
+					.to( dateFormat.format( to ) ).exclude()
+					.createQuery();
+
+		assertEquals( 1, fts.createFullTextQuery( query, Month.class ).getResultSize() );
+
+		query = monthQb.
+				range()
+					.onField( "estimatedCreation" )
+					.andField( "justfortest" )
+					.below( dateFormat.format( to ) )
+					.createQuery();
+
+		FullTextQuery hibQuery = fts.createFullTextQuery( query, Month.class );
+		assertEquals( 1, hibQuery.getResultSize() );
+		assertEquals( "January", ( (Month) hibQuery.list().get( 0 ) ).getName() );
+
+		query = monthQb.
+				range()
+					.onField( "estimatedCreation" )
+					.andField( "justfortest" )
+					.above( dateFormat.format( to ) )
+					.createQuery();
+		hibQuery = fts.createFullTextQuery( query, Month.class );
+		assertEquals( 1, hibQuery.getResultSize() );
+		assertEquals( "February", ( (Month) hibQuery.list().get( 0 ) ).getName() );
+
+		transaction.commit();
+
+		cleanData( fts );
+	}
+
+
 //	public void testTermQueryOnAnalyzer() throws Exception {
 //		FullTextSession fts = initData();
 //
@@ -363,8 +422,20 @@
 		Session session = openSession();
 		FullTextSession fts = Search.getFullTextSession( session );
 		Transaction tx = fts.beginTransaction();
-		fts.persist( new Month("January", "Month of colder and whitening", "Historically colder than any other month in the northern hemisphere") );
-		fts.persist( new Month("February", "Month of snowboarding", "Historically, the month where we make babies while watching the whitening landscape") );
+		final Calendar calendar = Calendar.getInstance();
+		calendar.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
+		calendar.set(0 + 1900, 2, 12, 0, 0, 0);
+		fts.persist( new Month(
+				"January",
+				"Month of colder and whitening",
+				"Historically colder than any other month in the northern hemisphere",
+				 calendar.getTime() ) );
+		calendar.set(100 + 1900, 2, 12, 0, 0, 0);
+		fts.persist( new Month(
+				"February",
+				"Month of snowboarding",
+				"Historically, the month where we make babies while watching the whitening landscape",
+				calendar.getTime() ) );
 		tx.commit();
 		fts.clear();
 		return fts;

Modified: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/Month.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/Month.java	2010-05-22 12:31:15 UTC (rev 19588)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/query/dsl/Month.java	2010-05-23 09:11:49 UTC (rev 19589)
@@ -1,13 +1,17 @@
 package org.hibernate.search.test.query.dsl;
 
+import java.util.Date;
 import javax.persistence.Entity;
 import javax.persistence.GeneratedValue;
 import javax.persistence.Id;
 
 import org.hibernate.search.annotations.Analyzer;
+import org.hibernate.search.annotations.DateBridge;
 import org.hibernate.search.annotations.Field;
 import org.hibernate.search.annotations.Fields;
+import org.hibernate.search.annotations.Index;
 import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.Resolution;
 
 /**
  * @author Emmanuel Bernard
@@ -17,10 +21,11 @@
 public class Month {
 	public Month() {}
 	
-	public Month(String name, String mythology, String history) {
+	public Month(String name, String mythology, String history, Date estimatedCreation) {
 		this.name = name;
 		this.mythology = mythology;
 		this.history = history;
+		this.estimatedCreation = estimatedCreation;
 	}
 
 	@Id @GeneratedValue
@@ -47,5 +52,10 @@
 	public void setHistory(String history) { this.history = history; }
 	private String history;
 
+	@Field(index = Index.UN_TOKENIZED) @DateBridge(resolution = Resolution.MINUTE)
+	public Date getEstimatedCreation() { return estimatedCreation; }
+	public void setEstimatedCreation(Date estimatedCreation) { this.estimatedCreation = estimatedCreation; }
+	private Date estimatedCreation;
+
 }
 



More information about the hibernate-commits mailing list