Author: epbernard
Date: 2007-06-28 11:37:54 -0400 (Thu, 28 Jun 2007)
New Revision: 11715
Added:
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Analyzer.java
trunk/HibernateExt/search/src/java/org/hibernate/search/util/ScopedAnalyzer.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/AbstractTestAnalyzer.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/AnalyzerTest.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/MyComponent.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/MyEntity.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test1Analyzer.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test2Analyzer.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test3Analyzer.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test4Analyzer.java
Modified:
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Field.java
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/DocumentBuilder.java
trunk/HibernateExt/search/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
Log:
HSEARCH-41 Add scoped Analyzer
Added: trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Analyzer.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Analyzer.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Analyzer.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,26 @@
+//$Id$
+package org.hibernate.search.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Documented;
+
+/**
+ * Define an Analizer for a given entity, method, field or Field
+ * The order of precedence is as such:
+ * - @Field
+ * - field / method
+ * - entity
+ * - default
+ *
+ * @author Emmanuel Bernard
+ */
+@Retention( RetentionPolicy.RUNTIME )
+@Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD} )
+@Documented
+
+public @interface Analyzer {
+ Class impl() default void.class;
+}
Modified: trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Field.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Field.java 2007-06-24
18:00:46 UTC (rev 11714)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Field.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -35,4 +35,10 @@
*/
Index index();
+ /**
+ * Define an analyzer for the field, default to
+ * the inherited analyzer
+ */
+ Analyzer analyzer() default @Analyzer;
+
}
Modified:
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/DocumentBuilder.java 2007-06-24
18:00:46 UTC (rev 11714)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/DocumentBuilder.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -45,6 +45,7 @@
import org.hibernate.search.bridge.TwoWayFieldBridge;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.util.BinderHelper;
+import org.hibernate.search.util.ScopedAnalyzer;
/**
* Set up and provide a manager for indexes classes
@@ -57,12 +58,11 @@
public class DocumentBuilder<T> {
private static final Log log = LogFactory.getLog( DocumentBuilder.class );
- private final PropertiesMetadata rootPropertiesMetadata = new PropertiesMetadata();
+ private final PropertiesMetadata rootPropertiesMetadata;
private final XClass beanClass;
private final DirectoryProvider directoryProvider;
private String idKeywordName;
private XMember idGetter;
- private final Analyzer analyzer;
private Float idBoost;
public static final String CLASS_FIELDNAME = "_hibernate_class";
private TwoWayFieldBridge idBridge;
@@ -70,32 +70,73 @@
private ReflectionManager reflectionManager;
private int level = 0;
private int maxLevel = Integer.MAX_VALUE;
+ private ScopedAnalyzer analyzer;
- public DocumentBuilder(XClass clazz, Analyzer analyzer, DirectoryProvider directory,
+ public DocumentBuilder(XClass clazz, Analyzer defaultAnalyzer, DirectoryProvider
directory,
ReflectionManager reflectionManager) {
+ this.analyzer = new ScopedAnalyzer();
this.beanClass = clazz;
- this.analyzer = analyzer;
this.directoryProvider = directory;
//FIXME get rid of it when boost is stored?
this.reflectionManager = reflectionManager;
if ( clazz == null ) throw new AssertionFailure( "Unable to build a
DocumemntBuilder with a null class" );
-
+ rootPropertiesMetadata = new PropertiesMetadata();
rootPropertiesMetadata.boost = getBoost( clazz );
+ rootPropertiesMetadata.analyzer = defaultAnalyzer;
Set<XClass> processedClasses = new HashSet<XClass>();
processedClasses.add( clazz );
initializeMembers(clazz, rootPropertiesMetadata, true, "", processedClasses
);
//processedClasses.remove( clazz ); for the sake of completness
-
+ this.analyzer.setGlobalAnalyzer( rootPropertiesMetadata.analyzer );
if ( idKeywordName == null ) {
throw new SearchException( "No document id in: " + clazz.getName() );
}
}
+ private Analyzer getAnalyzer(XAnnotatedElement annotatedElement) {
+ org.hibernate.search.annotations.Analyzer analyzerAnn =
+ annotatedElement.getAnnotation( org.hibernate.search.annotations.Analyzer.class );
+ return getAnalyzer( analyzerAnn );
+ }
+
+ private Analyzer getAnalyzer(org.hibernate.search.annotations.Analyzer analyzerAnn) {
+ Class analyzerClass = analyzerAnn == null ? void.class : analyzerAnn.impl();
+ if ( analyzerClass == void.class ) {
+ return null;
+ }
+ else {
+ try {
+ return (Analyzer) analyzerClass.newInstance();
+ }
+ catch (ClassCastException e) {
+ throw new SearchException(
+ "Lucene analyzer does not implement " + Analyzer.class.getName() +
": " + analyzerClass.getName(), e
+ );
+ }
+ catch (Exception e) {
+ throw new SearchException( "Failed to instantiate lucene analyzer with type
" + analyzerClass.getName(), e );
+ }
+ }
+ }
+
private void initializeMembers(XClass clazz, PropertiesMetadata propertiesMetadata,
boolean isRoot, String prefix,
Set<XClass> processedClasses) {
+ List<XClass> hierarchy = new ArrayList<XClass>();
for ( XClass currClass = clazz; currClass != null; currClass =
currClass.getSuperclass() ) {
+ hierarchy.add( currClass );
+ }
+ for (int index = hierarchy.size() -1 ; index >= 0 ; index--) {
+ XClass currClass = hierarchy.get( index );
+ /**
+ * Override the default analyzer for the properties if the class hold one
+ * That's the reason we go down the hierarchy
+ */
+ Analyzer analyzer = getAnalyzer( currClass );
+ if ( analyzer != null) {
+ propertiesMetadata.analyzer = analyzer;
+ }
//rejecting non properties because the object is loaded from Hibernate, so indexing a
non property does not make sense
List<XProperty> methods = currClass.getDeclaredProperties(
XClass.ACCESS_PROPERTY );
for ( XProperty method : methods ) {
@@ -181,10 +222,17 @@
if ( fieldAnn != null ) {
setAccessible( member );
propertiesMetadata.fieldGetters.add( member );
- propertiesMetadata.fieldNames.add( prefix + BinderHelper.getAttributeName( member,
fieldAnn.name() ) );
+ String fieldName = prefix + BinderHelper.getAttributeName( member, fieldAnn.name() );
+ propertiesMetadata.fieldNames.add( fieldName );
propertiesMetadata.fieldStore.add( getStore( fieldAnn.store() ) );
propertiesMetadata.fieldIndex.add( getIndex( fieldAnn.index() ) );
propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( member ) );
+ // Field > property > entity analyzer
+ Analyzer analyzer = getAnalyzer( fieldAnn.analyzer() );
+ if (analyzer == null) analyzer = getAnalyzer( member );
+ if (analyzer == null) analyzer = propertiesMetadata.analyzer;
+ if (analyzer == null) throw new AssertionFailure( "Analizer should not be
undefined" );
+ this.analyzer.addScopedAnalyzer( fieldName, analyzer );
}
IndexedEmbedded embeddedAnn = member.getAnnotation( IndexedEmbedded.class );
@@ -211,6 +259,9 @@
PropertiesMetadata metadata = new PropertiesMetadata();
propertiesMetadata.embeddedPropertiesMetadata.add(metadata);
metadata.boost = getBoost( member );
+ //property > entity analyzer
+ Analyzer analyzer = getAnalyzer( member );
+ metadata.analyzer = analyzer != null ? analyzer : propertiesMetadata.analyzer;
String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
initializeMembers( elementClass, metadata, false, localPrefix, processedClasses);
/**
@@ -623,7 +674,8 @@
}
private static class PropertiesMetadata {
- public Float boost = null;
+ public Float boost;
+ public Analyzer analyzer;
public final List<XMember> keywordGetters = new ArrayList<XMember>();
public final List<String> keywordNames = new ArrayList<String>();
public final List<FieldBridge> keywordBridges = new
ArrayList<FieldBridge>();
Modified:
trunk/HibernateExt/search/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2007-06-24
18:00:46 UTC (rev 11714)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -85,17 +85,17 @@
analyzerClass = StandardAnalyzer.class;
}
// Initialize analyzer
- Analyzer analyzer;
+ Analyzer defaultAnalyzer;
try {
- analyzer = (Analyzer) analyzerClass.newInstance();
+ defaultAnalyzer = (Analyzer) analyzerClass.newInstance();
}
catch (ClassCastException e) {
throw new SearchException(
- "Lucene analyzer does not implement " + Analyzer.class.getName() + ":
" + analyzerClassName
+ "Lucene analyzer does not implement " + Analyzer.class.getName() + ":
" + analyzerClassName, e
);
}
catch (Exception e) {
- throw new SearchException( "Failed to instantiate lucene analyzer with type
" + analyzerClassName );
+ throw new SearchException( "Failed to instantiate lucene analyzer with type
" + analyzerClassName, e );
}
Iterator iter = cfg.getClassMappings();
@@ -112,7 +112,7 @@
lockableDirectoryProviders.put( provider, new ReentrantLock() );
}
final DocumentBuilder<Object> documentBuilder = new
DocumentBuilder<Object>(
- mappedXClass, analyzer, provider, reflectionManager
+ mappedXClass, defaultAnalyzer, provider, reflectionManager
);
documentBuilders.put( mappedClass, documentBuilder );
Added: trunk/HibernateExt/search/src/java/org/hibernate/search/util/ScopedAnalyzer.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/util/ScopedAnalyzer.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/util/ScopedAnalyzer.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,43 @@
+//$Id$
+package org.hibernate.search.util;
+
+import java.io.Reader;
+import java.util.Map;
+import java.util.HashMap;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.TokenStream;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ScopedAnalyzer extends Analyzer {
+ public ScopedAnalyzer() {}
+
+ private Analyzer globalAnalyzer;
+ private Map<String, Analyzer> scopedAnalyzers = new HashMap<String,
Analyzer>();
+
+ public void setGlobalAnalyzer(Analyzer globalAnalyzer) {
+ this.globalAnalyzer = globalAnalyzer;
+ }
+
+ public void addScopedAnalyzer(String scope, Analyzer scopedAnalyzer) {
+ scopedAnalyzers.put( scope, scopedAnalyzer );
+ }
+
+ public TokenStream tokenStream(String fieldName, Reader reader) {
+ return getAnalyzer( fieldName ).tokenStream( fieldName, reader );
+ }
+
+ public int getPositionIncrementGap(String fieldName) {
+ return getAnalyzer( fieldName ).getPositionIncrementGap( fieldName );
+ }
+
+ private Analyzer getAnalyzer(String fieldName) {
+ Analyzer analyzer = scopedAnalyzers.get( fieldName );
+ if (analyzer == null) {
+ analyzer = globalAnalyzer;
+ }
+ return analyzer;
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/AbstractTestAnalyzer.java
===================================================================
---
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/AbstractTestAnalyzer.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/AbstractTestAnalyzer.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,35 @@
+//$Id$
+package org.hibernate.search.test.analyzer;
+
+import java.io.Reader;
+import java.io.IOException;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.Token;
+
+
+/**
+ * @author Emmanuel Bernard
+ */
+public abstract class AbstractTestAnalyzer extends Analyzer {
+
+ protected abstract String[] getTokens();
+
+ public TokenStream tokenStream(String fieldName, Reader reader) {
+ return new InternalTokenStream();
+ }
+
+ private class InternalTokenStream extends TokenStream {
+ private int position;
+
+ public Token next() throws IOException {
+ if ( position >= getTokens().length) {
+ return null;
+ }
+ else {
+ return new Token( getTokens()[position++], 0, 0 );
+ }
+ }
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/AnalyzerTest.java
===================================================================
---
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/AnalyzerTest.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/AnalyzerTest.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,61 @@
+//$Id$
+package org.hibernate.search.test.analyzer;
+
+import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.FullTextQuery;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.Query;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class AnalyzerTest extends SearchTestCase {
+
+ public void testScopedAnalyzers() throws Exception {
+ MyEntity en = new MyEntity();
+ en.setEntity( "Entity" );
+ en.setField( "Field" );
+ en.setProperty( "Property" );
+ en.setComponent( new MyComponent() );
+ en.getComponent().setComponentProperty( "component property" );
+ FullTextSession s = Search.createFullTextSession( openSession() );
+ Transaction tx = s.beginTransaction();
+ s.persist( en );
+ tx.commit();
+
+ tx = s.beginTransaction();
+ QueryParser parser = new QueryParser("id", new StandardAnalyzer() );
+ org.apache.lucene.search.Query luceneQuery = parser.parse( "entity:alarm");
+ FullTextQuery query = s.createFullTextQuery( luceneQuery, MyEntity.class );
+ assertEquals( 1, query.getResultSize() );
+
+ luceneQuery = parser.parse( "property:cat");
+ query = s.createFullTextQuery( luceneQuery, MyEntity.class );
+ assertEquals( 1, query.getResultSize() );
+
+ luceneQuery = parser.parse( "field:energy");
+ query = s.createFullTextQuery( luceneQuery, MyEntity.class );
+ assertEquals( 1, query.getResultSize() );
+
+ luceneQuery = parser.parse( "component.componentProperty:noise");
+ query = s.createFullTextQuery( luceneQuery, MyEntity.class );
+ assertEquals( 1, query.getResultSize() );
+
+ s.delete( query.uniqueResult() );
+ tx.commit();
+
+ s.close();
+
+ }
+
+ protected Class[] getMappings() {
+ return new Class[] {
+ MyEntity.class
+ };
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/MyComponent.java
===================================================================
---
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/MyComponent.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/MyComponent.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,23 @@
+//$Id$
+package org.hibernate.search.test.analyzer;
+
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.Analyzer;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class MyComponent {
+ @Field(index = Index.TOKENIZED)
+ @Analyzer(impl = Test4Analyzer.class)
+ private String componentProperty;
+
+ public String getComponentProperty() {
+ return componentProperty;
+ }
+
+ public void setComponentProperty(String componentProperty) {
+ this.componentProperty = componentProperty;
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/MyEntity.java
===================================================================
--- trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/MyEntity.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/MyEntity.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,82 @@
+//$Id$
+package org.hibernate.search.test.analyzer;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Embedded;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.Analyzer;
+import org.hibernate.search.annotations.IndexedEmbedded;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+@Indexed
+@Analyzer(impl = Test1Analyzer.class)
+public class MyEntity {
+ @Id
+ @GeneratedValue
+ @DocumentId
+ private Integer id;
+
+ @Field(index = Index.TOKENIZED)
+ private String entity;
+
+ @Field(index = Index.TOKENIZED)
+ @Analyzer(impl = Test2Analyzer.class)
+ private String property;
+
+ @Field(index = Index.TOKENIZED, analyzer = @Analyzer(impl = Test3Analyzer.class) )
+ @Analyzer(impl = Test2Analyzer.class)
+ private String field;
+
+ @IndexedEmbedded
+ @Embedded
+ private MyComponent component;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getEntity() {
+ return entity;
+ }
+
+ public void setEntity(String entity) {
+ this.entity = entity;
+ }
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ public String getField() {
+ return field;
+ }
+
+ public void setField(String field) {
+ this.field = field;
+ }
+
+ public MyComponent getComponent() {
+ return component;
+ }
+
+ public void setComponent(MyComponent component) {
+ this.component = component;
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test1Analyzer.java
===================================================================
---
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test1Analyzer.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test1Analyzer.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,13 @@
+//$Id$
+package org.hibernate.search.test.analyzer;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Test1Analyzer extends AbstractTestAnalyzer {
+ private final String[] tokens = { "alarm", "dog",
"performance" };
+
+ protected String[] getTokens() {
+ return tokens;
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test2Analyzer.java
===================================================================
---
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test2Analyzer.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test2Analyzer.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,13 @@
+//$Id$
+package org.hibernate.search.test.analyzer;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Test2Analyzer extends AbstractTestAnalyzer {
+ private final String[] tokens = { "sound", "cat", "speed"
};
+
+ protected String[] getTokens() {
+ return tokens;
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test3Analyzer.java
===================================================================
---
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test3Analyzer.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test3Analyzer.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,13 @@
+//$Id$
+package org.hibernate.search.test.analyzer;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Test3Analyzer extends AbstractTestAnalyzer {
+ private final String[] tokens = { "music", "elephant",
"energy" };
+
+ protected String[] getTokens() {
+ return tokens;
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test4Analyzer.java
===================================================================
---
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test4Analyzer.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/analyzer/Test4Analyzer.java 2007-06-28
15:37:54 UTC (rev 11715)
@@ -0,0 +1,13 @@
+//$Id$
+package org.hibernate.search.test.analyzer;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Test4Analyzer extends AbstractTestAnalyzer {
+ private final String[] tokens = { "noise", "mouse",
"light" };
+
+ protected String[] getTokens() {
+ return tokens;
+ }
+}