As you guys know I've not been comfortable with the idea of reusing Filter as an
implementation as it seem to be it is an implementation detail that we might change down
the road and that is of no interest to the user.
Instead of bitching abstractly, I've decided to implement a CDI style bean that
implement faceted search to see how the API us used.
Note that I have tried to implement what amazon does
http://www.amazon.com/gp/search/ref=sr_nr_scat_392442011_ln?rh=n%3A392442...
My feature list was:
- multiple facets per query
- ability to select one or more discrete facet values on a given facet (ie brand = epson
or canon)
- ability to display the facet menu on the left
- ability to remember the state of the selected facet values
While implementing it I've found several proposals for enhancement:
- should we rename in thw facet DSL createFacet to createFacetRequest(), as it's the
object returned
- does facet work with pagination (ie is not affected) => we did some optimization
around QueryHits when paginating => add a unit test
- jpa.FullTextQuery does not have the facet methods
- move Facet methods hosted on FTQuery to a FacetManager interface and do
ftQuery.getFacetManager().enableFacet("cdscds");
- make FacetRequest / FacetResults interfaces?
- Facet should implement equals / hashCode or else we cannot remember the facet selected
- //TODO should the index be included in object Facet? Not sure but worth thinking about
=> probably not if Facet objects change indexes from one query to the other
- should we rename Facet to FacetElement? We seem to talk about Facets as the global
concept whereas Facet the object represent one of the results of the Facet.
The client of the service is as followed
https://gist.github.com/869181
The solution with the current API is as followed
https://gist.github.com/869143
In this implementation:
- I need to keep some kind of state besides the query to remember which facet has been
selected by the user and which one have not been.
- I need to remember this state across query re execution: even if the request (and thus
the facetREsults) are updated, I need to remember this state or the user will yell
- FacetFilter as it is does not solve the problem at had as I need to:
. use a OR filter for selected elements across a Facet
. use AndFilter to aggregate the various facets and filter the results across all of
them
- I ended up writing and manipulating various OrFacetFilter and AndFilter object which
are not exactly easy to play with
I've worked on an alternative API that internalize the management of this facet
element selection so that the whole filter tree can be managed by Hibernate Search itself
and exposed to the user as a service
https://gist.github.com/869145
Query execution and faceting display are of similar complexity. But the select or deselect
method is simpler. Also note that I no longer need to implement or manipulate And/Or
filters myself (and potentially forget to set it).
I have attached the state of the selected facet elements on the FacetRequest (the
FacetResults are destroyed and reset upon requery). We might want to move it up to
FacetManager (not shown here), I ahve not thought about it.
What do you guys think? If I had to write a book on Hibernate Search, I'd prefer the
second option by a big margin.
On 11 mars 2011, at 20:08, Hardy Ferentschik wrote:
Hi,
sorry for the following lengthy code example, but it is hard to
demonstrate this much shorter.
The example shows how you can enable multiple facets and then continuously
restrict the query result
by chaining facets into a FacetFilter (which delegates to a ChainedFilter
underneath). WDYT?
If you want to see all the code or even get the feature branch -
https://github.com/hferentschik/hibernate-search/blob/HSEARCH-706
public void testMultipleFacetDrillDown() throws Exception {
final String ccsFacetName = "ccs";
final String ccsFacetFieldName = "cubicCapacity";
FacetRequest ccsFacetRequest = queryBuilder( Car.class ).facet()
.name( ccsFacetName )
.onField( ccsFacetFieldName )
.discrete()
.createFacet();
final String colorFacetName = "color";
final String colorFacetFieldName = "color";
FacetRequest colorFacetRequest = queryBuilder( Car.class ).facet()
.name( colorFacetName )
.onField( colorFacetFieldName )
.discrete()
.createFacet();
FullTextQuery query = createMatchAllQuery( Car.class );
query.enableFacet( colorFacetRequest );
query.enableFacet( ccsFacetRequest );
assertEquals( "Wrong number of query matches", 50, query.getResultSize()
);
List<Facet> colorFacetList = getFacetListForFacet( query, colorFacetName
);
assertFacetCounts( colorFacetList, new int[] { 12, 12, 12, 12, 2 } );
List<Facet> ccsFacetList = getFacetListForFacet( query, ccsFacetName );
assertFacetCounts( ccsFacetList, new int[] { 17, 16, 16, 1 } );
FacetFilter facetFilter = new FacetFilter();
query.setFilter( facetFilter );
facetFilter.addFacet( colorFacetList.get( 0 ) );
colorFacetList = getFacetListForFacet( query, colorFacetName );
assertFacetCounts( colorFacetList, new int[] { 12, 0, 0, 0, 0 } );
ccsFacetList = getFacetListForFacet( query, ccsFacetName );
assertFacetCounts( ccsFacetList, new int[] { 4, 4, 4, 0 } );
facetFilter.addFacet( ccsFacetList.get( 0 ) );
// needs to set the filter explicitly atm, because I need the query
state to reset
query.setFilter( facetFilter );
colorFacetList = getFacetListForFacet( query, colorFacetName );
assertFacetCounts( colorFacetList, new int[] { 4, 0, 0, 0, 0 } );
ccsFacetList = getFacetListForFacet( query, ccsFacetName );
assertFacetCounts( ccsFacetList, new int[] { 4, 0, 0, 0 } );
}
I like the idea of using Lucene Filters to implement the drilldown for. It
seems the most natural and the original query stays
untouched.
--Hardy
_______________________________________________
hibernate-dev mailing list
hibernate-dev(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/hibernate-dev