From f3cb1c224dbd1efb7be462ae61df38a7906e7718 Mon Sep 17 00:00:00 2001 From: Gethin James Date: Tue, 6 Sep 2016 14:18:46 +0000 Subject: [PATCH] Merged searchapi (5.2.1) to 5.2.N (5.2.1) 129808 gjames: SEARCH-112: Adding SearchContext to the response git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@130178 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../rest/api/search/impl/ResultMapper.java | 47 +++++++++++---- .../SerializerOfCollectionWithPaging.java | 12 +++- .../resource/SerializablePagedCollection.java | 6 ++ .../parameters/CollectionWithPagingInfo.java | 39 +++++++++--- .../resource/parameters/SearchContext.java | 59 +++++++++++++++++++ .../webscripts/ResourceWebScriptHelper.java | 3 +- .../rest/api/search/ResultMapperTests.java | 3 +- .../framework/tests/core/SerializeTests.java | 17 +++++- 8 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 source/java/org/alfresco/rest/framework/resource/parameters/SearchContext.java 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 2ef6c9c7dc..0e88a10004 100644 --- a/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java +++ b/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java @@ -26,29 +26,21 @@ package org.alfresco.rest.api.search.impl; -import org.alfresco.model.ContentModel; +import org.alfresco.repo.search.impl.lucene.PagingLuceneResultSet; +import org.alfresco.repo.search.impl.lucene.SolrJSONResultSet; +import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.model.Node; -import org.alfresco.rest.api.model.UserInfo; import org.alfresco.rest.api.search.model.SearchEntry; import org.alfresco.rest.api.search.model.SearchQuery; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; -import org.alfresco.rest.framework.resource.parameters.Paging; +import org.alfresco.rest.framework.resource.parameters.SearchContext; import org.alfresco.rest.framework.resource.parameters.Params; -import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.namespace.QName; import org.alfresco.util.ParameterCheck; -import org.apache.commons.lang.NotImplementedException; -import java.io.Serializable; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Map; - -import static org.alfresco.rest.api.Nodes.PARAM_INCLUDE_ASPECTNAMES; -import static org.alfresco.rest.api.Nodes.PARAM_INCLUDE_PROPERTIES; /** * Maps from a Solr ResultSet to a json public api representation. @@ -75,6 +67,7 @@ public class ResultMapper public CollectionWithPagingInfo toCollectionWithPagingInfo(Params params, ResultSet results) { SearchQuery searchQuery = (SearchQuery) params.getPassedIn(); + SearchContext context = null; Long totalItems = results.getNumberFound(); List noderesults = new ArrayList(); @@ -89,7 +82,35 @@ public class ResultMapper Integer total = Integer.valueOf(totalItems.intValue()); int skip = searchQuery.getPaging()==null?0:searchQuery.getPaging().getSkipCount(); - return CollectionWithPagingInfo.asPaged(searchQuery.getPaging(), noderesults, noderesults.size() + skip < total, total); + + SolrJSONResultSet jsonResultSet = findJsonResults(results); + + if (jsonResultSet != null) + { + context = new SearchContext(jsonResultSet.getLastIndexedTxId()); + } + + return CollectionWithPagingInfo.asPaged(searchQuery.getPaging(), noderesults, noderesults.size() + skip < total, total, null, context); } + /** + * Gets SolrJSONResultSet class if there is one. + * @param results + * @return + */ + protected SolrJSONResultSet findJsonResults(ResultSet results) + { + //This may get more complicated if the results are wrapped in another ResultSet class + if (results instanceof SolrJSONResultSet) + { + return (SolrJSONResultSet) results; + } +/** + if (results instanceof PagingLuceneResultSet) + { + return findJsonResults(((PagingLuceneResultSet) results).getWrapped()); + } +**/ + return null; + } } diff --git a/source/java/org/alfresco/rest/framework/jacksonextensions/SerializerOfCollectionWithPaging.java b/source/java/org/alfresco/rest/framework/jacksonextensions/SerializerOfCollectionWithPaging.java index 440ce789b7..846fe44a49 100644 --- a/source/java/org/alfresco/rest/framework/jacksonextensions/SerializerOfCollectionWithPaging.java +++ b/source/java/org/alfresco/rest/framework/jacksonextensions/SerializerOfCollectionWithPaging.java @@ -61,7 +61,8 @@ public class SerializerOfCollectionWithPaging extends SerializerBase * The requested paging parameters set by the client */ Paging getPaging(); + + /** + * The search context for the collection + */ + SearchContext getContext(); } diff --git a/source/java/org/alfresco/rest/framework/resource/parameters/CollectionWithPagingInfo.java b/source/java/org/alfresco/rest/framework/resource/parameters/CollectionWithPagingInfo.java index 99d62680ef..0d87fc9909 100644 --- a/source/java/org/alfresco/rest/framework/resource/parameters/CollectionWithPagingInfo.java +++ b/source/java/org/alfresco/rest/framework/resource/parameters/CollectionWithPagingInfo.java @@ -50,6 +50,7 @@ public class CollectionWithPagingInfo implements SerializablePagedCollection private final Integer totalItems; private final Paging paging; private final Object sourceEntity; + private final SearchContext context; /** * Constructs a new CollectionWithPagingInfo. @@ -58,7 +59,7 @@ public class CollectionWithPagingInfo implements SerializablePagedCollection * @param hasMoreItems - Are there more items after this Collection? * @param totalItems - The total number of items available. */ - protected CollectionWithPagingInfo(Collection collection, Paging paging, boolean hasMoreItems, Integer totalItems, Object sourceEntity) + protected CollectionWithPagingInfo(Collection collection, Paging paging, boolean hasMoreItems, Integer totalItems, Object sourceEntity, SearchContext context) { super(); this.hasMoreItems = hasMoreItems; @@ -75,8 +76,9 @@ public class CollectionWithPagingInfo implements SerializablePagedCollection this.totalItems = totalItems; } this.sourceEntity = sourceEntity; + this.context = context; } - + /** * Constructs a new CollectionWithPagingInfo. * It automatically sets the total items based on the collection size and @@ -89,7 +91,7 @@ public class CollectionWithPagingInfo implements SerializablePagedCollection public static CollectionWithPagingInfo asPaged(Paging paging, Collection aCollection) { int collectionSize = aCollection==null?0:aCollection.size(); - return new CollectionWithPagingInfo(aCollection, paging, false, collectionSize, null); + return new CollectionWithPagingInfo(aCollection, paging, false, collectionSize, null, null); } /** @@ -103,7 +105,7 @@ public class CollectionWithPagingInfo implements SerializablePagedCollection public static CollectionWithPagingInfo asPagedCollection(T ...entity) { Collection aNewCollection = Arrays.asList(entity); - return new CollectionWithPagingInfo(aNewCollection, Paging.DEFAULT, false, aNewCollection.size(), null); + return new CollectionWithPagingInfo(aNewCollection, Paging.DEFAULT, false, aNewCollection.size(), null, null); } /** @@ -117,7 +119,22 @@ public class CollectionWithPagingInfo implements SerializablePagedCollection */ public static CollectionWithPagingInfo asPaged(Paging paging, Collection aCollection, boolean hasMoreItems, Integer totalItems) { - return new CollectionWithPagingInfo(aCollection, paging, hasMoreItems, totalItems, null); + return new CollectionWithPagingInfo(aCollection, paging, hasMoreItems, totalItems, null, null); + } + + /** + * Constructs a new CollectionWithPagingInfo. Not for public use. + * + * @param paging - Paging request info + * @param aCollection - the collection that needs to be paged. + * @param hasMoreItems - Are there more items after this Collection? + * @param totalItems - The total number of items available. + * @param sourceEntity - The parent/source entity responsible for the collection + * @return CollectionWithPagingInfo + */ + public static CollectionWithPagingInfo asPaged(Paging paging, Collection aCollection, boolean hasMoreItems, Integer totalItems, Object sourceEntity) + { + return new CollectionWithPagingInfo(aCollection, paging, hasMoreItems, totalItems, sourceEntity, null); } /** @@ -128,11 +145,12 @@ public class CollectionWithPagingInfo implements SerializablePagedCollection * @param hasMoreItems - Are there more items after this Collection? * @param totalItems - The total number of items available. * @param sourceEntity - The parent/source entity responsible for the collection + * @param context - The search context * @return CollectionWithPagingInfo */ - public static CollectionWithPagingInfo asPaged(Paging paging, Collection aCollection, boolean hasMoreItems, Integer totalItems, Object sourceEntity) + public static CollectionWithPagingInfo asPaged(Paging paging, Collection aCollection, boolean hasMoreItems, Integer totalItems, Object sourceEntity, SearchContext context) { - return new CollectionWithPagingInfo(aCollection, paging, hasMoreItems, totalItems, sourceEntity); + return new CollectionWithPagingInfo(aCollection, paging, hasMoreItems, totalItems, sourceEntity, context); } /** @@ -183,4 +201,11 @@ public class CollectionWithPagingInfo implements SerializablePagedCollection { return this.paging; } + + @Override + public SearchContext getContext() + { + return context; + } + } diff --git a/source/java/org/alfresco/rest/framework/resource/parameters/SearchContext.java b/source/java/org/alfresco/rest/framework/resource/parameters/SearchContext.java new file mode 100644 index 0000000000..2bba6ae82c --- /dev/null +++ b/source/java/org/alfresco/rest/framework/resource/parameters/SearchContext.java @@ -0,0 +1,59 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.framework.resource.parameters; + +/** + * The contextual results of a Search + */ +public class SearchContext +{ + Consistency consistency; + + public SearchContext(long lastTxId) + { + consistency = new Consistency(lastTxId); + } + + public Consistency getConsistency() + { + return consistency; + } + + public class Consistency + { + private long lastTxId; + + public Consistency(long lastTxId) + { + this.lastTxId = lastTxId; + } + + public long getlastTxId() + { + return lastTxId; + } + } +} diff --git a/source/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptHelper.java b/source/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptHelper.java index a627f1f7e7..d487f80998 100644 --- a/source/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptHelper.java +++ b/source/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptHelper.java @@ -174,7 +174,8 @@ public class ResourceWebScriptHelper resultCollection.add(processAdditionsToTheResponse(res, api,entityCollectionName,params,obj)); } } - return CollectionWithPagingInfo.asPaged(collectionToWrap.getPaging(), resultCollection, collectionToWrap.hasMoreItems(), collectionToWrap.getTotalItems(), sourceEntity); + return CollectionWithPagingInfo.asPaged(collectionToWrap.getPaging(), resultCollection, collectionToWrap.hasMoreItems(), + collectionToWrap.getTotalItems(), sourceEntity, collectionToWrap.getContext()); } else { 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 fee5491613..b73c33db50 100644 --- a/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java +++ b/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java @@ -85,7 +85,7 @@ import java.util.Map; public class ResultMapperTests { static ResultMapper mapper; - public static final String JSON_REPONSE = "{\"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}]},\"processedDenies\":true}"; + public static final String JSON_REPONSE = "{\"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}]},\"processedDenies\":true, \"lastIndexedTx\":34}"; @BeforeClass public static void setupTests() throws Exception @@ -127,6 +127,7 @@ public class ResultMapperTests assertEquals(found.intValue(), collectionWithPage.getTotalItems().intValue()); Node firstNode = collectionWithPage.getCollection().stream().findFirst().get(); assertNotNull(firstNode.getSearch().getScore()); + assertEquals(34l, collectionWithPage.getContext().getConsistency().getlastTxId()); } private ResultSet mockResultset() throws JSONException diff --git a/source/test-java/org/alfresco/rest/framework/tests/core/SerializeTests.java b/source/test-java/org/alfresco/rest/framework/tests/core/SerializeTests.java index b937c6d3c2..3e23008cf9 100644 --- a/source/test-java/org/alfresco/rest/framework/tests/core/SerializeTests.java +++ b/source/test-java/org/alfresco/rest/framework/tests/core/SerializeTests.java @@ -49,6 +49,7 @@ import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartResource import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; import org.alfresco.rest.framework.resource.content.BinaryProperty; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.SearchContext; import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Params; import org.alfresco.rest.framework.tests.api.mocks.Farmer; @@ -59,7 +60,6 @@ import org.alfresco.rest.framework.tests.api.mocks3.Flock; import org.alfresco.rest.framework.tests.api.mocks3.SlimGoat; import org.alfresco.rest.framework.tools.RecognizedParamsExtractor; import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript; -import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.util.GUID; import org.alfresco.util.TempFileProvider; @@ -236,6 +236,21 @@ public class SerializeTests extends AbstractContextTest implements RecognizedPar assertTrue("There must 'source' json output", StringUtils.contains(out, "\"source\":{\"name\":\"Dolly\",\"age\":3,\"sheepGuid\":\"barbie\"")); } + + @Test + public void testIncludeContext() throws IOException + { + ExecutionResult exec1 = new ExecutionResult(new Farmer("180"),null); + ExecutionResult exec2 = new ExecutionResult(new Farmer("456"),getFilter("age")); + SearchContext searchContext = new SearchContext(23l); + CollectionWithPagingInfo coll = CollectionWithPagingInfo.asPaged(null, Arrays.asList(exec1, exec2), false, 2, null, searchContext); + Object resultCollection = helper.processAdditionsToTheResponse(mock(WebScriptResponse.class), api, null,ParamsExtender.valueOf(false,null),coll); + assertNotNull(resultCollection); + String out = writeResponse(resultCollection); + assertTrue("There must 'context' json output", StringUtils.contains(out, "\"context\":{\"consistency\":{\"lastTxId\":23}}")); + + } + @Test public void testExpandRecursiveRelations() throws IOException {