diff --git a/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java b/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java index f32544645d..4dd5c982fc 100644 --- a/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java +++ b/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java @@ -29,6 +29,16 @@ package org.alfresco.rest.api.search.impl; import static org.alfresco.rest.api.search.impl.StoreMapper.DELETED; import static org.alfresco.rest.api.search.impl.StoreMapper.LIVE_NODES; import static org.alfresco.rest.api.search.impl.StoreMapper.VERSIONS; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + import org.alfresco.repo.search.impl.lucene.SolrJSONResultSet; import org.alfresco.repo.version.Version2Model; import org.alfresco.rest.api.DeletedNodes; @@ -64,13 +74,7 @@ import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; +import com.google.gdata.data.introspection.Collection; /** * Maps from a ResultSet to a json public api representation. @@ -131,7 +135,7 @@ public class ResultMapper { SearchContext context = null; Integer total = null; - List noderesults = new ArrayList(); + List noderesults = new ArrayList(); Map mapUserInfo = new HashMap<>(10); Map>>> hightLighting = results.getHighlighting(); int notFound = 0; @@ -272,27 +276,38 @@ public class ResultMapper Map facetQueries = solrResultSet.getFacetQueries(); List facetResults = null; SpellCheckContext spellCheckContext = null; - List ffcs = null; + List ffcs = new ArrayList(); //Facet queries if(facetQueries!= null && !facetQueries.isEmpty()) { - facetResults = new ArrayList<>(facetQueries.size()); - for (Entry fq:facetQueries.entrySet()) + //If group by field populated in query facet return bucketing into facet field. + List facetQueryForFields = + getFacetBucketsFromFacetQueries(facetQueries,searchQuery); + if(!facetQueryForFields.isEmpty()) { - String filterQuery = null; - if (searchQuery != null && searchQuery.getFacetQueries() != null) + ffcs.addAll(facetQueryForFields); + } + else + { + // Return the old way facet query with no bucketing. + facetResults = new ArrayList<>(facetQueries.size()); + for (Entry fq:facetQueries.entrySet()) { - Optional found = searchQuery.getFacetQueries().stream().filter(facetQuery -> fq.getKey().equals(facetQuery.getLabel())).findFirst(); - filterQuery = found.isPresent()? found.get().getQuery():fq.getKey(); + String filterQuery = null; + if (searchQuery != null && searchQuery.getFacetQueries() != null) + { + Optional found = searchQuery.getFacetQueries().stream().filter(facetQuery -> fq.getKey().equals(facetQuery.getLabel())).findFirst(); + filterQuery = found.isPresent()? found.get().getQuery():fq.getKey(); + } + facetResults.add(new FacetQueryContext(fq.getKey(), filterQuery, fq.getValue())); } - facetResults.add(new FacetQueryContext(fq.getKey(), filterQuery, fq.getValue())); } } //Field Facets Map>> facetFields = solrResultSet.getFieldFacets(); - ffcs = getFacetBucketsForFacetFields(facetFields, searchQuery); + ffcs.addAll(getFacetBucketsForFacetFields(facetFields, searchQuery)); Map>> facetInterval = solrResultSet.getFacetIntervals(); List intervals = getFacetBucketsForIntervals(facetInterval, searchQuery); @@ -308,7 +323,49 @@ public class ResultMapper context = new SearchContext(solrResultSet.getLastIndexedTxId(), facetResults, ffcs, intervals, spellCheckContext, searchQuery.includeRequest()?searchQuery:null); return isNullContext(context)?null:context; } - + /** + * Builds a facet field from facet queries. + * @param facetQueries + * @return + */ + protected List getFacetBucketsFromFacetQueries(Map facetQueries, SearchQuery searchQuery) + { + List facetResults = new ArrayList(); + Map> groups = new HashMap<>(); + String group = null; + + for (Entry fq:facetQueries.entrySet()) + { + String filterQuery = null; + if (searchQuery != null && searchQuery.getFacetQueries() != null) + { + Optional found = searchQuery.getFacetQueries().stream().filter(facetQuery -> fq.getKey().equals(facetQuery.getLabel())).findFirst(); + filterQuery = found.isPresent()? found.get().getQuery():fq.getKey(); + if(found.isPresent() && found.get().getGroup() != null) + { + group= found.get().getGroup(); + } + } + if(group != null && !group.isEmpty()) + { + if(groups.containsKey(group)) + { + groups.get(group).add(new Bucket(fq.getKey(), filterQuery, fq.getValue(), null)); + } + else + { + List l = new ArrayList(); + l.add(new Bucket(fq.getKey(),filterQuery, fq.getValue(),null)); + groups.put(group, l); + } + } + } + if(!groups.isEmpty()) + { + groups.forEach((a,v) -> facetResults.add(new FacetFieldContext(a,v))); + } + return facetResults; + } protected List getFacetBucketsForFacetFields(Map>> facetFields, SearchQuery searchQuery) { if (facetFields != null && !facetFields.isEmpty()) @@ -348,7 +405,7 @@ public class ResultMapper return ffcs; } - return null; + return Collections.emptyList(); } protected List getFacetBucketsForIntervals(Map>> facetFields, SearchQuery searchQuery) diff --git a/source/java/org/alfresco/rest/api/search/model/FacetQuery.java b/source/java/org/alfresco/rest/api/search/model/FacetQuery.java index 34ff0cc073..0a1c60a73d 100644 --- a/source/java/org/alfresco/rest/api/search/model/FacetQuery.java +++ b/source/java/org/alfresco/rest/api/search/model/FacetQuery.java @@ -35,12 +35,16 @@ public class FacetQuery { private final String query; private final String label; + private final String group; @JsonCreator - public FacetQuery(@JsonProperty("query") String query, @JsonProperty("label") String label) + public FacetQuery(@JsonProperty("query") String query, + @JsonProperty("label") String label, + @JsonProperty("group") String group) { this.query = query; this.label = label; + this.group = group; } public String getQuery() @@ -52,4 +56,8 @@ public class FacetQuery { return label; } + public String getGroup() + { + return group; + } } diff --git a/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java b/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java index 8bb27d93f6..e8e9cdd9c7 100644 --- a/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java +++ b/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java @@ -270,7 +270,6 @@ public class ResultMapperTests assertEquals("alfresco",searchContext.getSpellCheck().getSuggestions().get(0)); assertEquals(1, searchContext.getFacetsFields().size()); assertEquals("content.size",searchContext.getFacetsFields().get(0).getLabel()); - assertEquals(5,searchContext.getFacetsFields().get(0).getBuckets().size()); //Facet intervals assertEquals(2, searchContext.getFacetIntervals().size()); @@ -319,6 +318,51 @@ public class ResultMapperTests assertEquals("(",sp.getHighlight().getFields().get(1).getPrefix()); assertEquals(")",sp.getHighlight().getFields().get(1).getPostfix()); } + + @Test + /** + * Test facet group with out facet fields + * @throws Exception + */ + public void testFacetingGroupResponse() throws Exception + { + String jsonQuery = "{\"query\": {\"query\": \"alfresco\"}," + + "\"facetQueries\": [" + + "{\"query\": \"content.size:[o TO 102400]\", \"label\": \"small\",\"group\":\"foo\"}," + + "{\"query\": \"content.size:[102400 TO 1048576]\", \"label\": \"medium\",\"group\":\"foo\"}," + + "{\"query\": \"content.size:[1048576 TO 16777216]\", \"label\": \"large\",\"group\":\"foo\"}]" + + "}"; + + String expectedResponse = "{\"responseHeader\":{\"status\":0,\"QTime\":9},\"_original_parameters_\":\"org.apache.solr.common.params.DefaultSolrParams:{params(df=TEXT&alternativeDic=DEFAULT_DICTIONARY&fl=DBID,score&start=0&fq={!afts}AUTHORITY_FILTER_FROM_JSON&fq={!afts}TENANT_FILTER_FROM_JSON&rows=1000&locale=en_US&wt=json),defaults(carrot.url=id&spellcheck.collateExtendedResults=true&carrot.produceSummary=true&spellcheck.maxCollations=3&spellcheck.maxCollationTries=5&spellcheck.alternativeTermCount=2&spellcheck.extendedResults=false&defType=afts&spellcheck.maxResultsForSuggest=5&spellcheck=false&carrot.outputSubClusters=false&spellcheck.count=5&carrot.title=mltext@m___t@{http://www.alfresco.org/model/content/1.0}title&carrot.snippet=content@s___t@{http://www.alfresco.org/model/content/1.0}content&spellcheck.collate=true)}\",\"_field_mappings_\":{},\"_date_mappings_\":{},\"_range_mappings_\":{},\"_pivot_mappings_\":{},\"_interval_mappings_\":{},\"_stats_field_mappings_\":{},\"_stats_facet_mappings_\":{},\"_facet_function_mappings_\":{},\"response\":{\"numFound\":6,\"start\":0,\"maxScore\":0.7849362,\"docs\":[{\"DBID\":565,\"score\":0.7849362},{\"DBID\":566,\"score\":0.7849362},{\"DBID\":521,\"score\":0.3540957},{\"DBID\":514,\"score\":0.33025497},{\"DBID\":420,\"score\":0.32440513},{\"DBID\":415,\"score\":0.2780319}]}," + + "\"spellcheck\":{\"searchInsteadFor\":\"alfresco\"}," + + "\"facet_counts\":{\"facet_queries\": {\"small\": 52,\"large\": 0,\"medium\": 0}}," + + "\"processedDenies\":true, \"lastIndexedTx\":34}"; + + ResultSet results = mockResultset(expectedResponse); + SearchQuery searchQuery = helper.extractFromJson(jsonQuery); + SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchQuery, 0); + assertEquals(34l, searchContext.getConsistency().getlastTxId()); + assertEquals(null, searchContext.getFacetQueries()); + assertEquals(1, searchContext.getFacetsFields().size()); + assertEquals(3,searchContext.getFacetsFields().get(0).getBuckets().size()); + assertEquals("small",searchContext.getFacetsFields().get(0).getBuckets().get(0).getLabel()); + assertEquals("content.size:[o TO 102400]",searchContext.getFacetsFields().get(0).getBuckets().get(0).getFilterQuery()); + assertNotNull(searchContext.getFacetsFields().get(0).getBuckets().get(0).getCount()); + } + private ResultSet mockResultset(String json) throws Exception + { + NodeService nodeService = mock(NodeService.class); + JSONObject jsonObj = new JSONObject(new JSONTokener(json)); + SearchParameters sp = new SearchParameters(); + sp.setBulkFetchEnabled(false); + ResultSet results = new SolrJSONResultSet(jsonObj, + sp, + nodeService, + null, + LimitBy.FINAL_SIZE, + 10); + return results; + } private ResultSet mockResultset(List archivedNodes, List versionNodes) throws JSONException { diff --git a/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java b/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java index f9c15fc0b7..e747d60010 100644 --- a/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java +++ b/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java @@ -387,14 +387,14 @@ public class SearchMapperTests //Doesn't error searchMapper.fromFacetQuery(searchParameters, null); - searchMapper.fromFacetQuery(searchParameters, Arrays.asList(new FacetQuery("ping", null), new FacetQuery("pong", "table"))); + searchMapper.fromFacetQuery(searchParameters, Arrays.asList(new FacetQuery("ping", null,null), new FacetQuery("pong", "table",null))); assertEquals(2 ,searchParameters.getFacetQueries().size()); assertEquals("{!afts key='ping'}ping" ,searchParameters.getFacetQueries().get(0)); assertEquals("{!afts key='table'}pong" ,searchParameters.getFacetQueries().get(1)); try { - searchMapper.fromFacetQuery(searchParameters, Arrays.asList(new FacetQuery("ping", null),new FacetQuery("{!afts}pang", "tennis"))); + searchMapper.fromFacetQuery(searchParameters, Arrays.asList(new FacetQuery("ping", null,null),new FacetQuery("{!afts}pang", "tennis",null))); fail(); } catch (InvalidArgumentException iae) @@ -655,4 +655,9 @@ public class SearchMapperTests SearchQuery sq = new SearchQuery(query, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); return sq; } + @Test + public void facetGroup() + { + + } } diff --git a/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java b/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java index ef3b5b8f88..f5356c0d36 100644 --- a/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java +++ b/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java @@ -94,7 +94,7 @@ public class SerializerTestHelper implements RequestReader } } - private SearchQuery extractFromJson(String json) throws IOException + public SearchQuery extractFromJson(String json) throws IOException { Content content = mock(Content.class); when(content.getReader()).thenReturn(new StringReader(json));