[hibernate-commits] Hibernate SVN: r14474 - in search/trunk/src: java/org/hibernate/search/annotations and 6 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Thu Mar 20 18:21:30 EDT 2008


Author: epbernard
Date: 2008-03-20 18:21:30 -0400 (Thu, 20 Mar 2008)
New Revision: 14474

Added:
   search/trunk/src/java/org/hibernate/search/annotations/Similarity.java
   search/trunk/src/test/org/hibernate/search/test/similarity/
   search/trunk/src/test/org/hibernate/search/test/similarity/Can.java
   search/trunk/src/test/org/hibernate/search/test/similarity/DummySimilarity.java
   search/trunk/src/test/org/hibernate/search/test/similarity/DummySimilarity2.java
   search/trunk/src/test/org/hibernate/search/test/similarity/SimilarityTest.java
   search/trunk/src/test/org/hibernate/search/test/similarity/Trash.java
Modified:
   search/trunk/src/java/org/hibernate/search/Environment.java
   search/trunk/src/java/org/hibernate/search/backend/Workspace.java
   search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
   search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
   search/trunk/src/java/org/hibernate/search/reader/CacheableMultiReader.java
   search/trunk/src/java/org/hibernate/search/reader/ReaderProviderHelper.java
Log:
HSEARCH-133 add similarity support

Modified: search/trunk/src/java/org/hibernate/search/Environment.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/Environment.java	2008-03-20 19:01:16 UTC (rev 14473)
+++ search/trunk/src/java/org/hibernate/search/Environment.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -18,10 +18,15 @@
 	public static final String INDEXING_STRATEGY = "hibernate.search.indexing_strategy";
 
 	/**
-	 * Lucene analyser
+	 * Default Lucene analyser
 	 */
 	public static final String ANALYZER_CLASS = "hibernate.search.analyzer";
 
+	/**
+	 * Default Lucene similarity
+	 */
+	public static final String SIMILARITY_CLASS = "hibernate.search.similarity";
+
 	public static final String WORKER_PREFIX = "hibernate.search.worker.";
 	public static final String WORKER_SCOPE = WORKER_PREFIX + "scope";
 	public static final String WORKER_BACKEND = WORKER_PREFIX + "backend";

