Merged searchbcr (5.2.1) to 5.2.N (5.2.1)

135953 msuzuki: SEARCH-374, added new response to display group faceting in buckets


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@136097 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gethin James
2017-03-24 13:17:55 +00:00
parent 962082d4c4
commit 86a0ed3479
5 changed files with 138 additions and 24 deletions

View File

@@ -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.DELETED;
import static org.alfresco.rest.api.search.impl.StoreMapper.LIVE_NODES; import static org.alfresco.rest.api.search.impl.StoreMapper.LIVE_NODES;
import static org.alfresco.rest.api.search.impl.StoreMapper.VERSIONS; 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.search.impl.lucene.SolrJSONResultSet;
import org.alfresco.repo.version.Version2Model; import org.alfresco.repo.version.Version2Model;
import org.alfresco.rest.api.DeletedNodes; 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.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import java.io.Serializable; import com.google.gdata.data.introspection.Collection;
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;
/** /**
* Maps from a ResultSet to a json public api representation. * Maps from a ResultSet to a json public api representation.
@@ -131,7 +135,7 @@ public class ResultMapper
{ {
SearchContext context = null; SearchContext context = null;
Integer total = null; Integer total = null;
List<Node> noderesults = new ArrayList(); List<Node> noderesults = new ArrayList<Node>();
Map<String, UserInfo> mapUserInfo = new HashMap<>(10); Map<String, UserInfo> mapUserInfo = new HashMap<>(10);
Map<NodeRef, List<Pair<String, List<String>>>> hightLighting = results.getHighlighting(); Map<NodeRef, List<Pair<String, List<String>>>> hightLighting = results.getHighlighting();
int notFound = 0; int notFound = 0;
@@ -272,27 +276,38 @@ public class ResultMapper
Map<String, Integer> facetQueries = solrResultSet.getFacetQueries(); Map<String, Integer> facetQueries = solrResultSet.getFacetQueries();
List<FacetQueryContext> facetResults = null; List<FacetQueryContext> facetResults = null;
SpellCheckContext spellCheckContext = null; SpellCheckContext spellCheckContext = null;
List<FacetFieldContext> ffcs = null; List<FacetFieldContext> ffcs = new ArrayList<FacetFieldContext>();
//Facet queries //Facet queries
if(facetQueries!= null && !facetQueries.isEmpty()) if(facetQueries!= null && !facetQueries.isEmpty())
{ {
facetResults = new ArrayList<>(facetQueries.size()); //If group by field populated in query facet return bucketing into facet field.
for (Entry<String, Integer> fq:facetQueries.entrySet()) List<FacetFieldContext> facetQueryForFields =
getFacetBucketsFromFacetQueries(facetQueries,searchQuery);
if(!facetQueryForFields.isEmpty())
{ {
String filterQuery = null; ffcs.addAll(facetQueryForFields);
if (searchQuery != null && searchQuery.getFacetQueries() != null) }
else
{
// Return the old way facet query with no bucketing.
facetResults = new ArrayList<>(facetQueries.size());
for (Entry<String, Integer> fq:facetQueries.entrySet())
{ {
Optional<FacetQuery> found = searchQuery.getFacetQueries().stream().filter(facetQuery -> fq.getKey().equals(facetQuery.getLabel())).findFirst(); String filterQuery = null;
filterQuery = found.isPresent()? found.get().getQuery():fq.getKey(); if (searchQuery != null && searchQuery.getFacetQueries() != null)
{
Optional<FacetQuery> 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 //Field Facets
Map<String, List<Pair<String, Integer>>> facetFields = solrResultSet.getFieldFacets(); Map<String, List<Pair<String, Integer>>> facetFields = solrResultSet.getFieldFacets();
ffcs = getFacetBucketsForFacetFields(facetFields, searchQuery); ffcs.addAll(getFacetBucketsForFacetFields(facetFields, searchQuery));
Map<String, List<Pair<String, Integer>>> facetInterval = solrResultSet.getFacetIntervals(); Map<String, List<Pair<String, Integer>>> facetInterval = solrResultSet.getFacetIntervals();
List<FacetFieldContext> intervals = getFacetBucketsForIntervals(facetInterval, searchQuery); List<FacetFieldContext> intervals = getFacetBucketsForIntervals(facetInterval, searchQuery);
@@ -308,7 +323,49 @@ public class ResultMapper
context = new SearchContext(solrResultSet.getLastIndexedTxId(), facetResults, ffcs, intervals, spellCheckContext, searchQuery.includeRequest()?searchQuery:null); context = new SearchContext(solrResultSet.getLastIndexedTxId(), facetResults, ffcs, intervals, spellCheckContext, searchQuery.includeRequest()?searchQuery:null);
return isNullContext(context)?null:context; return isNullContext(context)?null:context;
} }
/**
* Builds a facet field from facet queries.
* @param facetQueries
* @return
*/
protected List<FacetFieldContext> getFacetBucketsFromFacetQueries(Map<String, Integer> facetQueries, SearchQuery searchQuery)
{
List<FacetFieldContext> facetResults = new ArrayList<FacetFieldContext>();
Map<String,List<Bucket>> groups = new HashMap<>();
String group = null;
for (Entry<String, Integer> fq:facetQueries.entrySet())
{
String filterQuery = null;
if (searchQuery != null && searchQuery.getFacetQueries() != null)
{
Optional<FacetQuery> 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<Bucket> l = new ArrayList<Bucket>();
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<FacetFieldContext> getFacetBucketsForFacetFields(Map<String, List<Pair<String, Integer>>> facetFields, SearchQuery searchQuery) protected List<FacetFieldContext> getFacetBucketsForFacetFields(Map<String, List<Pair<String, Integer>>> facetFields, SearchQuery searchQuery)
{ {
if (facetFields != null && !facetFields.isEmpty()) if (facetFields != null && !facetFields.isEmpty())
@@ -348,7 +405,7 @@ public class ResultMapper
return ffcs; return ffcs;
} }
return null; return Collections.emptyList();
} }
protected List<FacetFieldContext> getFacetBucketsForIntervals(Map<String, List<Pair<String, Integer>>> facetFields, SearchQuery searchQuery) protected List<FacetFieldContext> getFacetBucketsForIntervals(Map<String, List<Pair<String, Integer>>> facetFields, SearchQuery searchQuery)

View File

@@ -35,12 +35,16 @@ public class FacetQuery
{ {
private final String query; private final String query;
private final String label; private final String label;
private final String group;
@JsonCreator @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.query = query;
this.label = label; this.label = label;
this.group = group;
} }
public String getQuery() public String getQuery()
@@ -52,4 +56,8 @@ public class FacetQuery
{ {
return label; return label;
} }
public String getGroup()
{
return group;
}
} }

