[hibernate-dev] [HSearch] Facet drilldown

Emmanuel Bernard emmanuel at hibernate.org
Mon Mar 14 10:43:20 EDT 2011


Note that another benefit of option 2 is that we can down the road (post 3.4) implement a way to pre-select which facet element is enabled (say you want to filter by Epson and Canon by default for the first query).

facet().name("Brand")
  .onField("brand")
  .discrete()
  .select("Epson", "Canon")
  .createFacetRequest();

Same for range, which might be even more useful.

BTW what's the facet.value() for a range facet?

On 14 mars 2011, at 15:29, Emmanuel Bernard wrote:

> 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%3A392442011%2Ck%3Ascreen&keywords=screen&ie=UTF8&qid=1300105439&scn=392442011&h=9aa8a737035ed82d321ca117e8f48611956411e5#/ref=nb_sb_noss?url=node%3D392442011&field-keywords=screen&rh=n%3A172282%2Cn%3A%21493964%2Cn%3A281407%2Cn%3A172575%2Cn%3A392442011%2Ck%3Ascreen
> 
> 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 at lists.jboss.org
>> https://lists.jboss.org/mailman/listinfo/hibernate-dev
> 
> 
> _______________________________________________
> hibernate-dev mailing list
> hibernate-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/hibernate-dev





More information about the hibernate-dev mailing list