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

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue Mar 16 14:48:41 EDT 2010


Author: epbernard
Date: 2010-03-16 14:48:40 -0400 (Tue, 16 Mar 2010)
New Revision: 19011

Added:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/
   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/QueryContextBuilder.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/impl/
   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/ConnectedQueryContextBuilder.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermContext.java
Log:
HSEARCH-414 first take at the query DSL (need tests)

Added: 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	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryBuilder.java	2010-03-16 18:48:40 UTC (rev 19011)
@@ -0,0 +1,13 @@
+package org.hibernate.search.query.dsl.v2;
+
+import org.hibernate.search.query.dsl.v2.TermContext;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface QueryBuilder {
+	/**
+	 * build a term query
+	 */
+	TermContext term();
+}

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryContextBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryContextBuilder.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/QueryContextBuilder.java	2010-03-16 18:48:40 UTC (rev 19011)
@@ -0,0 +1,15 @@
+package org.hibernate.search.query.dsl.v2;
+
+import org.apache.lucene.analysis.Analyzer;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface QueryContextBuilder {
+	//TODO make a forEntities
+	EntityContext forEntity(Class<?> entityType);
+	interface EntityContext {
+		EntityContext overridesForField(String field, String analyzerName);
+		QueryBuilder get();
+	}
+}

Added: 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	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/TermContext.java	2010-03-16 18:48:40 UTC (rev 19011)
@@ -0,0 +1,68 @@
+package org.hibernate.search.query.dsl.v2;
+
+import org.apache.lucene.search.Query;
+
+import org.hibernate.search.query.dsl.v2.QueryBuilder;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface TermContext {
+	/**
+	 * field / property the term query is executed on
+	 */
+	TermMatchingContext on(String field);
+
+	interface TermMatchingContext {
+		/**
+		 * text searched in the term query (the term is pre-analyzer unless ignoreAnalyzer is called)
+		 */
+		TermCustomization matches(String text);
+	}
+
+	interface 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();
+
+		/**
+		 * Create a Lucene query
+		 */
+		Query createQuery();
+
+		interface TermFuzzy extends TermCustomization {
+			/**
+			 * Threshold above which two terms are considered similar enough.
+			 * Value between 0 and 1 (1 excluded)
+			 * Defaults to .5
+			 */
+			TermFuzzy 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
+			 */
+			TermFuzzy prefixLength(int prefixLength);
+		}
+	}
+}

Added: 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	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedQueryBuilder.java	2010-03-16 18:48:40 UTC (rev 19011)
@@ -0,0 +1,26 @@
+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.QueryBuilder;
+import org.hibernate.search.query.dsl.v2.TermContext;
+
+/**
+ * Assuming connection with the search factory
+ * 
+ * @author Emmanuel Bernard
+ */
+public class ConnectedQueryBuilder implements QueryBuilder {
+	private final Analyzer queryAnalyzer;
+	private final SearchFactory factory;
+
+	public ConnectedQueryBuilder(Analyzer queryAnalyzer, SearchFactory factory) {
+		this.queryAnalyzer = queryAnalyzer;
+		this.factory = factory;
+	}
+
+	public TermContext term() {
+		return new ConnectedTermContext( queryAnalyzer, factory);
+	}
+}

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedQueryContextBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedQueryContextBuilder.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedQueryContextBuilder.java	2010-03-16 18:48:40 UTC (rev 19011)
@@ -0,0 +1,43 @@
+package org.hibernate.search.query.dsl.v2.impl;
+
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.query.dsl.v2.QueryBuilder;
+import org.hibernate.search.query.dsl.v2.QueryContextBuilder;
+import org.hibernate.search.util.ScopedAnalyzer;
+
+/**
+ * Assuming connection with the search factory
+ *
+ * @author Emmanuel Bernard
+ */
+public class ConnectedQueryContextBuilder implements QueryContextBuilder {
+	private final SearchFactory factory;
+
+	public ConnectedQueryContextBuilder(SearchFactory factory) {
+		this.factory = factory;
+	}
+
+	public EntityContext forEntity(Class<?> entityType) {
+		return new HSearchEntityContext(entityType, factory );
+	}
+
+	public final class HSearchEntityContext implements QueryContextBuilder.EntityContext {
+		private final ScopedAnalyzer queryAnalyzer;
+		private final SearchFactory factory;
+
+		public HSearchEntityContext(Class<?> entityType, SearchFactory factory) {
+			this.factory = factory;
+			queryAnalyzer = new ScopedAnalyzer();
+			queryAnalyzer.setGlobalAnalyzer( factory.getAnalyzer( entityType ) );
+		}
+
+		public EntityContext overridesForField(String field, String analyzerName) {
+			queryAnalyzer.addScopedAnalyzer( field, factory.getAnalyzer( analyzerName ) );
+			return this;
+		}
+
+		public QueryBuilder get() {
+			return new ConnectedQueryBuilder(queryAnalyzer, factory);
+		}
+	}
+}

Added: 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	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/query/dsl/v2/impl/ConnectedTermContext.java	2010-03-16 18:48:40 UTC (rev 19011)
@@ -0,0 +1,172 @@
+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.Token;
+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.TermContext;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ConnectedTermContext implements TermContext {
+	private final SearchFactory factory;
+	private final Analyzer queryAnalyzer;
+
+	public ConnectedTermContext(Analyzer queryAnalyzer, SearchFactory factory) {
+		this.factory = factory;
+		this.queryAnalyzer = queryAnalyzer;
+	}
+
+	public TermMatchingContext on(String field) {
+		return new ConnectedTermMatchingContext(field, queryAnalyzer, factory);
+	}
+
+	public static class ConnectedTermMatchingContext implements TermMatchingContext {
+		private final SearchFactory factory;
+		private final String field;
+		private final Analyzer queryAnalyzer;
+
+		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 static class ConnectedTermCustomization implements TermCustomization, TermCustomization.TermFuzzy {
+		private final SearchFactory factory;
+		private final String field;
+		private final String text;
+		private final Analyzer queryAnalyzer;
+
+		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;
+		}
+
+		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 Query createQuery() {
+			if ( ignoreAnalyzer ) {
+				return 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);
+				}
+
+				BooleanQuery finalQuery = new BooleanQuery();
+				for (String term : terms) {
+					Query termQuery = createTermQuery(term);
+					//termQuery.setBoost( boost );
+					finalQuery.add( termQuery, BooleanClause.Occur.SHOULD );
+				}
+				return finalQuery;
+			}
+		}
+
+		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 {
+			Reader reader = new StringReader(text);
+			TokenStream stream = analyzer.reusableTokenStream( fieldName, reader);
+			TermAttribute attribute = (TermAttribute) stream.addAttribute( TermAttribute.class );
+			stream.reset();
+			List<String> terms = new ArrayList<String>();
+			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
+		}
+	}
+}



More information about the hibernate-commits mailing list