Added: search/trunk/src/java/org/hibernate/search/annotations/Similarity.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/Similarity.java	                        (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/Similarity.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -0,0 +1,19 @@
+package org.hibernate.search.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention( RetentionPolicy.RUNTIME )
+ at Target( ElementType.TYPE )
+ at Documented
+/**
+ * Specifies a similarity implementation to use
+ *
+ * @author Nick Vincent
+ */
+public @interface Similarity {
+	public Class impl();
+}

Modified: search/trunk/src/java/org/hibernate/search/backend/Workspace.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/Workspace.java	2008-03-20 19:01:16 UTC (rev 14473)
+++ search/trunk/src/java/org/hibernate/search/backend/Workspace.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -112,10 +112,12 @@
 		lockProvider( provider );
 		if ( modificationOperation ) dpStatistics.get( provider ).operations++;
 		try {
+			DocumentBuilder documentBuilder = searchFactoryImplementor.getDocumentBuilders().get( entity );
 			Analyzer analyzer = entity != null ?
-					searchFactoryImplementor.getDocumentBuilders().get( entity ).getAnalyzer() :
+					documentBuilder.getAnalyzer() :
 					new SimpleAnalyzer(); //never used
 			writer = new IndexWriter( provider.getDirectory(), analyzer, false ); //has been created at init time
+			writer.setSimilarity( documentBuilder.getSimilarity() );
 
 			LuceneIndexingParameters indexingParams = searchFactoryImplementor.getIndexingParameters( provider );
 			if ( isBatch ) {

Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java	2008-03-20 19:01:16 UTC (rev 14473)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -17,6 +17,7 @@
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Similarity;
 import org.hibernate.Hibernate;
 import org.hibernate.annotations.common.AssertionFailure;
 import org.hibernate.annotations.common.reflection.ReflectionManager;
@@ -75,9 +76,10 @@
 	private int level = 0;
 	private int maxLevel = Integer.MAX_VALUE;
 	private ScopedAnalyzer analyzer;
+	private Similarity similarity;
 
 
-	public DocumentBuilder(XClass clazz, Analyzer defaultAnalyzer, DirectoryProvider[] directoryProviders,
+	public DocumentBuilder(XClass clazz, Analyzer defaultAnalyzer, Similarity defaultSimilarity, DirectoryProvider[] directoryProviders,
 						   IndexShardingStrategy shardingStrategy, ReflectionManager reflectionManager) {
 		this.analyzer = new ScopedAnalyzer();
 		this.beanClass = clazz;
@@ -85,8 +87,9 @@
 		this.shardingStrategy = shardingStrategy;
 		//FIXME get rid of it when boost is stored?
 		this.reflectionManager = reflectionManager;
+		this.similarity = defaultSimilarity;
 
-		if ( clazz == null ) throw new AssertionFailure( "Unable to build a DocumemntBuilder with a null class" );
+		if ( clazz == null ) throw new AssertionFailure( "Unable to build a DocumentBuilder with a null class" );
 		rootPropertiesMetadata = new PropertiesMetadata();
 		rootPropertiesMetadata.boost = getBoost( clazz );
 		rootPropertiesMetadata.analyzer = defaultAnalyzer;
@@ -132,6 +135,7 @@
 		for (XClass currClass = clazz; currClass != null; currClass = currClass.getSuperclass()) {
 			hierarchy.add( currClass );
 		}
+		Class similarityClass = null;
 		for (int index = hierarchy.size() - 1; index >= 0; index--) {
 			XClass currClass = hierarchy.get( index );
 			/**
@@ -157,6 +161,18 @@
 				bindClassAnnotation( prefix, propertiesMetadata, classBridgeAnn );
 			}
 
+			//Get similarity
+			//TODO: similarity form @IndexedEmbedded are not taken care of. Exception??
+			if ( isRoot ) {
+				org.hibernate.search.annotations.Similarity similarityAnn = currClass.getAnnotation( org.hibernate.search.annotations.Similarity.class );
+				if ( similarityAnn != null ) {
+					if ( similarityClass != null ) {
+						throw new SearchException( "Multiple Similarities defined in the same class hierarchy: " + beanClass.getName() );
+					}
+					similarityClass = similarityAnn.impl();
+				}
+			}
+
 			//rejecting non properties (ie regular methods) because the object is loaded from Hibernate,
 			// so indexing a non property does not make sense
 			List<XProperty> methods = currClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
@@ -169,12 +185,25 @@
 				initializeMember( field, propertiesMetadata, isRoot, prefix, processedClasses );
 			}
 		}
+		if ( isRoot && similarityClass != null ) {
+			try {
+				similarity = (Similarity) similarityClass.newInstance();
+			}
+			catch (Exception e) {
+				log.error( "Exception attempting to instantiate Similarity '" + similarityClass.getName()
+						+ "' set for " + beanClass.getName() );
+			}
+		}
 	}
 
 	public String getIdentifierName() {
 		return idGetter.getName();
 	}
 
+	public Similarity getSimilarity() {
+		return similarity;
+	}
+
 	private void initializeMember(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot,
 								  String prefix, Set<XClass> processedClasses) {
 

Modified: search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java	2008-03-20 19:01:16 UTC (rev 14473)
+++ search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -14,6 +14,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanQuery;
@@ -21,6 +22,7 @@
 import org.apache.lucene.search.Hits;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
 import org.apache.lucene.search.Sort;
 import org.apache.lucene.search.TermQuery;
 import org.hibernate.Criteria;
@@ -49,6 +51,8 @@
 import org.hibernate.search.engine.SearchFactoryImplementor;
 import org.hibernate.search.filter.ChainedFilter;
 import org.hibernate.search.filter.FilterKey;
+import org.hibernate.search.reader.ReaderProvider;
+import static org.hibernate.search.reader.ReaderProviderHelper.getIndexReaders;
 import org.hibernate.search.store.DirectoryProvider;
 import org.hibernate.search.util.ContextHelper;
 import org.hibernate.transform.ResultTransformer;
@@ -141,7 +145,7 @@
 		}
 		finally {
 			try {
-				searchFactoryImplementor.getReaderProvider().closeReader( searcher.getIndexReader() );
+				closeSearcher( searcher, searchFactoryImplementor.getReaderProvider() );
 			}
 			catch (SearchException e) {
 				log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e );
@@ -210,7 +214,7 @@
 		catch (IOException e) {
 			//close only in case of exception
 			try {
-				searchFactory.getReaderProvider().closeReader( searcher.getIndexReader() );
+				closeSearcher( searcher, searchFactory.getReaderProvider() );
 			}
 			catch (SearchException ee) {
 				//we have the initial issue already
@@ -244,7 +248,7 @@
 			}
 			Loader loader = getLoader( sess, searchFactoryImplementor );
 			List list = loader.load( infos.toArray( new EntityInfo[infos.size()] ) );
-			if ( resultTransformer == null || loader instanceof ProjectionLoader) {
+			if ( resultTransformer == null || loader instanceof ProjectionLoader ) {
 				//stay consistent with transformTuple which can only be executed during a projection
 				return list;
 			}
@@ -257,7 +261,7 @@
 		}
 		finally {
 			try {
-				searchFactoryImplementor.getReaderProvider().closeReader( searcher.getIndexReader() );
+				closeSearcher( searcher, searchFactoryImplementor.getReaderProvider() );
 			}
 			catch (SearchException e) {
 				log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e );
@@ -285,7 +289,7 @@
 		SearchFactoryImplementor searchFactoryImplementor = getSearchFactoryImplementor();
 		if ( filterDefinitions != null && filterDefinitions.size() > 0 ) {
 			ChainedFilter chainedFilter = new ChainedFilter();
-			for ( FullTextFilterImpl filterDefinition : filterDefinitions.values() ) {
+			for (FullTextFilterImpl filterDefinition : filterDefinitions.values()) {
 				FilterDef def = searchFactoryImplementor.getFilterDefinition( filterDefinition.getName() );
 				Class implClass = def.getImpl();
 				Object instance;
@@ -298,22 +302,22 @@
 				catch (IllegalAccessException e) {
 					throw new SearchException( "Unable to create @FullTextFilterDef: " + def.getImpl(), e );
 				}
-				for ( Map.Entry<String, Object> entry : filterDefinition.getParameters().entrySet() ) {
+				for (Map.Entry<String, Object> entry : filterDefinition.getParameters().entrySet()) {
 					def.invoke( entry.getKey(), instance, entry.getValue() );
 				}
 				if ( def.isCache() && def.getKeyMethod() == null && filterDefinition.getParameters().size() > 0 ) {
-					throw new SearchException("Filter with parameters and no @Key method: " + filterDefinition.getName() );
+					throw new SearchException( "Filter with parameters and no @Key method: " + filterDefinition.getName() );
 				}
 				FilterKey key = null;
 				if ( def.isCache() ) {
 					if ( def.getKeyMethod() == null ) {
-						key = new FilterKey( ) {
+						key = new FilterKey() {
 							public int hashCode() {
 								return getImpl().hashCode();
 							}
 
 							public boolean equals(Object obj) {
-								if ( ! ( obj instanceof FilterKey ) ) return false;
+								if ( !( obj instanceof FilterKey ) ) return false;
 								FilterKey that = (FilterKey) obj;
 								return this.getImpl().equals( that.getImpl() );
 							}
@@ -324,15 +328,15 @@
 							key = (FilterKey) def.getKeyMethod().invoke( instance );
 						}
 						catch (IllegalAccessException e) {
-							throw new SearchException("Unable to access @Key method: "
+							throw new SearchException( "Unable to access @Key method: "
 									+ def.getImpl().getName() + "." + def.getKeyMethod().getName() );
 						}
 						catch (InvocationTargetException e) {
-							throw new SearchException("Unable to access @Key method: "
+							throw new SearchException( "Unable to access @Key method: "
 									+ def.getImpl().getName() + "." + def.getKeyMethod().getName() );
 						}
 						catch (ClassCastException e) {
-							throw new SearchException("@Key method does not return FilterKey: "
+							throw new SearchException( "@Key method does not return FilterKey: "
 									+ def.getImpl().getName() + "." + def.getKeyMethod().getName() );
 						}
 					}
@@ -342,21 +346,21 @@
 				Filter filter = def.isCache() ?
 						searchFactoryImplementor.getFilterCachingStrategy().getCachedFilter( key ) :
 						null;
-				if (filter == null) {
+				if ( filter == null ) {
 					if ( def.getFactoryMethod() != null ) {
 						try {
 							filter = (Filter) def.getFactoryMethod().invoke( instance );
 						}
 						catch (IllegalAccessException e) {
-							throw new SearchException("Unable to access @Factory method: "
+							throw new SearchException( "Unable to access @Factory method: "
 									+ def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
 						}
 						catch (InvocationTargetException e) {
-							throw new SearchException("Unable to access @Factory method: "
+							throw new SearchException( "Unable to access @Factory method: "
 									+ def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
 						}
 						catch (ClassCastException e) {
-							throw new SearchException("@Key method does not return a org.apache.lucene.search.Filter class: "
+							throw new SearchException( "@Key method does not return a org.apache.lucene.search.Filter class: "
 									+ def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
 						}
 					}
@@ -365,11 +369,12 @@
 							filter = (Filter) instance;
 						}
 						catch (ClassCastException e) {
-							throw new SearchException("@Key method does not return a org.apache.lucene.search.Filter class: "
+							throw new SearchException( "@Key method does not return a org.apache.lucene.search.Filter class: "
 									+ def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
 						}
 					}
-					if ( def.isCache() ) searchFactoryImplementor.getFilterCachingStrategy().addCachedFilter( key, filter );
+					if ( def.isCache() )
+						searchFactoryImplementor.getFilterCachingStrategy().addCachedFilter( key, filter );
 				}
 				chainedFilter.addFilter( filter );
 			}
@@ -422,16 +427,15 @@
 	private IndexSearcher buildSearcher(SearchFactoryImplementor searchFactoryImplementor) {
 		Map<Class, DocumentBuilder<Object>> builders = searchFactoryImplementor.getDocumentBuilders();
 		List<DirectoryProvider> directories = new ArrayList<DirectoryProvider>();
+
+		Similarity searcherSimilarity = null;
+
 		if ( classes == null || classes.length == 0 ) {
 			//no class means all classes
 			for (DocumentBuilder builder : builders.values()) {
-				final DirectoryProvider[] directoryProviders =
-						builder.getDirectoryProviderSelectionStrategy().getDirectoryProvidersForAllShards();
-				for (DirectoryProvider provider : directoryProviders) {
-					if ( !directories.contains( provider ) ) {
-						directories.add( provider );
-					}
-				}
+				searcherSimilarity = checkSimilarity( searcherSimilarity, builder );
+				final DirectoryProvider[] directoryProviders = builder.getDirectoryProviderSelectionStrategy().getDirectoryProvidersForAllShards();
+				populateDirectories( directories, directoryProviders );
 			}
 			classesAndSubclasses = null;
 		}
@@ -442,27 +446,55 @@
 				DocumentBuilder builder = builders.get( clazz );
 				if ( builder != null ) involvedClasses.addAll( builder.getMappedSubclasses() );
 			}
+
 			for (Class clazz : involvedClasses) {
 				DocumentBuilder builder = builders.get( clazz );
 				//TODO should we rather choose a polymorphic path and allow non mapped entities
 				if ( builder == null )
 					throw new HibernateException( "Not a mapped entity (don't forget to add @Indexed): " + clazz );
-				final DirectoryProvider[] directoryProviders = 
-						builder.getDirectoryProviderSelectionStrategy().getDirectoryProvidersForAllShards();
-				for (DirectoryProvider provider : directoryProviders) {
-					if ( !directories.contains( provider ) ) {
-						directories.add( provider );
-					}
-				}
+
+				final DirectoryProvider[] directoryProviders = builder.getDirectoryProviderSelectionStrategy().getDirectoryProvidersForAllShards();
+				searcherSimilarity = checkSimilarity( searcherSimilarity, builder );
+				populateDirectories( directories, directoryProviders );
 			}
 			classesAndSubclasses = involvedClasses;
 		}
 
 		//set up the searcher
 		final DirectoryProvider[] directoryProviders = directories.toArray( new DirectoryProvider[directories.size()] );
-		return new IndexSearcher( searchFactoryImplementor.getReaderProvider().openReader( directoryProviders ) );
+		IndexSearcher is = new IndexSearcher( searchFactoryImplementor.getReaderProvider().openReader( directoryProviders ) );
+		is.setSimilarity( searcherSimilarity );
+		return is;
 	}
 
+	private void populateDirectories(List<DirectoryProvider> directories, DirectoryProvider[] directoryProviders) {
+		for (DirectoryProvider provider : directoryProviders) {
+			if ( !directories.contains( provider ) ) {
+				directories.add( provider );
+			}
+		}
+	}
+
+	private Similarity checkSimilarity(Similarity similarity, DocumentBuilder builder) {
+		if ( similarity == null ) {
+			similarity = builder.getSimilarity();
+		}
+		else if ( !similarity.getClass().equals( builder.getSimilarity().getClass() ) ) {
+			throw new HibernateException( "Cannot perform search on two entities with differing Similarity implementations (" + similarity.getClass().getName() + " & " + builder.getSimilarity().getClass().getName() + ")" );
+		}
+
+		return similarity;
+	}
+
+	private void closeSearcher(Searcher searcher, ReaderProvider readerProvider) {
+		boolean trace = log.isTraceEnabled();
+		Set<IndexReader> indexReaders = getIndexReaders( searcher );
+
+		for (IndexReader indexReader : indexReaders) {
+			readerProvider.closeReader( indexReader );
+		}
+	}
+
 	private void setResultSize(Hits hits) {
 		resultSize = hits.length();
 	}
@@ -488,7 +520,8 @@
 				finally {
 					//searcher cannot be null
 					try {
-						searchFactoryImplementor.getReaderProvider().closeReader( searcher.getIndexReader() );
+						closeSearcher( searcher, searchFactoryImplementor.getReaderProvider() );
+						//searchFactoryImplementor.getReaderProvider().closeReader( searcher.getIndexReader() );
 					}
 					catch (SearchException e) {
 						log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e );
@@ -515,16 +548,16 @@
 	}
 
 	public FullTextQuery setFirstResult(int firstResult) {
-		if (firstResult < 0) {
-			throw new IllegalArgumentException("'first' pagination parameter less than 0");
+		if ( firstResult < 0 ) {
+			throw new IllegalArgumentException( "'first' pagination parameter less than 0" );
 		}
 		this.firstResult = firstResult;
 		return this;
 	}
 
 	public FullTextQuery setMaxResults(int maxResults) {
-		if (maxResults < 0) {
-			throw new IllegalArgumentException("'max' pagination parameter less than 0");
+		if ( maxResults < 0 ) {
+			throw new IllegalArgumentException( "'max' pagination parameter less than 0" );
 		}
 		this.maxResults = maxResults;
 		return this;
@@ -568,10 +601,10 @@
 		filterDefinition = new FullTextFilterImpl();
 		filterDefinition.setName( name );
 		FilterDef filterDef = getSearchFactoryImplementor().getFilterDefinition( name );
-		if (filterDef == null) {
-			throw new SearchException("Unkown @FullTextFilter: " + name);
+		if ( filterDef == null ) {
+			throw new SearchException( "Unkown @FullTextFilter: " + name );
 		}
-		filterDefinitions.put(name, filterDefinition);
+		filterDefinitions.put( name, filterDefinition );
 		return filterDefinition;
 	}
 

Modified: search/trunk/src/java/org/hibernate/search/reader/CacheableMultiReader.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/reader/CacheableMultiReader.java	2008-03-20 19:01:16 UTC (rev 14473)
+++ search/trunk/src/java/org/hibernate/search/reader/CacheableMultiReader.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -5,7 +5,6 @@
 
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.MultiReader;
-import org.hibernate.search.SearchException;
 
 /**
  * MultiReader ensuring equals returns true if the underlying readers are the same (and in the same order)
@@ -14,30 +13,34 @@
  * @author Emmanuel Bernard
  */
 public class CacheableMultiReader extends MultiReader {
-	private IndexReader[] subReaders;
 
+	// This is package private as the intention of the Lucene team seems to be to not 
+	// expose this publically (it's a protected member in Lucene 2.3)
+	IndexReader[] subReaders;
+
 	public CacheableMultiReader(IndexReader[] subReaders) throws IOException {
 		super( subReaders );
 		this.subReaders = subReaders;
 	}
 
-	/** only available since 2.3 */
+	/**
+	 * only available since 2.3
+	 */
 	/*
 	public CacheableMultiReader(IndexReader[] subReaders, boolean closeSubReaders) {
 		super( subReaders, closeSubReaders );
 		this.subReaders = subReaders;
 	}
 	 */
-
 	@Override
 	public boolean equals(Object obj) {
 		if ( this == obj ) return true;
-		if ( ! ( obj instanceof CacheableMultiReader ) ) return false;
-		CacheableMultiReader that = ( CacheableMultiReader ) obj;
+		if ( !( obj instanceof CacheableMultiReader ) ) return false;
+		CacheableMultiReader that = (CacheableMultiReader) obj;
 		int length = this.subReaders.length;
 		if ( length != that.subReaders.length ) return false;
-		for (int index = 0 ; index < length ; index++) {
-			if ( ! this.subReaders[index].equals( that.subReaders[index] ) ) return false;
+		for (int index = 0; index < length; index++) {
+			if ( !this.subReaders[index].equals( that.subReaders[index] ) ) return false;
 		}
 		return true;
 	}
@@ -46,7 +49,7 @@
 	public int hashCode() {
 		int result = 0;
 		for (Object reader : this.subReaders) {
-			result = 31*result + reader.hashCode();
+			result = 31 * result + reader.hashCode();
 		}
 		return result;
 	}

Modified: search/trunk/src/java/org/hibernate/search/reader/ReaderProviderHelper.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/reader/ReaderProviderHelper.java	2008-03-20 19:01:16 UTC (rev 14473)
+++ search/trunk/src/java/org/hibernate/search/reader/ReaderProviderHelper.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -2,9 +2,13 @@
 package org.hibernate.search.reader;
 
 import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.MultiReader;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MultiSearcher;
+import org.apache.lucene.search.Searchable;
 import org.hibernate.search.SearchException;
 
 /**
@@ -52,4 +56,48 @@
 		}
 		throw e;
 	}
+
+	/**
+	 * Find the underlying IndexReaders for the given searchable
+	 *
+	 * @param searchable The searchable to find the IndexReaders for
+	 * @return A list of all base IndexReaders used within this searchable
+	 */
+	public static Set<IndexReader> getIndexReaders(Searchable searchable) {
+		Set<IndexReader> readers = new HashSet<IndexReader>();
+		getIndexReadersInternal( readers, searchable );
+		return readers;
+	}
+
+	/**
+	 * Find the underlying IndexReaders for the given reader
+	 *
+	 * @param reader The reader to find the IndexReaders for
+	 * @return A list of all base IndexReaders used within this searchable
+	 */
+	public static Set<IndexReader> getIndexReaders(IndexReader reader) {
+		Set<IndexReader> readers = new HashSet<IndexReader>();
+		getIndexReadersInternal( readers, reader );
+		return readers;
+	}
+
+	/**
+	 * Recursive method should identify all underlying readers for any nested structure of Lucene Searchable or IndexReader
+	 *
+	 * @param readers The working list of all readers found
+	 * @param obj	 The object to find the readers within
+	 */
+	private static void getIndexReadersInternal(Set<IndexReader> readers, Object obj) {
+		if ( obj instanceof MultiSearcher ) {
+			for (Searchable s : ( (MultiSearcher) obj ).getSearchables()) {
+				getIndexReadersInternal( readers, s );
+			}
+		}
+		else if ( obj instanceof IndexSearcher ) {
+			getIndexReadersInternal( readers, ( (IndexSearcher) obj ).getIndexReader() );
+		}
+		else if ( obj instanceof IndexReader ) {
+			readers.add( (IndexReader) obj );
+		}
+	}
 }

Added: search/trunk/src/test/org/hibernate/search/test/similarity/Can.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/similarity/Can.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/similarity/Can.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -0,0 +1,40 @@
+//$
+package org.hibernate.search.test.similarity;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Indexed;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+ at Indexed
+public class Can {
+	@Id
+	@DocumentId
+	@GeneratedValue
+	private Integer id;
+	@Field
+	private String name;
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+}

Added: search/trunk/src/test/org/hibernate/search/test/similarity/DummySimilarity.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/similarity/DummySimilarity.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/similarity/DummySimilarity.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -0,0 +1,41 @@
+//$
+package org.hibernate.search.test.similarity;
+
+import org.apache.lucene.search.DefaultSimilarity;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class DummySimilarity extends DefaultSimilarity {
+	private float CONST = 1.0f;
+
+	@Override
+	public float lengthNorm(String fieldName, int numTerms) {
+		return CONST;
+	}
+
+	@Override
+	public float queryNorm(float sumOfSquaredWeights) {
+		return CONST;
+	}
+
+	@Override
+	public float tf(float freq) {
+		return CONST;
+	}
+
+	@Override
+	public float sloppyFreq(int distance) {
+		return CONST;
+	}
+
+	@Override
+	public float idf(int docFreq, int numDocs) {
+		return CONST;
+	}
+
+	@Override
+	public float coord(int overlap, int maxOverlap) {
+		return CONST;
+	}
+}

Added: search/trunk/src/test/org/hibernate/search/test/similarity/DummySimilarity2.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/similarity/DummySimilarity2.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/similarity/DummySimilarity2.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -0,0 +1,41 @@
+//$
+package org.hibernate.search.test.similarity;
+
+import org.apache.lucene.search.DefaultSimilarity;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class DummySimilarity2 extends DefaultSimilarity {
+	private float CONST = .5f;
+
+	@Override
+	public float lengthNorm(String fieldName, int numTerms) {
+		return CONST;
+	}
+
+	@Override
+	public float queryNorm(float sumOfSquaredWeights) {
+		return CONST;
+	}
+
+	@Override
+	public float tf(float freq) {
+		return CONST;
+	}
+
+	@Override
+	public float sloppyFreq(int distance) {
+		return CONST;
+	}
+
+	@Override
+	public float idf(int docFreq, int numDocs) {
+		return CONST;
+	}
+
+	@Override
+	public float coord(int overlap, int maxOverlap) {
+		return CONST;
+	}
+}
\ No newline at end of file

Added: search/trunk/src/test/org/hibernate/search/test/similarity/SimilarityTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/similarity/SimilarityTest.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/similarity/SimilarityTest.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -0,0 +1,71 @@
+//$
+package org.hibernate.search.test.similarity;
+
+import java.util.List;
+
+import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.Search;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.FullTextQuery;
+import org.hibernate.search.Environment;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.index.Term;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class SimilarityTest extends SearchTestCase {
+	public void testClassAndGlobalSimilarity() throws Exception {
+	    Session s = openSession(  );
+		Transaction tx = s.beginTransaction();
+		Trash trash = new Trash();
+		trash.setName( "Green trash" );
+		s.persist( trash );
+		trash = new Trash();
+		trash.setName( "Green Green Green trash" );
+		s.persist( trash );
+		Can can = new Can();
+		can.setName( "Green can" );
+		s.persist( can );
+		can = new Can();
+		can.setName( "Green Green Green can" );
+		s.persist( can );
+		tx.commit();
+
+		s.clear();
+
+		tx = s.beginTransaction();
+		TermQuery tq = new TermQuery( new Term("name", "green") );
+		FullTextSession fts = Search.createFullTextSession( s );
+		List results = fts.createFullTextQuery( tq, Trash.class ).setProjection( FullTextQuery.SCORE, FullTextQuery.THIS ).list();
+		assertEquals( 2, results.size() );
+		assertEquals( "Similarity not overridden at the class level", ( (Object[]) results.get( 0 ) )[0], ( (Object[]) results.get( 1 ) )[0]);
+		assertEquals( "Similarity not overridden", 1.0f, ( (Object[]) results.get( 0 ) )[0] );
+		for (Object result : results) s.delete( ( (Object[]) result )[1] );
+
+		results = fts.createFullTextQuery( tq, Can.class ).setProjection( FullTextQuery.SCORE, FullTextQuery.THIS ).list();
+		assertEquals( 2, results.size() );
+		assertEquals( "Similarity not overridden by the global setting", ( (Object[]) results.get( 0 ) )[0], ( (Object[]) results.get( 1 ) )[0]);
+		assertFalse( "Similarity not overridden by the global setting", new Float(1.0f).equals( ( (Object[]) results.get( 0 ) )[0] ) );
+		for (Object result : results) s.delete( ( (Object[]) result )[1] );
+
+		tx.commit();
+		s.close();
+
+	}
+	protected Class[] getMappings() {
+		return new Class[] {
+				Trash.class,
+				Can.class
+		};
+	}
+
+	@Override
+	protected void configure(Configuration cfg) {
+		cfg.setProperty( Environment.SIMILARITY_CLASS, DummySimilarity2.class.getName() );
+		super.configure( cfg );
+	}
+}

Added: search/trunk/src/test/org/hibernate/search/test/similarity/Trash.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/similarity/Trash.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/similarity/Trash.java	2008-03-20 22:21:30 UTC (rev 14474)
@@ -0,0 +1,42 @@
+//$
+package org.hibernate.search.test.similarity;
+
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Entity;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Similarity;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+ at Indexed
+ at Similarity(impl = DummySimilarity.class)
+public class Trash {
+	@Id
+	@DocumentId
+	@GeneratedValue
+	private Integer id;
+	@Field
+	private String name;
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+}




More information about the hibernate-commits mailing list