I've just committed support for Lucene Filters inside Hibernate Search
I like the global idea but not entirely happy yet with some of the
details. I would appreciate some feedbacks on it, especially how to
simplify the user experience. Lucene Filters are very similar to
Hibernate Core filters in the way they interact with data.
How to define filters
@FullTextFilterDef(name = "bestDriver", impl =
BestDriversFilter.class) //on any of the @Indexed class, there is no
association to the class
BestDriverFilter is a org.apache.lucene.search.Filter implementation
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;
}
}
To enable it on a given fullTextQuery:
fullTextQuery.enableFullTextFilter( "bestDriver");
the query results are now filtered
Sometimes (often), the filter needs to get some parameters injected.
The filter creation might be fairly complex and require a factory.
The following example shows how it works in complex cases:
@FullTextFilterDef(name = "security", impl =
SecurityFilterFactory.class) //on any @Indexed class, there is no
association to the class
/**
* this is the way most Query filters will likely to be implemented
*/
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) );
}
}
setLogin is the way parameters are injected (see below)
the method marked as @Key must return a FilterKey. This object must
implement equals and hashCode in a way that uniquely identify a given
filter type with a given set of parameters (for caching, see later).
StandardFilterKey is a standard implementation to make things simpler
in common cases. @Key is necessary when parameters are used. @Key can
be define on a factory method or on a filter method.
PS Key is the part I'm not too keen about. But to enable caching
the method marked as @Factory is used to return a Filter instance
(with the appropriate parameters set).
To enable this filter:
To enable it on a given fullTextQuery:
fullTextQuery.enableFullTextFilter( "bestDriver").setParameter
("login", "emmanuel");
the query results are now filtered
the parameter key is the setter name, the parameter value is any object.
More than one filter can be applied on a given query, the filtered
intersection is applied.
Filter instances are cached (per their key). It is useful when used
in conjunction with a Lucene CachingWrapperFilter: that way filters
computation is only done the first time.
The default caching strategy is a MRU + soft reference ( thanks
Steve ;) ), but it can be overridden by a custom implementation.
I am contemplating the idea of adding a @FullTextFilterDef.cache
parameter to enable/disable the cache on a per filter definition
basis. It would also help to get rid of @Key (at the cost of not
caching).
If the user wants to deal with the filters manually (for whatever
reason) there is a fullTextSession.setFilter(securityfilter);
I'm not sure I want to keep this method alive though.
The code has just been committed, please review (esp the method /
class naming) and let me know what you think, and how it could be
made simpler. The test case are in org.hibernate.search.test.filter
Emmanuel
PS I've attached the patch if you prefer reading it rather than TRUNK