View File

@@ -270,7 +270,6 @@ public class ResultMapperTests
assertEquals("alfresco",searchContext.getSpellCheck().getSuggestions().get(0)); assertEquals("alfresco",searchContext.getSpellCheck().getSuggestions().get(0));
assertEquals(1, searchContext.getFacetsFields().size()); assertEquals(1, searchContext.getFacetsFields().size());
assertEquals("content.size",searchContext.getFacetsFields().get(0).getLabel()); assertEquals("content.size",searchContext.getFacetsFields().get(0).getLabel());
assertEquals(5,searchContext.getFacetsFields().get(0).getBuckets().size());
//Facet intervals //Facet intervals
assertEquals(2, searchContext.getFacetIntervals().size()); 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).getPrefix());
assertEquals(")",sp.getHighlight().getFields().get(1).getPostfix()); 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<Long> archivedNodes, List<Long> versionNodes) throws JSONException private ResultSet mockResultset(List<Long> archivedNodes, List<Long> versionNodes) throws JSONException
{ {

View File

@@ -387,14 +387,14 @@ public class SearchMapperTests
//Doesn't error //Doesn't error
searchMapper.fromFacetQuery(searchParameters, null); 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(2 ,searchParameters.getFacetQueries().size());
assertEquals("{!afts key='ping'}ping" ,searchParameters.getFacetQueries().get(0)); assertEquals("{!afts key='ping'}ping" ,searchParameters.getFacetQueries().get(0));
assertEquals("{!afts key='table'}pong" ,searchParameters.getFacetQueries().get(1)); assertEquals("{!afts key='table'}pong" ,searchParameters.getFacetQueries().get(1));
try 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(); fail();
} }
catch (InvalidArgumentException iae) 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); SearchQuery sq = new SearchQuery(query, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
return sq; return sq;
} }
@Test
public void facetGroup()
{
}
} }

View File

@@ -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); Content content = mock(Content.class);
when(content.getReader()).thenReturn(new StringReader(json)); when(content.getReader()).thenReturn(new StringReader(json));