/* * Hibernate Search, full-text search for your domain model * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.search.test.query.facet; import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.apache.lucene.search.MatchAllDocsQuery; import org.hibernate.search.annotations.Analyze; import org.hibernate.search.annotations.DocumentId; import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Index; import org.hibernate.search.annotations.Indexed; import org.hibernate.search.annotations.IndexedEmbedded; import org.hibernate.search.backend.spi.Work; import org.hibernate.search.backend.spi.WorkType; import org.hibernate.search.query.dsl.QueryBuilder; import org.hibernate.search.query.engine.spi.HSQuery; import org.hibernate.search.query.facet.Facet; import org.hibernate.search.query.facet.FacetSortOrder; import org.hibernate.search.query.facet.FacetingRequest; import org.hibernate.search.testsupport.TestForIssue; import org.hibernate.search.testsupport.junit.SearchFactoryHolder; import org.hibernate.search.testsupport.setup.TransactionContextForTest; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @TestForIssue(jiraKey = "HSEARCH-2535") public class MultiValuedFacetingTest { @Rule public SearchFactoryHolder sfHolder = new SearchFactoryHolder( StringArrayFacetEntity.class, StringCollectionFacetEntity.class, StringMapFacetEntity.class, NumberArrayFacetEntity.class, NumberCollectionFacetEntity.class, NumberMapFacetEntity.class ); @Test public void stringArray() throws Exception { StringArrayFacetEntity entity = new StringArrayFacetEntity( 1L ); storeData( entity.id, entity ); entity = new StringArrayFacetEntity( 1L, "foo" ); storeData( entity.id, entity ); entity = new StringArrayFacetEntity( 2L, "foo", "bar" ); storeData( entity.id, entity ); HSQuery hsQuery = sfHolder.getSearchFactory().createHSQuery( new MatchAllDocsQuery(), StringArrayFacetEntity.class ); QueryBuilder builder = sfHolder.getSearchFactory().buildQueryBuilder().forEntity( StringArrayFacetEntity.class ).get(); FacetingRequest facetReq = builder.facet() .name( "someFacet" ) .onField( "facet" ) .discrete() .orderedBy( FacetSortOrder.COUNT_DESC ) .includeZeroCounts( false ) .maxFacetCount( 10 ) .createFacetingRequest(); List<Facet> facets = hsQuery.getFacetManager().enableFaceting( facetReq ).getFacets( "someFacet" ); assertEquals( "There should be two facets", 2, facets.size() ); assertFacet( facets.get( 0 ), "foo", 2 ); assertFacet( facets.get( 1 ), "bar", 1 ); } @Test public void stringCollection() throws Exception { StringCollectionFacetEntity entity = new StringCollectionFacetEntity( 1L ); storeData( entity.id, entity ); entity = new StringCollectionFacetEntity( 1L, "foo" ); storeData( entity.id, entity ); entity = new StringCollectionFacetEntity( 2L, "foo", "bar" ); storeData( entity.id, entity ); HSQuery hsQuery = sfHolder.getSearchFactory().createHSQuery( new MatchAllDocsQuery(), StringCollectionFacetEntity.class ); QueryBuilder builder = sfHolder.getSearchFactory().buildQueryBuilder().forEntity( StringCollectionFacetEntity.class ).get(); FacetingRequest facetReq = builder.facet() .name( "someFacet" ) .onField( "facet" ) .discrete() .orderedBy( FacetSortOrder.COUNT_DESC ) .includeZeroCounts( false ) .maxFacetCount( 10 ) .createFacetingRequest(); List<Facet> facets = hsQuery.getFacetManager().enableFaceting( facetReq ).getFacets( "someFacet" ); assertEquals( "There should be two facets", 2, facets.size() ); assertFacet( facets.get( 0 ), "foo", 2 ); assertFacet( facets.get( 1 ), "bar", 1 ); } @Test public void stringMap() throws Exception { StringMapFacetEntity entity = new StringMapFacetEntity( 1L ); storeData( entity.id, entity ); entity = new StringMapFacetEntity( 1L, "foo" ); storeData( entity.id, entity ); entity = new StringMapFacetEntity( 2L, "foo", "bar" ); storeData( entity.id, entity ); HSQuery hsQuery = sfHolder.getSearchFactory().createHSQuery( new MatchAllDocsQuery(), StringMapFacetEntity.class ); QueryBuilder builder = sfHolder.getSearchFactory().buildQueryBuilder().forEntity( StringMapFacetEntity.class ).get(); FacetingRequest facetReq = builder.facet() .name( "someFacet" ) .onField( "facet" ) .discrete() .orderedBy( FacetSortOrder.COUNT_DESC ) .includeZeroCounts( false ) .maxFacetCount( 10 ) .createFacetingRequest(); List<Facet> facets = hsQuery.getFacetManager().enableFaceting( facetReq ).getFacets( "someFacet" ); assertEquals( "There should be two facets", 2, facets.size() ); assertFacet( facets.get( 0 ), "foo", 2 ); assertFacet( facets.get( 1 ), "bar", 1 ); } @Test @Ignore // HSEARCH-1927 : Range faceting on multiple numeric values does not work public void numberArray() throws Exception { NumberArrayFacetEntity entity = new NumberArrayFacetEntity( 1L ); storeData( entity.id, entity ); entity = new NumberArrayFacetEntity( 1L, 42 ); storeData( entity.id, entity ); entity = new NumberArrayFacetEntity( 2L, 43, 442 ); storeData( entity.id, entity ); HSQuery hsQuery = sfHolder.getSearchFactory().createHSQuery( new MatchAllDocsQuery(), NumberArrayFacetEntity.class ); QueryBuilder builder = sfHolder.getSearchFactory().buildQueryBuilder().forEntity( NumberArrayFacetEntity.class ).get(); FacetingRequest facetReq = builder.facet() .name( "someFacet" ) .onField( "facet" ) .range() .from( 0.0f ).to( 100.0f ).excludeLimit() .from( 100.0f ).to( 500.0f ).excludeLimit() .orderedBy( FacetSortOrder.COUNT_DESC ) .includeZeroCounts( false ) .maxFacetCount( 10 ) .createFacetingRequest(); List<Facet> facets = hsQuery.getFacetManager().enableFaceting( facetReq ).getFacets( "someFacet" ); assertEquals( "There should be two facets", 2, facets.size() ); assertFacet( facets.get( 0 ), "[0.0, 100.0)", 2 ); assertFacet( facets.get( 1 ), "[100.0, 500.0)", 1 ); } @Test @Ignore // HSEARCH-1927 : Range faceting on multiple numeric values does not work public void numberCollection() throws Exception { NumberCollectionFacetEntity entity = new NumberCollectionFacetEntity( 1L ); storeData( entity.id, entity ); entity = new NumberCollectionFacetEntity( 1L, 42.2f ); storeData( entity.id, entity ); entity = new NumberCollectionFacetEntity( 2L, 42.3f, 442.2f ); storeData( entity.id, entity ); HSQuery hsQuery = sfHolder.getSearchFactory().createHSQuery( new MatchAllDocsQuery(), NumberCollectionFacetEntity.class ); QueryBuilder builder = sfHolder.getSearchFactory().buildQueryBuilder().forEntity( NumberCollectionFacetEntity.class ).get(); FacetingRequest facetReq = builder.facet() .name( "someFacet" ) .onField( "facet" ) .range() .from( 0.0f ).to( 100.0f ).excludeLimit() .from( 100.0f ).to( 500.0f ).excludeLimit() .orderedBy( FacetSortOrder.COUNT_DESC ) .includeZeroCounts( false ) .maxFacetCount( 10 ) .createFacetingRequest(); List<Facet> facets = hsQuery.getFacetManager().enableFaceting( facetReq ).getFacets( "someFacet" ); assertEquals( "There should be two facets", 2, facets.size() ); assertFacet( facets.get( 0 ), "[0.0, 100.0)", 2 ); assertFacet( facets.get( 1 ), "[100.0, 500.0)", 1 ); } @Test @Ignore // HSEARCH-1927 : Range faceting on multiple numeric values does not work public void numberMap() throws Exception { NumberMapFacetEntity entity = new NumberMapFacetEntity( 1L ); storeData( entity.id, entity ); entity = new NumberMapFacetEntity( 1L, 42.2f ); storeData( entity.id, entity ); entity = new NumberMapFacetEntity( 2L, 42.3f, 442.2f ); storeData( entity.id, entity ); HSQuery hsQuery = sfHolder.getSearchFactory().createHSQuery( new MatchAllDocsQuery(), NumberMapFacetEntity.class ); QueryBuilder builder = sfHolder.getSearchFactory().buildQueryBuilder().forEntity( NumberMapFacetEntity.class ).get(); FacetingRequest facetReq = builder.facet() .name( "someFacet" ) .onField( "facet" ) .range() .from( 0.0f ).to( 100.0f ).excludeLimit() .from( 100.0f ).to( 500.0f ).excludeLimit() .orderedBy( FacetSortOrder.COUNT_DESC ) .includeZeroCounts( false ) .maxFacetCount( 10 ) .createFacetingRequest(); List<Facet> facets = hsQuery.getFacetManager().enableFaceting( facetReq ).getFacets( "someFacet" ); assertEquals( "There should be two facets", 2, facets.size() ); assertFacet( facets.get( 0 ), "[0.0, 100.0)", 2 ); assertFacet( facets.get( 1 ), "[100.0, 500.0)", 1 ); } private void storeData(Long id, Object entry) { Work work = new Work( entry, id, WorkType.ADD, false ); TransactionContextForTest tc = new TransactionContextForTest(); sfHolder.getSearchFactory().getWorker().performWork( work, tc ); tc.end(); } private void assertFacet(Facet facet, String expectedValue, int expectedCount) { assertEquals( "Wrong facet value", expectedValue, facet.getValue() ); assertEquals( "Wrong facet count", expectedCount, facet.getCount() ); } @Indexed private static class StringArrayFacetEntity { @DocumentId private Long id; @Field(analyze = Analyze.NO) @org.hibernate.search.annotations.Facet @IndexedEmbedded // This enables ArrayBridge: not needed for faceting, but needed for indexing private String[] facet; private StringArrayFacetEntity(Long id, String ... facet) { super(); this.id = id; this.facet = facet; } } @Indexed private static class StringCollectionFacetEntity { @DocumentId private Long id; @Field(analyze = Analyze.NO) @org.hibernate.search.annotations.Facet @IndexedEmbedded // This enables IterableBridge: not needed for faceting, but needed for indexing private Collection<String> facet; private StringCollectionFacetEntity(Long id, String ... facet) { super(); this.id = id; this.facet = Arrays.asList( facet ); } } @Indexed private static class StringMapFacetEntity { @DocumentId private Long id; @Field(analyze = Analyze.NO) @org.hibernate.search.annotations.Facet @IndexedEmbedded // This enables MapBridge: not needed for faceting, but needed for indexing private Map<Integer, String> facet; private StringMapFacetEntity(Long id, String ... facet) { super(); this.id = id; this.facet = new TreeMap<>(); int index = 0; for ( String facetValue : facet ) { this.facet.put( index, facetValue ); ++index; } } } @Indexed private static class NumberArrayFacetEntity { @DocumentId private Long id; @Field(analyze = Analyze.NO) @org.hibernate.search.annotations.Facet @IndexedEmbedded // This enables ArrayBridge: not needed for faceting, but needed for indexing private int[] facet; private NumberArrayFacetEntity(Long id, int ... facet) { super(); this.id = id; this.facet = facet; } } @Indexed private static class NumberCollectionFacetEntity { @DocumentId private Long id; @Field(analyze = Analyze.NO) @org.hibernate.search.annotations.Facet @IndexedEmbedded // This enables IterableBridge: not needed for faceting, but needed for indexing private Collection<Float> facet; private NumberCollectionFacetEntity(Long id, Float ... facet) { super(); this.id = id; this.facet = Arrays.asList( facet ); } } @Indexed private static class NumberMapFacetEntity { @DocumentId private Long id; @Field(analyze = Analyze.NO, index = Index.NO) @org.hibernate.search.annotations.Facet @IndexedEmbedded // This enables MapBridge: not needed for faceting, but needed for indexing private Map<Integer, Float> facet; private NumberMapFacetEntity(Long id, Float ... facet) { super(); this.id = id; this.facet = new TreeMap<>(); int index = 0; for ( Float facetValue : facet ) { this.facet.put( index, facetValue ); ++index; } } } }