Author: epbernard
Date: 2007-07-24 22:58:26 -0400 (Tue, 24 Jul 2007)
New Revision: 12813
Added:
trunk/HibernateExt/search/src/java/org/hibernate/search/FullTextFilter.java
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Factory.java
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/FullTextFilterDef.java
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/FullTextFilterDefs.java
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Key.java
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/FilterDef.java
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/ChainedFilter.java
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/FilterCachingStrategy.java
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/FilterKey.java
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/MRUFilterCachingStrategy.java
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/StandardFilterKey.java
trunk/HibernateExt/search/src/java/org/hibernate/search/query/FullTextFilterImpl.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/BestDriversFilter.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/Driver.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/FilterTest.java
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/SecurityFilterFactory.java
Modified:
trunk/HibernateExt/search/src/java/org/hibernate/search/Environment.java
trunk/HibernateExt/search/src/java/org/hibernate/search/FullTextQuery.java
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java
trunk/HibernateExt/search/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
trunk/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java
Log:
HSEARCH-15 provide ftQuery.setFilter(Filter) (Hardy Ferentschik)
HSEARCH-58 Support named Filters (and caching)
Modified: trunk/HibernateExt/search/src/java/org/hibernate/search/Environment.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/Environment.java 2007-07-24
20:35:47 UTC (rev 12812)
+++ trunk/HibernateExt/search/src/java/org/hibernate/search/Environment.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -56,4 +56,12 @@
* define the reader strategy used
*/
public static final String READER_STRATEGY = READER_PREFIX + "strategy";
+ /**
+ * filter caching strategy
+ */
+ public static final String FILTER_CACHING_STRATEGY_PREFIX =
"hibernate.search.filter.cache_strategy.";
+ /**
+ * filter caching strategy class (must have a no-arg constructor and implements
FilterCachingStrateg
+ */
+ public static final String FILTER_CACHING_STRATEGY_IMPL =
"hibernate.search.filter.cache_strategy.impl";
}
Added: trunk/HibernateExt/search/src/java/org/hibernate/search/FullTextFilter.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/FullTextFilter.java
(rev 0)
+++ trunk/HibernateExt/search/src/java/org/hibernate/search/FullTextFilter.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,13 @@
+//$Id$
+package org.hibernate.search;
+
+/**
+ * represents a FullTextFilter that is about to be applied
+ * Used to inject parameters
+ *
+ * @author Emmanuel Bernard
+ */
+public interface FullTextFilter {
+ FullTextFilter setParameter(String name, Object value);
+ Object getParameter(String name);
+}
Modified: trunk/HibernateExt/search/src/java/org/hibernate/search/FullTextQuery.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/FullTextQuery.java 2007-07-24
20:35:47 UTC (rev 12812)
+++ trunk/HibernateExt/search/src/java/org/hibernate/search/FullTextQuery.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -1,9 +1,10 @@
//$Id$
package org.hibernate.search;
+import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Sort;
+import org.hibernate.Criteria;
import org.hibernate.Query;
-import org.hibernate.Criteria;
/**
* The base interface for lucene powered searches.
@@ -24,6 +25,15 @@
* @return this for method chaining
*/
FullTextQuery setSort(Sort sort);
+
+ /**
+ * Allows to use lucene filters.
+ * Semi-deprecated? a preferred way is to use the @FullTextFilterDef approach
+ *
+ * @param filter The lucene filter.
+ * @return this for method chaining
+ */
+ FullTextQuery setFilter(Filter filter);
/**
* Returns the number of hits for this search
@@ -57,4 +67,15 @@
*
*/
FullTextQuery setIndexProjection(String... fields);
+
+ /**
+ * Enable a given filter by its name. Returns a FullTextFilter object that allows filter
parameter injection
+ */
+ FullTextFilter enableFullTextFilter(String name);
+
+ /**
+ * Disable a given filter by its name
+ */
+ void disableFullTextFilter(String name);
+
}
Added: trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Factory.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Factory.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Factory.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,24 @@
+//$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;
+
+/**
+ * Marks a method as a factory method for a given type.
+ * A factory method is called whenever a new instance of a given
+ * type is requested.
+ * The factory method is used with a higher priority than a plain no-arg constructor when
present
+ *
+ * @Factory currently works for @FullTextFilterDef.impl classes
+ *
+ * @author Emmanuel Bernard
+ */
+@Retention( RetentionPolicy.RUNTIME )
+@Target( ElementType.METHOD )
+@Documented
+public @interface Factory {
+}
Added:
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/FullTextFilterDef.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/FullTextFilterDef.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/FullTextFilterDef.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,35 @@
+//$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;
+
+/**
+ * Defines a FullTextFilter that can be optionally applied to
+ * every FullText Queries
+ * While not related to a specific indexed entity, the annotation has to be set on one of
them
+ *
+ * @author Emmanuel Bernard
+ */
+@Retention( RetentionPolicy.RUNTIME )
+@Target( { ElementType.TYPE } )
+@Documented
+public @interface FullTextFilterDef {
+ /**
+ * Filter name. Must be unique accross all mappings for a given persistence unit
+ */
+ String name();
+
+ /**
+ * Either implements org.apache.lucene.search.Filter
+ * or contains a @Factory method returning one.
+ * The Filter generated must be thread-safe
+ *
+ * If the filter accept parameters, an @Key method must be present as well
+ *
+ */
+ Class impl();
+}
Added:
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/FullTextFilterDefs.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/FullTextFilterDefs.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/FullTextFilterDefs.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,20 @@
+//$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;
+
+/**
+ * A list of FullTextFilterDef
+ *
+ * @author Emmanuel Bernard
+ */
+@Retention( RetentionPolicy.RUNTIME )
+@Target( { ElementType.TYPE } )
+@Documented
+public @interface FullTextFilterDefs {
+ FullTextFilterDef[] value();
+}
Added: trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Key.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Key.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/annotations/Key.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,25 @@
+//$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;
+
+/**
+ * Marks a method as a key constructor for a given type.
+ * A key is an object that uniquely identify a given object type and a given set of
parameters
+ *
+ * The key object must implement equals / hashcode so that 2 keys are equals iif
+ * the given target object types are the same, the set of parameters are the same.
+ *
+ * @Factory currently works for @FullTextFilterDef.impl classes
+ *
+ * @author Emmanuel Bernard
+ */
+@Retention( RetentionPolicy.RUNTIME )
+@Target( ElementType.METHOD )
+@Documented
+public @interface Key {
+}
Added: trunk/HibernateExt/search/src/java/org/hibernate/search/engine/FilterDef.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/engine/FilterDef.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/FilterDef.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,63 @@
+//$Id$
+package org.hibernate.search.engine;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.search.SearchException;
+
+/**
+ * @author Emmanuel Bernard
+ */
+//TODO serialization
+public class FilterDef {
+ private Class impl;
+ private Method factoryMethod;
+ private Method keyMethod;
+ private Map<String, Method> setters = new HashMap<String, Method>();
+
+ public Class getImpl() {
+ return impl;
+ }
+
+ public void setImpl(Class impl) {
+ this.impl = impl;
+ }
+
+ public Method getFactoryMethod() {
+ return factoryMethod;
+ }
+
+ public void setFactoryMethod(Method factoryMethod) {
+ this.factoryMethod = factoryMethod;
+ }
+
+ public Method getKeyMethod() {
+ return keyMethod;
+ }
+
+ public void setKeyMethod(Method keyMethod) {
+ this.keyMethod = keyMethod;
+ }
+
+ public void addSetter(String name, Method method) {
+ if ( method.isAccessible() ) method.setAccessible( true );
+ setters.put( name, method );
+ }
+
+ public void invoke(String parameterName, Object filter, Object parameterValue) {
+ Method method = setters.get( parameterName );
+ if ( method == null ) throw new SearchException( "No setter " + parameterName
+ " found in " + this.impl );
+ try {
+ method.invoke( filter, parameterValue );
+ }
+ catch (IllegalAccessException e) {
+ throw new SearchException( "Unable to set Filter parameter: " +
parameterName + " on filter class: " + this.impl, e );
+ }
+ catch (InvocationTargetException e) {
+ throw new SearchException( "Unable to set Filter parameter: " +
parameterName + " on filter class: " + this.impl, e );
+ }
+ }
+}
Modified:
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java 2007-07-24
20:35:47 UTC (rev 12812)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -5,6 +5,7 @@
import java.util.concurrent.locks.ReentrantLock;
import org.hibernate.search.SearchFactory;
+import org.hibernate.search.filter.FilterCachingStrategy;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.store.optimization.OptimizerStrategy;
import org.hibernate.search.backend.BackendQueueProcessorFactory;
@@ -30,7 +31,11 @@
void addOptimizerStrategy(DirectoryProvider<?> provider, OptimizerStrategy
optimizerStrategy);
- public OptimizerStrategy getOptimizerStrategy(DirectoryProvider<?> provider);
+ OptimizerStrategy getOptimizerStrategy(DirectoryProvider<?> provider);
+
+ FilterCachingStrategy getFilterCachingStrategy();
+
+ FilterDef getFilterDefinition(String name);
public LuceneIndexingParameters getIndexingParameters(DirectoryProvider<?>
provider );
Added: trunk/HibernateExt/search/src/java/org/hibernate/search/filter/ChainedFilter.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/filter/ChainedFilter.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/ChainedFilter.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,43 @@
+//$Id$
+package org.hibernate.search.filter;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+import java.io.IOException;
+
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.index.IndexReader;
+import org.hibernate.annotations.common.AssertionFailure;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ChainedFilter extends Filter {
+ private List<Filter> chainedFilters = new ArrayList<Filter>();
+
+
+ public void addFilter(Filter filter) {
+ this.chainedFilters.add( filter );
+ }
+
+ public BitSet bits(IndexReader reader) throws IOException {
+ if (chainedFilters.size() == 0) throw new AssertionFailure("Chainedfilter has no
filters to chain for");
+ //we need to copy the first BitSet because BitSet is modified by .logicalOp
+ Filter filter = chainedFilters.get( 0 );
+ BitSet result = (BitSet) filter.bits( reader ).clone();
+ for (int index = 1 ; index < chainedFilters.size() ; index++) {
+ result.and( chainedFilters.get( index ).bits( reader ) );
+ }
+ return result;
+ }
+
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder("ChainedFilter [");
+ for (Filter filter : chainedFilters) {
+ sb.append( "\n ").append( filter.toString() );
+ }
+ return sb.append("\n]" ).toString();
+ }
+}
Added:
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/FilterCachingStrategy.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/FilterCachingStrategy.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/FilterCachingStrategy.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,28 @@
+//$Id$
+package org.hibernate.search.filter;
+
+import java.util.Properties;
+
+import org.apache.lucene.search.Filter;
+
+/**
+ * Defines the caching filter strategy
+ *
+ * @author Emmanuel Bernard
+ */
+public interface FilterCachingStrategy {
+ /**
+ * initialize the strategy from the properties
+ * The Properties must not be changed
+ */
+ void initialize(Properties properties);
+ /**
+ * Retrieve the cached filter for a given key or null if not cached
+ */
+ Filter getCachedFilter(FilterKey key);
+
+ /**
+ * Propose a candidate filter for caching
+ */
+ void addCachedFilter(FilterKey key, Filter filter);
+}
Added: trunk/HibernateExt/search/src/java/org/hibernate/search/filter/FilterKey.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/filter/FilterKey.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/FilterKey.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,31 @@
+//$Id$
+package org.hibernate.search.filter;
+
+/**
+ * The key object must implement equals / hashcode so that 2 keys are equals if and only
if
+ * the given Filter types are the same and the set of parameters are the same.
+ *
+ * The FilterKey creator (ie the @Key method) does not have to inject
<code>impl</code>
+ * It will be done by Hibernate Search
+ *
+ * @author Emmanuel Bernard
+ */
+public abstract class FilterKey {
+
+ private Class impl;
+
+ /**
+ * Represent the @FullTextFilterDef.impl class
+ */
+ public Class getImpl() {
+ return impl;
+ }
+
+ public void setImpl(Class impl) {
+ this.impl = impl;
+ }
+
+ public abstract int hashCode();
+
+ public abstract boolean equals(Object obj);
+}
Added:
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/MRUFilterCachingStrategy.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/MRUFilterCachingStrategy.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/MRUFilterCachingStrategy.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,46 @@
+//$Id$
+package org.hibernate.search.filter;
+
+import java.util.Properties;
+
+import org.apache.lucene.search.Filter;
+import org.hibernate.search.Environment;
+import org.hibernate.search.SearchException;
+import org.hibernate.util.SoftLimitMRUCache;
+
+/**
+ * Keep the most recently used Filters in the cache
+ * The cache is at least as big as
<code>hibernate.search.filter.cache_strategy.size</code>
+ * Above this limit, Filters are kept as soft references
+ *
+ * @author Emmanuel Bernard
+ */
+public class MRUFilterCachingStrategy implements FilterCachingStrategy {
+ private static final String DEFAULT_SIZE = "128";
+ private SoftLimitMRUCache cache;
+
+ public void initialize(Properties properties) {
+ int size;
+ try {
+ size = Integer.parseInt(
+ properties.getProperty( Environment.FILTER_CACHING_STRATEGY_PREFIX +
"size", DEFAULT_SIZE )
+ );
+ }
+ catch (NumberFormatException nfe) {
+ throw new SearchException(
+ "Unable to parse " + Environment.FILTER_CACHING_STRATEGY_PREFIX +
"size: "
+ + properties.getProperty( Environment.FILTER_CACHING_STRATEGY_PREFIX +
"size", DEFAULT_SIZE ), nfe
+ );
+ }
+
+ cache = new SoftLimitMRUCache( size );
+ }
+
+ public Filter getCachedFilter(FilterKey key) {
+ return (Filter) cache.get( key );
+ }
+
+ public void addCachedFilter(FilterKey key, Filter filter) {
+ cache.put( key, filter );
+ }
+}
Added:
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/StandardFilterKey.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/filter/StandardFilterKey.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/filter/StandardFilterKey.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,59 @@
+//$Id$
+package org.hibernate.search.filter;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Implements a filter key usign all injected parameters to compute
+ * equals and hashCode
+ * the order the parameters are added is significant
+ *
+ * @author Emmanuel Bernard
+ */
+public class StandardFilterKey extends FilterKey {
+ private List parameters = new ArrayList();
+ private boolean implSet;
+
+
+ public void setImpl(Class impl) {
+ super.setImpl( impl );
+ //add impl once and only once
+ if (implSet) {
+ parameters.set( 0, impl );
+ }
+ else {
+ implSet = true;
+ parameters.add( 0, impl );
+ }
+ }
+
+ public void addParameter(Object value) {
+ parameters.add( value );
+ }
+ public int hashCode() {
+ int hash = 23;
+ for (Object param : parameters) {
+ hash = 31*hash + (param != null ? param.hashCode() : 0);
+ }
+ return hash;
+ }
+
+ public boolean equals(Object obj) {
+ if ( ! ( obj instanceof StandardFilterKey ) ) return false;
+ StandardFilterKey that = (StandardFilterKey) obj;
+ int size = parameters.size();
+ if ( size != that.parameters.size() ) return false;
+ for (int index = 0 ; index < size; index++) {
+ Object paramThis = parameters.get( index );
+ Object paramThat = that.parameters.get( index );
+ if (paramThis == null && paramThat != null) return false;
+ if (paramThis != null && ! paramThis.equals( paramThat ) ) return false;
+ }
+ return true;
+ }
+}
Modified:
trunk/HibernateExt/search/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2007-07-24
20:35:47 UTC (rev 12812)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -1,26 +1,36 @@
//$Id$
package org.hibernate.search.impl;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Properties;
import java.util.concurrent.locks.ReentrantLock;
+import java.lang.reflect.Method;
+import java.beans.Introspector;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
+import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.search.Environment;
import org.hibernate.search.SearchException;
import org.hibernate.search.Version;
+import org.hibernate.search.filter.FilterCachingStrategy;
+import org.hibernate.search.filter.MRUFilterCachingStrategy;
import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.FullTextFilterDef;
+import org.hibernate.search.annotations.Factory;
+import org.hibernate.search.annotations.Key;
+import org.hibernate.search.annotations.FullTextFilterDefs;
import org.hibernate.search.backend.BackendQueueProcessorFactory;
import org.hibernate.search.backend.LuceneIndexingParameters;
import org.hibernate.search.backend.LuceneWork;
@@ -29,6 +39,7 @@
import org.hibernate.search.backend.WorkerFactory;
import org.hibernate.search.engine.DocumentBuilder;
import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.hibernate.search.engine.FilterDef;
import org.hibernate.search.reader.ReaderProvider;
import org.hibernate.search.reader.ReaderProviderFactory;
import org.hibernate.search.store.DirectoryProvider;
@@ -37,7 +48,6 @@
import org.hibernate.util.ReflectHelper;
/**
- *
* @author Emmanuel Bernard
*/
public class SearchFactoryImpl implements SearchFactoryImplementor {
@@ -47,7 +57,7 @@
static {
Version.touch();
}
-
+
private Map<Class, DocumentBuilder<Object>> documentBuilders = new
HashMap<Class, DocumentBuilder<Object>>();
//keep track of the index modifiers per DirectoryProvider since multiple entity can use
the same directory provider
private Map<DirectoryProvider, ReentrantLock> lockableDirectoryProviders =
@@ -57,8 +67,9 @@
private Worker worker;
private ReaderProvider readerProvider;
private BackendQueueProcessorFactory backendQueueProcessorFactory;
-
-
+ private Map<String, FilterDef> filterDefinitions;
+ private FilterCachingStrategy filterCachingStrategy;
+
/**
* Each directory provider (index) can have its own performance settings.
*/
@@ -81,15 +92,72 @@
Analyzer analyzer = initAnalyzer(cfg);
initDocumentBuilders(cfg, reflectionManager, analyzer);
-
+
Set<Class> indexedClasses = documentBuilders.keySet();
- for (DocumentBuilder builder : documentBuilders.values()) {
+ for (DocumentBuilder builder : documentBuilders.values()) {
builder.postInitialize( indexedClasses );
}
worker = WorkerFactory.createWorker( cfg, this );
readerProvider = ReaderProviderFactory.createReaderProvider( cfg, this );
+ buildFilterCachingStrategy( cfg.getProperties() );
}
+ private void bindFilterDefs(XClass mappedXClass) {
+ filterDefinitions = new HashMap<String, FilterDef>();
+ FullTextFilterDef defAnn = mappedXClass.getAnnotation( FullTextFilterDef.class );
+ if ( defAnn != null ) {
+ bindFilterDef( defAnn, mappedXClass );
+ }
+ FullTextFilterDefs defsAnn = mappedXClass.getAnnotation( FullTextFilterDefs.class );
+ if (defsAnn != null) {
+ for ( FullTextFilterDef def : defsAnn.value() ) {
+ bindFilterDef( def, mappedXClass );
+ }
+ }
+ }
+
+ private void bindFilterDef(FullTextFilterDef defAnn, XClass mappedXClass) {
+ if ( filterDefinitions.containsKey( defAnn.name() ) ) {
+ throw new SearchException("Multiple definition of @FullTextFilterDef.name="
+ defAnn.name() + ": "
+ + mappedXClass.getName() );
+ }
+ FilterDef filterDef = new FilterDef();
+ filterDef.setImpl( defAnn.impl() );
+ try {
+ filterDef.getImpl().newInstance();
+ }
+ catch (IllegalAccessException e) {
+ throw new SearchException("Unable to create Filter class: " +
filterDef.getImpl().getName(), e);
+ }
+ catch (InstantiationException e) {
+ throw new SearchException("Unable to create Filter class: " +
filterDef.getImpl().getName(), e);
+ }
+ for ( Method method : filterDef.getImpl().getMethods() ) {
+ if ( method.isAnnotationPresent( Factory.class ) ) {
+ if ( filterDef.getFactoryMethod() != null ) {
+ throw new SearchException("Multiple @Factory methods found" +
defAnn.name() + ": "
+ + filterDef.getImpl().getName() + "." + method.getName() );
+ }
+ if ( !method.isAccessible() ) method.setAccessible( true );
+ filterDef.setFactoryMethod( method );
+ }
+ if ( method.isAnnotationPresent( Key.class ) ) {
+ if ( filterDef.getKeyMethod() != null ) {
+ throw new SearchException("Multiple @Key methods found" + defAnn.name() +
": "
+ + filterDef.getImpl().getName() + "." + method.getName() );
+ }
+ if ( !method.isAccessible() ) method.setAccessible( true );
+ filterDef.setKeyMethod( method );
+ }
+
+ String name = method.getName();
+ if ( name.startsWith( "set" ) && method.getParameterTypes().length
== 1 ) {
+ filterDef.addSetter( Introspector.decapitalize( name.substring( 3 ) ), method );
+ }
+ }
+ filterDefinitions.put( defAnn.name(), filterDef );
+ }
+
//code doesn't have to be multithreaded because SF creation is not.
//this is not a public API, should really only be used during the SessionFActory
building
//FIXME this is ugly, impl.staticmethod, fix that
@@ -124,7 +192,7 @@
public void addOptimizerStrategy(DirectoryProvider<?> provider, OptimizerStrategy
optimizerStrategy) {
dirProviderOptimizerStrategies.put( provider, optimizerStrategy );
}
-
+
public void addIndexingParmeters(DirectoryProvider<?> provider,
LuceneIndexingParameters indexingParams) {
dirProviderIndexingParams.put( provider, indexingParams );
}
@@ -132,10 +200,10 @@
public OptimizerStrategy getOptimizerStrategy(DirectoryProvider<?> provider) {
return dirProviderOptimizerStrategies.get( provider );
}
-
+
public LuceneIndexingParameters getIndexingParameters(DirectoryProvider<?>
provider ) {
return dirProviderIndexingParams.get( provider );
- }
+ }
public ReaderProvider getReaderProvider() {
return readerProvider;
@@ -178,7 +246,7 @@
queue.add( new OptimizeLuceneWork( entityType ) );
getBackendQueueProcessorFactory().getProcessor( queue ).run();
}
-
+
private void initDocumentBuilders(Configuration cfg, ReflectionManager
reflectionManager, Analyzer analyzer) {
Iterator iter = cfg.getClassMappings();
DirectoryProviderFactory factory = new DirectoryProviderFactory();
@@ -187,16 +255,20 @@
Class<?> mappedClass = clazz.getMappedClass();
if (mappedClass != null) {
XClass mappedXClass = reflectionManager.toXClass(mappedClass);
- if (mappedXClass != null && mappedXClass.isAnnotationPresent(Indexed.class))
{
- DirectoryProvider provider = factory.createDirectoryProvider(mappedXClass, cfg,
this);
- // TODO move that into DirectoryProviderFactory
- if (!lockableDirectoryProviders.containsKey(provider)) {
- lockableDirectoryProviders.put(provider, new ReentrantLock());
+ if ( mappedXClass != null) {
+ if ( mappedXClass.isAnnotationPresent( Indexed.class ) ) {
+ DirectoryProvider provider = factory.createDirectoryProvider( mappedXClass, cfg,
this );
+ //TODO move that into DirectoryProviderFactory
+ if ( !lockableDirectoryProviders.containsKey( provider ) ) {
+ lockableDirectoryProviders.put( provider, new ReentrantLock() );
+ }
+ final DocumentBuilder<Object> documentBuilder = new
DocumentBuilder<Object>(
+ mappedXClass, analyzer, provider, reflectionManager
+ );
+
+ documentBuilders.put( mappedClass, documentBuilder );
}
- final DocumentBuilder<Object> documentBuilder = new
DocumentBuilder<Object>(mappedXClass, analyzer,
- provider, reflectionManager);
-
- documentBuilders.put(mappedClass, documentBuilder);
+ bindFilterDefs(mappedXClass);
}
}
}
@@ -204,7 +276,7 @@
/**
* 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.
@@ -233,5 +305,36 @@
throw new SearchException("Failed to instantiate lucene analyzer with type "
+ analyzerClassName, e);
}
return defaultAnalyzer;
- }
+ }
+
+ private void buildFilterCachingStrategy(Properties properties) {
+ String impl = properties.getProperty( Environment.FILTER_CACHING_STRATEGY_IMPL );
+ if ( StringHelper.isEmpty( impl ) || "mru".equalsIgnoreCase( impl ) ) {
+ filterCachingStrategy = new MRUFilterCachingStrategy();
+ }
+ else {
+ try {
+ Class filterCachingStrategyClass =
org.hibernate.annotations.common.util.ReflectHelper.classForName( impl,
SearchFactoryImpl.class );
+ filterCachingStrategy = (FilterCachingStrategy)
filterCachingStrategyClass.newInstance();
+ }
+ catch (ClassNotFoundException e) {
+ throw new SearchException( "Unable to find filterCachingStrategy class: " +
impl, e );
+ }
+ catch (IllegalAccessException e) {
+ throw new SearchException( "Unable to instanciate filterCachingStrategy class:
" + impl, e );
+ }
+ catch (InstantiationException e) {
+ throw new SearchException( "Unable to instanciate filterCachingStrategy class:
" + impl, e );
+ }
+ }
+ filterCachingStrategy.initialize( properties );
+ }
+
+ public FilterCachingStrategy getFilterCachingStrategy() {
+ return filterCachingStrategy;
+ }
+
+ public FilterDef getFilterDefinition(String name) {
+ return filterDefinitions.get( name );
+ }
}
Added:
trunk/HibernateExt/search/src/java/org/hibernate/search/query/FullTextFilterImpl.java
===================================================================
--- trunk/HibernateExt/search/src/java/org/hibernate/search/query/FullTextFilterImpl.java
(rev 0)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/query/FullTextFilterImpl.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,37 @@
+//$Id$
+package org.hibernate.search.query;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import org.hibernate.search.FullTextFilter;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class FullTextFilterImpl implements FullTextFilter {
+ private Map<String, Object> parameters = new HashMap<String, Object>();
+ private String name;
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public FullTextFilter setParameter(String name, Object value) {
+ parameters.put( name, value );
+ return this;
+ }
+
+ public Object getParameter(String name) {
+ return parameters.get( name );
+ }
+
+
+ public Map<String, Object> getParameters() {
+ return parameters;
+ }
+}
Modified:
trunk/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
---
trunk/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2007-07-24
20:35:47 UTC (rev 12812)
+++
trunk/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -4,17 +4,20 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.lang.reflect.InvocationTargetException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Searcher;
@@ -32,16 +35,20 @@
import org.hibernate.engine.query.ParameterMetadata;
import org.hibernate.impl.AbstractQueryImpl;
import org.hibernate.impl.CriteriaImpl;
+import org.hibernate.search.FullTextFilter;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.SearchException;
import org.hibernate.search.engine.DocumentBuilder;
import org.hibernate.search.engine.DocumentExtractor;
import org.hibernate.search.engine.EntityInfo;
+import org.hibernate.search.engine.FilterDef;
import org.hibernate.search.engine.Loader;
import org.hibernate.search.engine.ObjectLoader;
import org.hibernate.search.engine.ProjectionLoader;
import org.hibernate.search.engine.QueryLoader;
import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.hibernate.search.filter.ChainedFilter;
+import org.hibernate.search.filter.FilterKey;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.util.ContextHelper;
@@ -61,8 +68,11 @@
private Integer maxResults;
private Integer resultSize;
private Sort sort;
+ private Filter filter;
private Criteria criteria;
private String[] indexProjection;
+ private SearchFactoryImplementor searchFactoryImplementor;
+ private Map<String, FullTextFilterImpl> filterDefinitions;
/**
* classes must be immutable
@@ -75,13 +85,23 @@
this.classes = classes;
}
-
+ /**
+ * {@inheritDoc}
+ */
public FullTextQuery setSort(Sort sort) {
this.sort = sort;
return this;
}
/**
+ * {@inheritDoc}
+ */
+ public FullTextQuery setFilter(Filter filter) {
+ this.filter = filter;
+ return this;
+ }
+
+ /**
* Return an interator on the results.
* Retrieve the object one by one (initialize it during the next() operation)
*/
@@ -242,16 +262,104 @@
private Hits getHits(Searcher searcher) throws IOException {
Hits hits;
org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
- if ( sort == null ) {
- hits = searcher.search( query );
- }
- else {
- hits = searcher.search( query, sort );
- }
+ buildFilters();
+ hits = searcher.search( query, filter, sort );
setResultSize( hits );
return hits;
}
+ private void buildFilters() {
+ SearchFactoryImplementor searchFactoryImplementor = getSearchFactoryImplementor();
+ if ( filterDefinitions != null && filterDefinitions.size() > 0 ) {
+ ChainedFilter chainedFilter = new ChainedFilter();
+ for ( FullTextFilterImpl filterDefinition : filterDefinitions.values() ) {
+ FilterDef def = searchFactoryImplementor.getFilterDefinition(
filterDefinition.getName() );
+ Class implClass = def.getImpl();
+ Object instance;
+ try {
+ instance = implClass.newInstance();
+ }
+ catch (InstantiationException e) {
+ throw new SearchException( "Unable to create @FullTextFilterDef: " +
def.getImpl(), e );
+ }
+ catch (IllegalAccessException e) {
+ throw new SearchException( "Unable to create @FullTextFilterDef: " +
def.getImpl(), e );
+ }
+ for ( Map.Entry<String, Object> entry :
filterDefinition.getParameters().entrySet() ) {
+ def.invoke( entry.getKey(), instance, entry.getValue() );
+ }
+ if ( def.getKeyMethod() == null && filterDefinition.getParameters().size()
> 0 ) {
+ throw new SearchException("Filter with parameters and no @Key method: " +
filterDefinition.getName() );
+ }
+ FilterKey key;
+ if ( def.getKeyMethod() == null ) {
+ key = new FilterKey( ) {
+ public int hashCode() {
+ return getImpl().hashCode();
+ }
+
+ public boolean equals(Object obj) {
+ if ( ! ( obj instanceof FilterKey ) ) return false;
+ FilterKey that = (FilterKey) obj;
+ return this.getImpl().equals( that.getImpl() );
+ }
+ };
+ }
+ else {
+ try {
+ key = (FilterKey) def.getKeyMethod().invoke( instance );
+ }
+ catch (IllegalAccessException e) {
+ 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: "
+ + def.getImpl().getName() + "." + def.getKeyMethod().getName() );
+ }
+ catch (ClassCastException e) {
+ throw new SearchException("@Key method does not return FilterKey: "
+ + def.getImpl().getName() + "." + def.getKeyMethod().getName() );
+ }
+ }
+ key.setImpl( def.getImpl() );
+ Filter filter = searchFactoryImplementor.getFilterCachingStrategy().getCachedFilter(
key );
+ 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: "
+ + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
+ }
+ catch (InvocationTargetException e) {
+ 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: "
+ + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
+ }
+ }
+ else {
+ try {
+ filter = (Filter) instance;
+ }
+ catch (ClassCastException e) {
+ throw new SearchException("@Key method does not return a
org.apache.lucene.search.Filter class: "
+ + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
+ }
+ }
+ searchFactoryImplementor.getFilterCachingStrategy().addCachedFilter( key, filter );
+ }
+ chainedFilter.addFilter( filter );
+ }
+ if ( filter != null ) chainedFilter.addFilter( filter );
+ filter = chainedFilter;
+ }
+ }
+
private org.apache.lucene.search.Query
filterQueryByClasses(org.apache.lucene.search.Query luceneQuery) {
//A query filter is more practical than a manual class filtering post query (esp on
scrollable resultsets)
//it also probably minimise the memory footprint
@@ -404,6 +512,34 @@
return null;
}
+ public FullTextFilter enableFullTextFilter(String name) {
+ if ( filterDefinitions == null ) {
+ filterDefinitions = new HashMap<String, FullTextFilterImpl>();
+ }
+ FullTextFilterImpl filterDefinition = filterDefinitions.get( name );
+ if ( filterDefinition != null ) return filterDefinition;
+
+ filterDefinition = new FullTextFilterImpl();
+ filterDefinition.setName( name );
+ FilterDef filterDef = getSearchFactoryImplementor().getFilterDefinition( name );
+ if (filterDef == null) {
+ throw new SearchException("Unkown @FullTextFilter: " + name);
+ }
+ filterDefinitions.put(name, filterDefinition);
+ return filterDefinition;
+ }
+
+ public void disableFullTextFilter(String name) {
+ filterDefinitions.remove( name );
+ }
+
+ private SearchFactoryImplementor getSearchFactoryImplementor() {
+ if ( searchFactoryImplementor == null ) {
+ searchFactoryImplementor = ContextHelper.getSearchFactoryBySFI( session );
+ }
+ return searchFactoryImplementor;
+ }
+
private static Loader noLoader = new Loader() {
public void init(Session session, SearchFactoryImplementor searchFactoryImplementor) {
}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/BestDriversFilter.java
===================================================================
---
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/BestDriversFilter.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/BestDriversFilter.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,25 @@
+//$Id$
+package org.hibernate.search.test.filter;
+
+import java.util.BitSet;
+import java.io.IOException;
+
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.Term;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class BestDriversFilter extends Filter {
+
+ public BitSet bits(IndexReader reader) throws IOException {
+ BitSet bitSet = new BitSet( reader.maxDoc() );
+ TermDocs termDocs = reader.termDocs( new Term("score", "5") );
+ while ( termDocs.next() ) {
+ bitSet.set( termDocs.doc() );
+ }
+ return bitSet;
+ }
+}
Added: trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/Driver.java
===================================================================
--- trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/Driver.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/Driver.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,104 @@
+//$Id$
+package org.hibernate.search.test.filter;
+
+import java.util.Date;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+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.DateBridge;
+import org.hibernate.search.annotations.Resolution;
+import org.hibernate.search.annotations.FullTextFilterDef;
+import org.hibernate.search.annotations.FullTextFilterDefs;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+@Indexed
+@FullTextFilterDefs( {
+ @FullTextFilterDef(name = "bestDriver", impl = BestDriversFilter.class),
//actual Filter implementation
+ @FullTextFilterDef(name = "security", impl = SecurityFilterFactory.class)
//Filter factory with parameters
+})
+public class Driver {
+ @Id
+ @DocumentId
+ private int id;
+ @Field(index= Index.TOKENIZED)
+ private String name;
+ @Field(index= Index.UN_TOKENIZED)
+ private String teacher;
+ @Field(index= Index.UN_TOKENIZED)
+ private int score;
+ @Field(index= Index.UN_TOKENIZED)
+ @DateBridge( resolution = Resolution.YEAR)
+ private Date delivery;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getTeacher() {
+ return teacher;
+ }
+
+ public void setTeacher(String teacher) {
+ this.teacher = teacher;
+ }
+
+ public int getScore() {
+ return score;
+ }
+
+ public void setScore(int score) {
+ this.score = score;
+ }
+
+ public Date getDelivery() {
+ return delivery;
+ }
+
+ public void setDelivery(Date delivery) {
+ this.delivery = delivery;
+ }
+
+ public boolean equals(Object o) {
+ if ( this == o ) return true;
+ if ( o == null || getClass() != o.getClass() ) return false;
+
+ Driver driver = (Driver) o;
+
+ if ( id != driver.id ) return false;
+ if ( score != driver.score ) return false;
+ if ( delivery != null ? !delivery.equals( driver.delivery ) : driver.delivery != null )
return false;
+ if ( name != null ? !name.equals( driver.name ) : driver.name != null ) return false;
+ if ( teacher != null ? !teacher.equals( driver.teacher ) : driver.teacher != null )
return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = id;
+ result = 31 * result + ( name != null ? name.hashCode() : 0 );
+ result = 31 * result + ( teacher != null ? teacher.hashCode() : 0 );
+ result = 31 * result + score;
+ result = 31 * result + ( delivery != null ? delivery.hashCode() : 0 );
+ return result;
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/FilterTest.java
===================================================================
--- trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/FilterTest.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/FilterTest.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,137 @@
+//$Id$
+package org.hibernate.search.test.filter;
+
+import java.util.Date;
+import java.util.Calendar;
+
+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.apache.lucene.search.Query;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.RangeFilter;
+import org.apache.lucene.index.Term;
+
+/**
+ * @author Emmanuel Bernard
+ * @author Hardy Ferentschik
+ */
+public class FilterTest extends SearchTestCase {
+
+ public void testNamedFilters() {
+ createData();
+ FullTextSession s = Search.createFullTextSession( openSession( ) );
+ s.getTransaction().begin();
+ BooleanQuery query = new BooleanQuery();
+ query.add( new TermQuery( new Term("teacher", "andre") ),
BooleanClause.Occur.SHOULD );
+ query.add( new TermQuery( new Term("teacher", "max") ),
BooleanClause.Occur.SHOULD );
+ query.add( new TermQuery( new Term("teacher", "aaron") ),
BooleanClause.Occur.SHOULD );
+ FullTextQuery ftQuery = s.createFullTextQuery( query, Driver.class );
+ assertEquals("No filter should happen", 3, ftQuery.getResultSize() );
+
+ ftQuery = s.createFullTextQuery( query, Driver.class );
+ ftQuery.enableFullTextFilter( "bestDriver");
+ assertEquals("Should filter out Gavin", 2, ftQuery.getResultSize() );
+
+ ftQuery = s.createFullTextQuery( query, Driver.class );
+ ftQuery.enableFullTextFilter( "bestDriver");
+ ftQuery.enableFullTextFilter( "security").setParameter( "login",
"andre" );
+ assertEquals("Should filter to limit to Emmanuel", 1, ftQuery.getResultSize()
);
+
+ ftQuery = s.createFullTextQuery( query, Driver.class );
+ ftQuery.enableFullTextFilter( "bestDriver");
+ ftQuery.enableFullTextFilter( "security").setParameter( "login",
"andre" );
+ ftQuery.disableFullTextFilter( "security");
+ ftQuery.disableFullTextFilter( "bestDriver");
+ assertEquals("Should not filter anymore", 3, ftQuery.getResultSize() );
+
+ s.getTransaction().commit();
+ s.close();
+ deleteData();
+ }
+
+ public void testStraightFilters() {
+ createData();
+ FullTextSession s = Search.createFullTextSession( openSession( ) );
+ s.getTransaction().begin();
+ BooleanQuery query = new BooleanQuery();
+ query.add( new TermQuery( new Term("teacher", "andre") ),
BooleanClause.Occur.SHOULD );
+ query.add( new TermQuery( new Term("teacher", "max") ),
BooleanClause.Occur.SHOULD );
+ query.add( new TermQuery( new Term("teacher", "aaron") ),
BooleanClause.Occur.SHOULD );
+ FullTextQuery ftQuery;
+
+ ftQuery = s.createFullTextQuery( query, Driver.class );
+ ftQuery.enableFullTextFilter( "bestDriver");
+ Filter dateFilter = new RangeFilter("delivery", "2001",
"2005",
+ true, true);
+ ftQuery.setFilter( dateFilter );
+ assertEquals("Should select only liz", 1, ftQuery.getResultSize() );
+
+ ftQuery = s.createFullTextQuery( query, Driver.class );
+ ftQuery.setFilter( dateFilter );
+ ftQuery.enableFullTextFilter( "bestDriver");
+ ftQuery.enableFullTextFilter( "security").setParameter( "login",
"andre" );
+ ftQuery.disableFullTextFilter( "security");
+ ftQuery.disableFullTextFilter( "bestDriver");
+ ftQuery.setFilter( null );
+ assertEquals("Should not filter anymore", 3, ftQuery.getResultSize() );
+
+ s.getTransaction().commit();
+ s.close();
+ deleteData();
+ }
+
+
+ private void deleteData() {
+ Session s = openSession( );
+ s.getTransaction().begin();
+ s.createQuery( "delete " + Driver.class.getName() + "
t").executeUpdate();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private void createData() {
+ Session s = openSession( );
+ s.getTransaction().begin();
+ Calendar cal = Calendar.getInstance();
+ cal.set( 2006, 10, 11);
+ Driver driver = new Driver();
+ driver.setDelivery( cal.getTime() );
+ driver.setId( 1 );
+ driver.setName( "Emmanuel" );
+ driver.setScore( 5 );
+ driver.setTeacher( "andre" );
+ s.persist( driver );
+
+ cal.set( 2007, 10, 11);
+ driver = new Driver();
+ driver.setDelivery( cal.getTime() );
+ driver.setId( 2 );
+ driver.setName( "Gavin" );
+ driver.setScore( 3 );
+ driver.setTeacher( "aaron" );
+ s.persist( driver );
+
+ cal.set( 2004, 10, 11);
+ driver = new Driver();
+ driver.setDelivery( cal.getTime() );
+ driver.setId( 3 );
+ driver.setName( "Liz" );
+ driver.setScore( 5 );
+ driver.setTeacher( "max" );
+ s.persist( driver );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected Class[] getMappings() {
+ return new Class[] {
+ Driver.class
+ };
+ }
+}
Added:
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/SecurityFilterFactory.java
===================================================================
---
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/SecurityFilterFactory.java
(rev 0)
+++
trunk/HibernateExt/search/src/test/org/hibernate/search/test/filter/SecurityFilterFactory.java 2007-07-25
02:58:26 UTC (rev 12813)
@@ -0,0 +1,43 @@
+//$Id$
+package org.hibernate.search.test.filter;
+
+import org.hibernate.search.filter.FilterKey;
+import org.hibernate.search.filter.StandardFilterKey;
+import org.hibernate.search.annotations.Key;
+import org.hibernate.search.annotations.Factory;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.QueryFilter;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.QueryWrapperFilter;
+import org.apache.lucene.search.CachingWrapperFilter;
+import org.apache.lucene.index.Term;
+
+/**
+ * Apply a security filter to the results
+ *
+ * @author Emmanuel Bernard
+ */
+public class SecurityFilterFactory {
+ private String login;
+
+ /**
+ * injected parameter
+ */
+ public void setLogin(String login) {
+ this.login = login;
+ }
+
+ @Key
+ public FilterKey getKey() {
+ StandardFilterKey key = new StandardFilterKey();
+ key.addParameter( login );
+ return key;
+ }
+
+ @Factory
+ public Filter getFilter() {
+ Query query = new TermQuery( new Term("teacher", login) );
+ return new CachingWrapperFilter( new QueryWrapperFilter(query) );
+ }
+}