Hibernate SVN: r14474 - in search/trunk/src: java/org/hibernate/search/annotations and 6 other directories.
by hibernate-commits@lists.jboss.org
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;
+
+@Retention( RetentionPolicy.RUNTIME )
+@Target( ElementType.TYPE )
+@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
+ */
+@Entity
+@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
+ */
+@Entity
+@Indexed
+@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;
+ }
+}