[hibernate-commits] Hibernate SVN: r14524 - in search/trunk: src/java/org/hibernate/search/annotations and 5 other directories.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Tue Apr 22 22:31:14 EDT 2008
Author: epbernard
Date: 2008-04-22 22:31:14 -0400 (Tue, 22 Apr 2008)
New Revision: 14524
Added:
search/trunk/lib/apache-solr-1.2.0.jar
search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDef.java
search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDefs.java
search/trunk/src/java/org/hibernate/search/annotations/TokenFilterDef.java
search/trunk/src/java/org/hibernate/search/annotations/TokenizerDef.java
search/trunk/src/java/org/hibernate/search/impl/InitContext.java
search/trunk/src/java/org/hibernate/search/util/DelegateNamedAnalyzer.java
search/trunk/src/test/org/hibernate/search/test/analyzer/solr/
search/trunk/src/test/org/hibernate/search/test/analyzer/solr/SolrAnalyzerTest.java
search/trunk/src/test/org/hibernate/search/test/analyzer/solr/Team.java
search/trunk/src/test/org/hibernate/search/test/analyzer/solr/stoplist.properties
Modified:
search/trunk/lib/README.txt
search/trunk/src/java/org/hibernate/search/annotations/Analyzer.java
search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
Log:
HSEARCH-186 Integrate Solr analyzer infrastructure
Modified: search/trunk/lib/README.txt
===================================================================
--- search/trunk/lib/README.txt 2008-04-22 15:04:59 UTC (rev 14523)
+++ search/trunk/lib/README.txt 2008-04-23 02:31:14 UTC (rev 14524)
@@ -8,6 +8,7 @@
hibernate core dependencies: required (see Hibernate Core for more information)
lucene-core-*.jar: required (used version 2.3.0)
jms.jar: optional (needed for JMS based clustering strategy, usually available with your application server)
+apache-solr-*.jar: optional (used version 1.2.0), needed if @AnalyzerDef is used
Test
====
Added: search/trunk/lib/apache-solr-1.2.0.jar
===================================================================
(Binary files differ)
Property changes on: search/trunk/lib/apache-solr-1.2.0.jar
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Modified: search/trunk/src/java/org/hibernate/search/annotations/Analyzer.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/Analyzer.java 2008-04-22 15:04:59 UTC (rev 14523)
+++ search/trunk/src/java/org/hibernate/search/annotations/Analyzer.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -8,13 +8,16 @@
import java.lang.annotation.Documented;
/**
- * Define an Analizer for a given entity, method, field or Field
+ * Define an Analyzer for a given entity, method, field or Field
* The order of precedence is as such:
* - @Field
* - field / method
* - entity
* - default
*
+ * Either describe an explicit implementation through the <code>impl</code> parameter
+ * or use an external @AnalyzerDef definition through the <code>def</code> parameter
+ *
* @author Emmanuel Bernard
*/
@Retention( RetentionPolicy.RUNTIME )
@@ -23,4 +26,5 @@
public @interface Analyzer {
Class impl() default void.class;
+ String definition() default "";
}
Added: search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDef.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDef.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDef.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,39 @@
+//$
+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;
+
+/**
+ * Reusable analyzer definition.
+ * An analyzer definition defines:
+ * - one tokenizer
+ * - optionally some filters
+ * Filters are applied in the order they are defined
+ *
+ * Reuses the Solr Tokenizer and Filter architecture
+ *
+ * @author Emmanuel Bernard
+ */
+ at Retention( RetentionPolicy.RUNTIME )
+ at Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD} )
+ at Documented
+public @interface AnalyzerDef {
+ /**
+ * Reference name to be used on {#org.hibernate.search.annotations.Analyzer}
+ */
+ String name();
+
+ /**
+ * Tokenizer used
+ */
+ TokenizerDef tokenizer();
+
+ /**
+ * Filters used. The filters are applied in the defined order
+ */
+ TokenFilterDef[] filters() default {};
+}
Added: search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDefs.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDefs.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDefs.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,20 @@
+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;
+
+/**
+ * Reusable analyzer definitions.
+ * This annotation allows multiple definition declarations per element
+ *
+ * @author Emmanuel Bernard
+ */
+ at Retention( RetentionPolicy.RUNTIME )
+ at Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD} )
+ at Documented
+public @interface AnalyzerDefs {
+ AnalyzerDef[] value();
+}
Added: search/trunk/src/java/org/hibernate/search/annotations/TokenFilterDef.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/TokenFilterDef.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/TokenFilterDef.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,30 @@
+//$
+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;
+
+import org.apache.solr.analysis.TokenFilterFactory;
+
+/**
+ * Define a TokenFilterFactory and its parameters
+ *
+ * @author Emmanuel Bernard
+ */
+ at Retention( RetentionPolicy.RUNTIME )
+ at Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD } )
+ at Documented
+public @interface TokenFilterDef {
+ /**
+ * Defines the TokenFilterFactory implementation used
+ */
+ public abstract Class<? extends TokenFilterFactory> factory();
+
+ /**
+ * optional parameters passed to the TokenFilterFactory
+ */
+ public abstract Parameter[] params() default {};
+}
\ No newline at end of file
Added: search/trunk/src/java/org/hibernate/search/annotations/TokenizerDef.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/TokenizerDef.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/TokenizerDef.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,30 @@
+//$
+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;
+
+import org.apache.solr.analysis.TokenizerFactory;
+
+/**
+ * Define a TokenizerFactory and its parameters
+ *
+ * @author Emmanuel Bernard
+ */
+ at Retention( RetentionPolicy.RUNTIME )
+ at Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD} )
+ at Documented
+public @interface TokenizerDef {
+ /**
+ * Defines the TokenizerFactory implementation used
+ */
+ Class<? extends TokenizerFactory> factory();
+
+ /**
+ * optional parameters passed to the TokenizerFactory
+ */
+ Parameter[] params() default {};
+}
Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java 2008-04-22 15:04:59 UTC (rev 14523)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -26,8 +26,10 @@
import org.hibernate.annotations.common.reflection.XMember;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.annotations.common.util.ReflectHelper;
+import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.search.SearchException;
+import org.hibernate.search.impl.InitContext;
import org.hibernate.search.annotations.Boost;
import org.hibernate.search.annotations.ClassBridge;
import org.hibernate.search.annotations.ClassBridges;
@@ -37,6 +39,8 @@
import org.hibernate.search.annotations.IndexedEmbedded;
import org.hibernate.search.annotations.Store;
import org.hibernate.search.annotations.TermVector;
+import org.hibernate.search.annotations.AnalyzerDefs;
+import org.hibernate.search.annotations.AnalyzerDef;
import org.hibernate.search.backend.AddLuceneWork;
import org.hibernate.search.backend.DeleteLuceneWork;
import org.hibernate.search.backend.LuceneWork;
@@ -79,7 +83,7 @@
private Similarity similarity;
- public DocumentBuilder(XClass clazz, Analyzer defaultAnalyzer, Similarity defaultSimilarity, DirectoryProvider[] directoryProviders,
+ public DocumentBuilder(XClass clazz, InitContext context, DirectoryProvider[] directoryProviders,
IndexShardingStrategy shardingStrategy, ReflectionManager reflectionManager) {
this.analyzer = new ScopedAnalyzer();
this.beanClass = clazz;
@@ -87,15 +91,15 @@
this.shardingStrategy = shardingStrategy;
//FIXME get rid of it when boost is stored?
this.reflectionManager = reflectionManager;
- this.similarity = defaultSimilarity;
+ this.similarity = context.getDefaultSimilarity();
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;
+ rootPropertiesMetadata.analyzer = context.getDefaultAnalyzer();
Set<XClass> processedClasses = new HashSet<XClass>();
processedClasses.add( clazz );
- initializeMembers( clazz, rootPropertiesMetadata, true, "", processedClasses );
+ initializeMembers( clazz, rootPropertiesMetadata, true, "", processedClasses, context );
//processedClasses.remove( clazz ); for the sake of completness
this.analyzer.setGlobalAnalyzer( rootPropertiesMetadata.analyzer );
if ( idKeywordName == null ) {
@@ -103,16 +107,23 @@
}
}
- private Analyzer getAnalyzer(XAnnotatedElement annotatedElement) {
+ private Analyzer getAnalyzer(XAnnotatedElement annotatedElement, InitContext context) {
org.hibernate.search.annotations.Analyzer analyzerAnn =
annotatedElement.getAnnotation( org.hibernate.search.annotations.Analyzer.class );
- return getAnalyzer( analyzerAnn );
+ return getAnalyzer( analyzerAnn, context );
}
- private Analyzer getAnalyzer(org.hibernate.search.annotations.Analyzer analyzerAnn) {
+ private Analyzer getAnalyzer(org.hibernate.search.annotations.Analyzer analyzerAnn, InitContext context) {
Class analyzerClass = analyzerAnn == null ? void.class : analyzerAnn.impl();
if ( analyzerClass == void.class ) {
- return null;
+ String definition = analyzerAnn == null ? "" : analyzerAnn.definition();
+ if ( StringHelper.isEmpty( definition ) ) {
+ return null;
+ }
+ else {
+
+ return context.buildLazyAnalyzer( definition );
+ }
}
else {
try {
@@ -130,7 +141,7 @@
}
private void initializeMembers(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix,
- Set<XClass> processedClasses) {
+ Set<XClass> processedClasses, InitContext context) {
List<XClass> hierarchy = new ArrayList<XClass>();
for (XClass currClass = clazz; currClass != null; currClass = currClass.getSuperclass()) {
hierarchy.add( currClass );
@@ -142,23 +153,25 @@
* 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 );
+ Analyzer analyzer = getAnalyzer( currClass, context );
+
if ( analyzer != null ) {
propertiesMetadata.analyzer = analyzer;
}
+ getAnalyzerDefs(currClass, context);
// Check for any ClassBridges annotation.
ClassBridges classBridgesAnn = currClass.getAnnotation( ClassBridges.class );
if ( classBridgesAnn != null ) {
ClassBridge[] cbs = classBridgesAnn.value();
for (ClassBridge cb : cbs) {
- bindClassAnnotation( prefix, propertiesMetadata, cb );
+ bindClassAnnotation( prefix, propertiesMetadata, cb, context );
}
}
// Check for any ClassBridge style of annotations.
ClassBridge classBridgeAnn = currClass.getAnnotation( ClassBridge.class );
if ( classBridgeAnn != null ) {
- bindClassAnnotation( prefix, propertiesMetadata, classBridgeAnn );
+ bindClassAnnotation( prefix, propertiesMetadata, classBridgeAnn, context );
}
//Get similarity
@@ -177,12 +190,12 @@
// so indexing a non property does not make sense
List<XProperty> methods = currClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
for (XProperty method : methods) {
- initializeMember( method, propertiesMetadata, isRoot, prefix, processedClasses );
+ initializeMember( method, propertiesMetadata, isRoot, prefix, processedClasses, context );
}
List<XProperty> fields = currClass.getDeclaredProperties( XClass.ACCESS_FIELD );
for (XProperty field : fields) {
- initializeMember( field, propertiesMetadata, isRoot, prefix, processedClasses );
+ initializeMember( field, propertiesMetadata, isRoot, prefix, processedClasses, context );
}
}
if ( isRoot && similarityClass != null ) {
@@ -196,6 +209,17 @@
}
}
+ private void getAnalyzerDefs(XAnnotatedElement annotatedElement, InitContext context) {
+ AnalyzerDefs defs = annotatedElement.getAnnotation( AnalyzerDefs.class );
+ if ( defs != null ) {
+ for (AnalyzerDef def : defs.value()) {
+ context.addAnalyzerDef( def );
+ }
+ }
+ AnalyzerDef def = annotatedElement.getAnnotation( AnalyzerDef.class );
+ context.addAnalyzerDef( def );
+ }
+
public String getIdentifierName() {
return idGetter.getName();
}
@@ -205,7 +229,7 @@
}
private void initializeMember(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot,
- String prefix, Set<XClass> processedClasses) {
+ String prefix, Set<XClass> processedClasses, InitContext context) {
DocumentId documentIdAnn = member.getAnnotation( DocumentId.class );
if ( documentIdAnn != null ) {
@@ -239,7 +263,7 @@
propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( null, member, reflectionManager ) );
// Field > property > entity analyzer
Analyzer analyzer = null; //no field analyzer
- if ( analyzer == null ) analyzer = getAnalyzer( member );
+ if ( analyzer == null ) analyzer = getAnalyzer( member, context );
if ( analyzer == null ) analyzer = propertiesMetadata.analyzer;
if ( analyzer == null ) throw new AssertionFailure( "Analizer should not be undefined" );
this.analyzer.addScopedAnalyzer( fieldName, analyzer );
@@ -249,7 +273,7 @@
org.hibernate.search.annotations.Field fieldAnn =
member.getAnnotation( org.hibernate.search.annotations.Field.class );
if ( fieldAnn != null ) {
- bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn );
+ bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
}
}
{
@@ -257,10 +281,11 @@
member.getAnnotation( org.hibernate.search.annotations.Fields.class );
if ( fieldsAnn != null ) {
for (org.hibernate.search.annotations.Field fieldAnn : fieldsAnn.value()) {
- bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn );
+ bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
}
}
}
+ getAnalyzerDefs( member, context );
IndexedEmbedded embeddedAnn = member.getAnnotation( IndexedEmbedded.class );
if ( embeddedAnn != null ) {
@@ -297,10 +322,10 @@
propertiesMetadata.embeddedPropertiesMetadata.add( metadata );
metadata.boost = getBoost( member );
//property > entity analyzer
- Analyzer analyzer = getAnalyzer( member );
+ Analyzer analyzer = getAnalyzer( member, context );
metadata.analyzer = analyzer != null ? analyzer : propertiesMetadata.analyzer;
String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
- initializeMembers( elementClass, metadata, false, localPrefix, processedClasses );
+ initializeMembers( elementClass, metadata, false, localPrefix, processedClasses, context );
/**
* We will only index the "expected" type but that's OK, HQL cannot do downcasting either
*/
@@ -338,7 +363,7 @@
}
}
- private void bindClassAnnotation(String prefix, PropertiesMetadata propertiesMetadata, ClassBridge ann) {
+ private void bindClassAnnotation(String prefix, PropertiesMetadata propertiesMetadata, ClassBridge ann, InitContext context) {
//FIXME name should be prefixed
String fieldName = prefix + ann.name();
propertiesMetadata.classNames.add( fieldName );
@@ -348,13 +373,13 @@
propertiesMetadata.classBridges.add( BridgeFactory.extractType( ann ) );
propertiesMetadata.classBoosts.add( ann.boost().value() );
- Analyzer analyzer = getAnalyzer( ann.analyzer() );
+ Analyzer analyzer = getAnalyzer( ann.analyzer(), context );
if ( analyzer == null ) analyzer = propertiesMetadata.analyzer;
if ( analyzer == null ) throw new AssertionFailure( "Analyzer should not be undefined" );
this.analyzer.addScopedAnalyzer( fieldName, analyzer );
}
- private void bindFieldAnnotation(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, org.hibernate.search.annotations.Field fieldAnn) {
+ private void bindFieldAnnotation(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, org.hibernate.search.annotations.Field fieldAnn, InitContext context) {
setAccessible( member );
propertiesMetadata.fieldGetters.add( member );
String fieldName = prefix + BinderHelper.getAttributeName( member, fieldAnn.name() );
@@ -365,8 +390,8 @@
propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( fieldAnn, member, reflectionManager ) );
// Field > property > entity analyzer
- Analyzer analyzer = getAnalyzer( fieldAnn.analyzer() );
- if ( analyzer == null ) analyzer = getAnalyzer( member );
+ Analyzer analyzer = getAnalyzer( fieldAnn.analyzer(), context );
+ if ( analyzer == null ) analyzer = getAnalyzer( member, context );
if ( analyzer == null ) analyzer = propertiesMetadata.analyzer;
if ( analyzer == null ) throw new AssertionFailure( "Analizer should not be undefined" );
this.analyzer.addScopedAnalyzer( fieldName, analyzer );
Added: search/trunk/src/java/org/hibernate/search/impl/InitContext.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/InitContext.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/impl/InitContext.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,193 @@
+//$
+package org.hibernate.search.impl;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.hibernate.search.annotations.AnalyzerDef;
+import org.hibernate.search.annotations.TokenizerDef;
+import org.hibernate.search.annotations.Parameter;
+import org.hibernate.search.annotations.TokenFilterDef;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.Environment;
+import org.hibernate.search.util.DelegateNamedAnalyzer;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.util.ReflectHelper;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.search.Similarity;
+import org.apache.solr.analysis.TokenizerFactory;
+import org.apache.solr.analysis.TokenizerChain;
+import org.apache.solr.analysis.TokenFilterFactory;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class InitContext {
+ private Map<String, AnalyzerDef> analyzerDefs = new HashMap<String, AnalyzerDef>();
+ private List<DelegateNamedAnalyzer> lazyAnalyzers = new ArrayList<DelegateNamedAnalyzer>();
+ private final Analyzer defaultAnalyzer;
+ private final Similarity defaultSimilarity;
+
+ public InitContext(Configuration cfg) {
+ defaultAnalyzer = initAnalyzer(cfg);
+ defaultSimilarity = initSimilarity(cfg);
+ }
+
+ public void addAnalyzerDef(AnalyzerDef ann) {
+ //FIXME somehow remember where the analyzerDef comes from and raise an exception if an analyzerDef
+ //with the same name from two different places are added
+ //multiple adding from the same place is required to deal with inheritance hierarchy processed multiple times
+ if ( ann != null && analyzerDefs.put( ann.name(), ann ) != null ) {
+ //throw new SearchException("Multiple AnalyzerDef with the same name: " + name);
+ }
+ }
+
+ public Analyzer buildLazyAnalyzer(String name) {
+ final DelegateNamedAnalyzer delegateNamedAnalyzer = new DelegateNamedAnalyzer( name );
+ lazyAnalyzers.add(delegateNamedAnalyzer);
+ return delegateNamedAnalyzer;
+ }
+
+ public List<DelegateNamedAnalyzer> getLazyAnalyzers() {
+ return lazyAnalyzers;
+ }
+
+ /**
+ * Initilises the Lucene analyzer to use by reading the analyzer class from the configuration and instantiating it.
+ *
+ * @param cfg
+ * The current configuration.
+ * @return The Lucene analyzer to use for tokenisation.
+ */
+ private Analyzer initAnalyzer(Configuration cfg) {
+ Class analyzerClass;
+ String analyzerClassName = cfg.getProperty( Environment.ANALYZER_CLASS);
+ if (analyzerClassName != null) {
+ try {
+ analyzerClass = ReflectHelper.classForName(analyzerClassName);
+ } catch (Exception e) {
+ return buildLazyAnalyzer( analyzerClassName );
+// throw new SearchException("Lucene analyzer class '" + analyzerClassName + "' defined in property '"
+// + Environment.ANALYZER_CLASS + "' could not be found.", e);
+ }
+ } else {
+ analyzerClass = StandardAnalyzer.class;
+ }
+ // Initialize analyzer
+ Analyzer defaultAnalyzer;
+ try {
+ defaultAnalyzer = (Analyzer) analyzerClass.newInstance();
+ } catch (ClassCastException e) {
+ throw new SearchException("Lucene analyzer does not implement " + Analyzer.class.getName() + ": "
+ + analyzerClassName, e);
+ } catch (Exception e) {
+ throw new SearchException("Failed to instantiate lucene analyzer with type " + analyzerClassName, e);
+ }
+ return defaultAnalyzer;
+ }
+
+ /**
+ * Initilises the Lucene similarity to use
+ */
+ private Similarity initSimilarity(Configuration cfg) {
+ Class similarityClass;
+ String similarityClassName = cfg.getProperty(Environment.SIMILARITY_CLASS);
+ if (similarityClassName != null) {
+ try {
+ similarityClass = ReflectHelper.classForName(similarityClassName);
+ } catch (Exception e) {
+ throw new SearchException("Lucene Similarity class '" + similarityClassName + "' defined in property '"
+ + Environment.SIMILARITY_CLASS + "' could not be found.", e);
+ }
+ }
+ else {
+ similarityClass = null;
+ }
+
+ // Initialize similarity
+ if ( similarityClass == null ) {
+ return Similarity.getDefault();
+ }
+ else {
+ Similarity defaultSimilarity;
+ try {
+ defaultSimilarity = (Similarity) similarityClass.newInstance();
+ } catch (ClassCastException e) {
+ throw new SearchException("Lucene similarity does not extend " + Similarity.class.getName() + ": "
+ + similarityClassName, e);
+ } catch (Exception e) {
+ throw new SearchException("Failed to instantiate lucene similarity with type " + similarityClassName, e);
+ }
+ return defaultSimilarity;
+ }
+ }
+
+ public Analyzer getDefaultAnalyzer() {
+ return defaultAnalyzer;
+ }
+
+ public Similarity getDefaultSimilarity() {
+ return defaultSimilarity;
+ }
+
+ public void initLazyAnalyzers() {
+ Map<String, Analyzer> initializedAnalizers = new HashMap<String, Analyzer>( analyzerDefs.size() );
+
+ for (DelegateNamedAnalyzer namedAnalyzer : lazyAnalyzers) {
+ String name = namedAnalyzer.getName();
+ if ( initializedAnalizers.containsKey( name ) ) {
+ namedAnalyzer.setDelegate( initializedAnalizers.get( name ) );
+ }
+ else {
+ if ( analyzerDefs.containsKey( name ) ) {
+ final Analyzer analyzer = buildAnalyzer( analyzerDefs.get( name ) );
+ namedAnalyzer.setDelegate( analyzer );
+ initializedAnalizers.put( name, analyzer );
+ }
+ else {
+ //exception
+ throw new SearchException("Analyzer found with an unknown definition: " + name);
+ }
+ }
+ }
+ }
+
+ private Analyzer buildAnalyzer(AnalyzerDef analyzerDef) {
+ TokenizerDef token = analyzerDef.tokenizer();
+ TokenizerFactory tokenFactory = (TokenizerFactory) instantiate( token.factory() );
+ tokenFactory.init( getMapOfParameters( token.params() ) );
+
+ final int length = analyzerDef.filters().length;
+ TokenFilterFactory[] filters = new TokenFilterFactory[length];
+ for ( int index = 0 ; index < length ; index++ ) {
+ TokenFilterDef filterDef = analyzerDef.filters()[index];
+ filters[index] = (TokenFilterFactory) instantiate( filterDef.factory() );
+ filters[index].init( getMapOfParameters( filterDef.params() ) );
+ }
+ return new TokenizerChain(tokenFactory, filters);
+ }
+
+ private Object instantiate(Class clazz) {
+ try {
+ return clazz.newInstance();
+ }
+ catch (IllegalAccessException e) {
+ throw new SearchException( "Unable to instantiate class: " + clazz, e );
+ }
+ catch (InstantiationException e) {
+ throw new SearchException( "Unable to instantiate class: " + clazz, e );
+ }
+ }
+
+ private Map<String, String> getMapOfParameters(Parameter[] params) {
+ Map<String, String> mapOfParams = new HashMap<String, String>( params.length );
+ for (Parameter param : params) {
+ mapOfParams.put( param.name(), param.value() );
+ }
+ return Collections.unmodifiableMap( mapOfParams );
+ }
+}
Modified: search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-04-22 15:04:59 UTC (rev 14523)
+++ search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -13,9 +13,6 @@
import java.util.WeakHashMap;
import java.util.concurrent.locks.ReentrantLock;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.search.Similarity;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
@@ -46,7 +43,6 @@
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.store.DirectoryProviderFactory;
import org.hibernate.search.store.optimization.OptimizerStrategy;
-import org.hibernate.util.ReflectHelper;
/**
* @author Emmanuel Bernard
@@ -90,11 +86,8 @@
public SearchFactoryImpl(Configuration cfg) {
//yuk
ReflectionManager reflectionManager = getReflectionManager( cfg );
- //InitContext context = new InitContext();
- //Analyzer analyzer = initAnalyzer(cfg, context);
- Analyzer analyzer = initAnalyzer(cfg);
- Similarity similarity = initSimilarity(cfg);
- initDocumentBuilders(cfg, reflectionManager, analyzer, similarity);
+ InitContext context = new InitContext(cfg);
+ initDocumentBuilders(cfg, reflectionManager, context );
Set<Class> indexedClasses = documentBuilders.keySet();
for (DocumentBuilder builder : documentBuilders.values()) {
@@ -250,13 +243,10 @@
getBackendQueueProcessorFactory().getProcessor( queue ).run();
}
- private void initDocumentBuilders(Configuration cfg, ReflectionManager reflectionManager, Analyzer analyzer, Similarity similarity) {
+ private void initDocumentBuilders(Configuration cfg, ReflectionManager reflectionManager, InitContext context) {
Iterator iter = cfg.getClassMappings();
DirectoryProviderFactory factory = new DirectoryProviderFactory();
- //Map<String, AnalyzerDef> analyzerDefs = new HashMap<String, AnalyzerDef>();
-
- //cfg.getClass().getAnnotation( AnalyzerDef.class );
- //TODO SOlr.......
+
while (iter.hasNext()) {
PersistentClass clazz = (PersistentClass) iter.next();
Class<?> mappedClass = clazz.getMappedClass();
@@ -267,88 +257,21 @@
DirectoryProviderFactory.DirectoryProviders providers = factory.createDirectoryProviders( mappedXClass, cfg, this );
final DocumentBuilder<Object> documentBuilder = new DocumentBuilder<Object>(
- mappedXClass, analyzer, similarity, providers.getProviders(), providers.getSelectionStrategy(),
+ mappedXClass, context, providers.getProviders(), providers.getSelectionStrategy(),
reflectionManager
);
documentBuilders.put( mappedClass, documentBuilder );
}
bindFilterDefs(mappedXClass);
+ //TODO should analyzer def for classes at tyher sqme level???
}
}
}
+ context.initLazyAnalyzers();
factory.startDirectoryProviders();
}
- /**
- * Initilises the Lucene analyzer to use by reading the analyzer class from the configuration and instantiating it.
- *
- * @param cfg
- * The current configuration.
- * @return The Lucene analyzer to use for tokenisation.
- */
- private Analyzer initAnalyzer(Configuration cfg) {
- Class analyzerClass;
- String analyzerClassName = cfg.getProperty(Environment.ANALYZER_CLASS);
- if (analyzerClassName != null) {
- try {
- analyzerClass = ReflectHelper.classForName(analyzerClassName);
- } catch (Exception e) {
- throw new SearchException("Lucene analyzer class '" + analyzerClassName + "' defined in property '"
- + Environment.ANALYZER_CLASS + "' could not be found.", e);
- }
- } else {
- analyzerClass = StandardAnalyzer.class;
- }
- // Initialize analyzer
- Analyzer defaultAnalyzer;
- try {
- defaultAnalyzer = (Analyzer) analyzerClass.newInstance();
- } catch (ClassCastException e) {
- throw new SearchException("Lucene analyzer does not implement " + Analyzer.class.getName() + ": "
- + analyzerClassName, e);
- } catch (Exception e) {
- throw new SearchException("Failed to instantiate lucene analyzer with type " + analyzerClassName, e);
- }
- return defaultAnalyzer;
- }
-
- /**
- * Initilises the Lucene similarity to use
- */
- private Similarity initSimilarity(Configuration cfg) {
- Class similarityClass;
- String similarityClassName = cfg.getProperty(Environment.SIMILARITY_CLASS);
- if (similarityClassName != null) {
- try {
- similarityClass = ReflectHelper.classForName(similarityClassName);
- } catch (Exception e) {
- throw new SearchException("Lucene Similarity class '" + similarityClassName + "' defined in property '"
- + Environment.SIMILARITY_CLASS + "' could not be found.", e);
- }
- }
- else {
- similarityClass = null;
- }
-
- // Initialize similarity
- if ( similarityClass == null ) {
- return Similarity.getDefault();
- }
- else {
- Similarity defaultSimilarity;
- try {
- defaultSimilarity = (Similarity) similarityClass.newInstance();
- } catch (ClassCastException e) {
- throw new SearchException("Lucene similarity does not extend " + Similarity.class.getName() + ": "
- + similarityClassName, e);
- } catch (Exception e) {
- throw new SearchException("Failed to instantiate lucene similarity with type " + similarityClassName, e);
- }
- return defaultSimilarity;
- }
- }
-
private void buildFilterCachingStrategy(Properties properties) {
String impl = properties.getProperty( Environment.FILTER_CACHING_STRATEGY );
if ( StringHelper.isEmpty( impl ) || "mru".equalsIgnoreCase( impl ) ) {
@@ -363,10 +286,10 @@
throw new SearchException( "Unable to find filterCachingStrategy class: " + impl, e );
}
catch (IllegalAccessException e) {
- throw new SearchException( "Unable to instanciate filterCachingStrategy class: " + impl, e );
+ throw new SearchException( "Unable to instantiate filterCachingStrategy class: " + impl, e );
}
catch (InstantiationException e) {
- throw new SearchException( "Unable to instanciate filterCachingStrategy class: " + impl, e );
+ throw new SearchException( "Unable to instantiate filterCachingStrategy class: " + impl, e );
}
}
filterCachingStrategy.initialize( properties );
Added: search/trunk/src/java/org/hibernate/search/util/DelegateNamedAnalyzer.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/util/DelegateNamedAnalyzer.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/util/DelegateNamedAnalyzer.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,46 @@
+//$
+package org.hibernate.search.util;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.TokenStream;
+
+/**
+ * delegate to a named analyzer
+ * delegated Analyzers are lazily configured
+ *
+ * @author Emmanuel Bernard
+ */
+public class DelegateNamedAnalyzer extends Analyzer {
+ private String name;
+ private Analyzer delegate;
+
+ public DelegateNamedAnalyzer(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setDelegate(Analyzer delegate) {
+ this.delegate = delegate;
+ this.name = null; //unique init
+ }
+
+ public TokenStream tokenStream(String fieldName, Reader reader) {
+ return delegate.tokenStream( fieldName, reader );
+ }
+
+ @Override
+ public TokenStream reusableTokenStream(String fieldName, Reader reader) throws IOException {
+ return delegate.reusableTokenStream( fieldName, reader );
+ }
+
+ @Override
+ public int getPositionIncrementGap(String fieldName) {
+ return delegate.getPositionIncrementGap( fieldName );
+ }
+}
Added: search/trunk/src/test/org/hibernate/search/test/analyzer/solr/SolrAnalyzerTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/analyzer/solr/SolrAnalyzerTest.java (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/analyzer/solr/SolrAnalyzerTest.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,41 @@
+package org.hibernate.search.test.analyzer.solr;
+
+import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.Transaction;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.index.Term;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class SolrAnalyzerTest extends SearchTestCase {
+ public void testAnalyzerDef() throws Exception {
+ Team team = new Team();
+ team.setDescription( "This is a Dscription" );
+ team.setLocation( "Atlanta" );
+ team.setName( "ATL team" );
+ FullTextSession fts = Search.createFullTextSession( openSession() );
+ Transaction tx = fts.beginTransaction();
+ fts.persist( team );
+ tx.commit();
+ fts.clear();
+ tx = fts.beginTransaction();
+ TermQuery query = new TermQuery( new Term("description", "Dscription") );
+ assertEquals( "iso latin filter should work", 0, fts.createFullTextQuery( query ).list().size() );
+ query = new TermQuery( new Term("description", "is") );
+ assertEquals( "stop word filter should work", 0, fts.createFullTextQuery( query ).list().size() );
+ query = new TermQuery( new Term("description", "dascription") );
+ assertEquals( 1, fts.createFullTextQuery( query ).list().size() );
+ fts.delete( fts.createFullTextQuery( query ).list().get( 0 ) );
+ tx.commit();
+ fts.close();
+ }
+
+ protected Class[] getMappings() {
+ return new Class[] {
+ Team.class
+ };
+ }
+}
Added: search/trunk/src/test/org/hibernate/search/test/analyzer/solr/Team.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/analyzer/solr/Team.java (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/analyzer/solr/Team.java 2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,81 @@
+package org.hibernate.search.test.analyzer.solr;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.AnalyzerDef;
+import org.hibernate.search.annotations.TokenizerDef;
+import org.hibernate.search.annotations.TokenFilterDef;
+import org.hibernate.search.annotations.Parameter;
+import org.hibernate.search.annotations.Analyzer;
+import org.apache.solr.analysis.StandardTokenizerFactory;
+import org.apache.solr.analysis.ISOLatin1AccentFilterFactory;
+import org.apache.solr.analysis.LowerCaseFilterFactory;
+import org.apache.solr.analysis.StopFilterFactory;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+ at Indexed
+ at AnalyzerDef(name="customanalyzer",
+ tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
+ filters = {
+ @TokenFilterDef(factory = ISOLatin1AccentFilterFactory.class),
+ @TokenFilterDef(factory = LowerCaseFilterFactory.class),
+ @TokenFilterDef(factory = StopFilterFactory.class, params = {
+ @Parameter(name="words", value= "org/hibernate/search/test/analyzer/solr/stoplist.properties" ),
+ @Parameter(name="ignoreCase", value="true")
+ })
+})
+public class Team {
+ @Id
+ @DocumentId
+ @GeneratedValue
+ private Integer id;
+
+ @Field
+ private String name;
+
+ @Field
+ private String location;
+
+ @Field @Analyzer(definition = "customanalyzer")
+ private String description;
+
+ 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;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+}
Added: search/trunk/src/test/org/hibernate/search/test/analyzer/solr/stoplist.properties
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/analyzer/solr/stoplist.properties (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/analyzer/solr/stoplist.properties 2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,33 @@
+a
+an
+and
+are
+as
+at
+be
+but
+by
+for
+if
+in
+into
+is
+it
+no
+not
+of
+on
+or
+such
+that
+the
+their
+then
+there
+these
+they
+this
+to
+was
+will
+with
\ No newline at end of file
More information about the hibernate-commits
mailing list