From 5fd392b1ce792033e53f853e4fdca6e52dcbdbc5 Mon Sep 17 00:00:00 2001 From: Alexandru Epure Date: Tue, 9 Aug 2016 14:16:40 +0000 Subject: [PATCH] Merged 5.2.N (5.2.1) to HEAD (5.2) 129193 mmuller: Merged RETURN-OF-THE-API (5.2.0) to 5.2.N (5.2.1) 129042 jvonka: V1 REST API: Sites Live Search (/queries/live-search-sites) - add experimental option (for review) to choose sort type - fix post query sort to use AlfrescoCollator rather than RuleBasedCollator (latter ignores spaces) - fix post query sort to apply paging correctly (get results first) - TODO add test REPO-232, REPO-882, REPO-883 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@129367 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../java/org/alfresco/rest/api/Queries.java | 6 +- .../alfresco/rest/api/impl/QueriesImpl.java | 107 ++++++++------ .../rest/api/tests/QueriesSitesApiTest.java | 130 ++++++++++++++++++ 3 files changed, 200 insertions(+), 43 deletions(-) diff --git a/source/java/org/alfresco/rest/api/Queries.java b/source/java/org/alfresco/rest/api/Queries.java index 09308b2980..d7459a25b3 100644 --- a/source/java/org/alfresco/rest/api/Queries.java +++ b/source/java/org/alfresco/rest/api/Queries.java @@ -63,7 +63,11 @@ public interface Queries static int MIN_TERM_LENGTH_PEOPLE = 2; // Sites query - static int MIN_TERM_LENGTH_SITES = 2; + static String PARAM_SITE_ID = "id"; + static String PARAM_SITE_TITLE = "title"; + static String PARAM_SITE_DESCRIPTION = "description"; + static int MIN_TERM_LENGTH_SITES = 2; + static String PARAM_SORT_TYPE = "sortType"; // TODO review /** * Find Nodes diff --git a/source/java/org/alfresco/rest/api/impl/QueriesImpl.java b/source/java/org/alfresco/rest/api/impl/QueriesImpl.java index 5f4e8a439f..87a190d52f 100644 --- a/source/java/org/alfresco/rest/api/impl/QueriesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/QueriesImpl.java @@ -43,6 +43,7 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; +import org.alfresco.repo.site.SiteModel; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.People; import org.alfresco.rest.api.Queries; @@ -72,8 +73,10 @@ import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.alfresco.util.AlfrescoCollator; import org.alfresco.util.ISO9075; import org.alfresco.util.ParameterCheck; +import org.alfresco.util.SearchLanguageConversion; import org.springframework.beans.factory.InitializingBean; import org.springframework.extensions.surf.util.I18NUtil; @@ -95,6 +98,11 @@ public class QueriesImpl implements Queries, InitializingBean ContentModel.PROP_FIRSTNAME, ContentModel.PROP_LASTNAME); + private final static Map SITE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames( + PARAM_SITE_ID, ContentModel.PROP_NAME, + PARAM_SITE_TITLE, ContentModel.PROP_TITLE, + PARAM_SITE_DESCRIPTION, ContentModel.PROP_DESCRIPTION); + /** * Helper method to build a map of sort parameter names to QNames. This method iterates through * the parameters. If a parameter is a String it is assumed to be a sort parameter name and will @@ -307,15 +315,46 @@ public class QueriesImpl implements Queries, InitializingBean @Override public CollectionWithPagingInfo findSites(Parameters parameters) { - // TODO Alternatively do we want to implement our own find rather than rely on SiteService ?! + // TODO review + AbstractQuery.Sort sortType = IN_QUERY_SORT; + String sortTypeStr = parameters.getParameter(PARAM_SORT_TYPE); + if (sortTypeStr != null) { + if (sortTypeStr.equalsIgnoreCase("in-query")) + { + sortType = IN_QUERY_SORT; + } + else if (sortTypeStr.equalsIgnoreCase("post-query")) + { + sortType = POST_QUERY_SORT; + } + else + { + throw new IllegalArgumentException("Unexpected sortType: "+sortTypeStr+" (expected in-query or post-query)"); + } + } + return new AbstractQuery(nodeService, searchService) { @Override protected void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName) { - // not used + sp.addQueryTemplate(queryTemplateName, "%(cm:name cm:title cm:description)"); + sp.setExcludeTenantFilter(false); + sp.setPermissionEvaluation(PermissionEvaluationMode.EAGER); + + query.append("TYPE:\"").append(SiteModel.TYPE_SITE).append("\" AND (\"*"); + query.append(term); + query.append("*\")"); } - + + @Override + protected String getTerm(Parameters parameters, String termName, int minTermLength) + { + String filter = super.getTerm(parameters, termName, minTermLength); + String escNameFilter = SearchLanguageConversion.escapeLuceneQuery(filter.replace('"', ' ')); + return escNameFilter; + } + @Override protected List newList(int capacity) { @@ -325,34 +364,10 @@ public class QueriesImpl implements Queries, InitializingBean @Override protected Site convert(NodeRef nodeRef, List includeParam) { - // not used - return null; + return getSite(siteService.getSite(nodeRef), true); } - @Override - public CollectionWithPagingInfo find(Parameters parameters, String termName, int minTermLength, - String queryTemplateName, - Sort sort, Map sortParamsToQNames, SortColumn... defaultSort) - { - Paging paging = parameters.getPaging(); - String term = getTerm(parameters, termName, minTermLength); - - // TODO optimise paging - // TODO implement sorting (see open-question) - List siteInfos = siteService.findSites(term, Integer.MAX_VALUE); - List collection = newList(siteInfos.size()); - - for (SiteInfo siteInfo : siteInfos) - { - // convert - Site s = getSite(siteInfo, true); - collection.add(s); - } - - return listPage(collection, paging); - } - - }.find(parameters, PARAM_TERM, MIN_TERM_LENGTH_SITES, null, null, null, null); + }.find(parameters, PARAM_TERM, MIN_TERM_LENGTH_SITES, "_SITE", sortType, SITE_SORT_PARAMS_TO_QNAMES, new SortColumn(PARAM_SITE_TITLE, true)); } // note: see also Sites.getSite @@ -398,18 +413,19 @@ public class QueriesImpl implements Queries, InitializingBean StringBuilder query = new StringBuilder(); buildQuery(query, term, sp, queryTemplateName); sp.setQuery(query.toString()); - - List defaultSortCols = Arrays.asList(defaultSort); - if (sort == IN_QUERY_SORT) - { - addSortOrder(parameters, sortParamsToQNames, defaultSortCols, sp); - } Paging paging = parameters.getPaging(); PagingRequest pagingRequest = Util.getPagingRequest(paging); - sp.setSkipCount(pagingRequest.getSkipCount()); - sp.setMaxItems(pagingRequest.getMaxItems()); + + List defaultSortCols = (defaultSort != null ? Arrays.asList(defaultSort) : Collections.emptyList()); + if (sort == IN_QUERY_SORT) + { + addSortOrder(parameters, sortParamsToQNames, defaultSortCols, sp); + sp.setSkipCount(pagingRequest.getSkipCount()); + sp.setMaxItems(pagingRequest.getMaxItems()); + } + ResultSet queryResults = null; List collection = null; try @@ -439,8 +455,15 @@ public class QueriesImpl implements Queries, InitializingBean queryResults.close(); } } - - return CollectionWithPagingInfo.asPaged(paging, collection, queryResults.hasMore(), new Long(queryResults.getNumberFound()).intValue()); + + if (sort == POST_QUERY_SORT) + { + return listPage(collection, paging); + } + else + { + return CollectionWithPagingInfo.asPaged(paging, collection, queryResults.hasMore(), new Long(queryResults.getNumberFound()).intValue()); + } } /** @@ -549,7 +572,7 @@ public class QueriesImpl implements Queries, InitializingBean sortPropQNames.add(sortPropQName); } - final Collator col = Collator.getInstance(I18NUtil.getLocale()); + final Collator col = AlfrescoCollator.getInstance(I18NUtil.getLocale()); Collections.sort(nodeRefs, new Comparator() { @Override @@ -566,7 +589,7 @@ public class QueriesImpl implements Queries, InitializingBean result = ((p1 instanceof Long) && (p2 instanceof Long) ? Long.compare((Long)p1, (Long)p2) - : col.compare(p1.toString(), p2)) + : col.compare(p1.toString(), p2.toString())) * (sortCol.asc ? 1 : -1); if (result != 0) @@ -590,7 +613,7 @@ public class QueriesImpl implements Queries, InitializingBean } // note: see also AbstractNodeRelation - protected CollectionWithPagingInfo listPage(List result, Paging paging) + protected static CollectionWithPagingInfo listPage(List result, Paging paging) { // return 'page' of results (based on full result set) int skipCount = paging.getSkipCount(); diff --git a/source/test-java/org/alfresco/rest/api/tests/QueriesSitesApiTest.java b/source/test-java/org/alfresco/rest/api/tests/QueriesSitesApiTest.java index 6d7a282ac2..9233644a6b 100644 --- a/source/test-java/org/alfresco/rest/api/tests/QueriesSitesApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/QueriesSitesApiTest.java @@ -35,6 +35,7 @@ import org.alfresco.service.cmr.site.SiteVisibility; import org.junit.Test; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -210,6 +211,135 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest } } + @Test + public void testLiveSearchSites_Sort() throws Exception + { + setRequestContext(user1); + + List siteIds = new ArrayList<>(5); + + try + { + // As user 1 ... + + Paging paging = getPaging(0, 100); + + // create site + String s1 = createSite("siABCDEF", "ABCDEF DEF", "sdABCDEF", SiteVisibility.PRIVATE, 201).getId(); + String s2 = createSite("siABCD", "ABCD DEF", "sdABCD", SiteVisibility.PRIVATE, 201).getId(); + String s3 = createSite("siABCDE", "ABCDE DEF", "sdABCDE", SiteVisibility.PRIVATE, 201).getId(); + String s4 = createSite("siAB", "AB DEF", "sdAB", SiteVisibility.PRIVATE, 201).getId(); + String s5 = createSite("siABC", "ABC DEF", "sdABC", SiteVisibility.PRIVATE, 201).getId(); + + siteIds.addAll(Arrays.asList(new String[] {s1, s2, s3, s4, s5})); + + int sCount = siteIds.size(); + + // test sort order + + // TODO agree and test default sort order + + // sort order - id asc + Map params = new HashMap<>(1); + params.put(Queries.PARAM_TERM, "siAB"); + params.put(Queries.PARAM_ORDERBY, "id asc"); + params.put("sortType", "in-query"); + HttpResponse response = getAll(URL_QUERIES_LSS, paging, params, 200); + List sites = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Site.class); + assertEquals(sCount, sites.size()); + assertEquals(Arrays.asList(new String[] {s4, s5, s2, s3, s1}), getSiteIds(sites)); + + // sort order - id desc + params = new HashMap<>(1); + params.put(Queries.PARAM_TERM, "siAB"); + params.put(Queries.PARAM_ORDERBY, "id desc"); + params.put("sortType", "in-query"); + response = getAll(URL_QUERIES_LSS, paging, params, 200); + sites = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Site.class); + assertEquals(sCount, sites.size()); + assertEquals(Arrays.asList(new String[] {s1, s3, s2, s5, s4}), getSiteIds(sites)); + + // sort order - title asc + params = new HashMap<>(1); + params.put(Queries.PARAM_TERM, "siAB"); + params.put(Queries.PARAM_ORDERBY, "title asc"); + params.put("sortType", "in-query"); + response = getAll(URL_QUERIES_LSS, paging, params, 200); + sites = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Site.class); + assertEquals(sCount, sites.size()); + assertEquals(Arrays.asList(new String[] {s2, s3, s5, s1, s4}), getSiteIds(sites)); + + // sort order - title desc + params = new HashMap<>(1); + params.put(Queries.PARAM_TERM, "siAB"); + params.put(Queries.PARAM_ORDERBY, "title desc"); + params.put("sortType", "in-query"); + response = getAll(URL_QUERIES_LSS, paging, params, 200); + sites = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Site.class); + assertEquals(sCount, sites.size()); + assertEquals(Arrays.asList(new String[] {s4, s1, s5, s3, s2}), getSiteIds(sites)); + + // sort order - title asc (post query - using Alfresco Collator which does not ignore spaces, unlike default RuleBasedCollator) + params = new HashMap<>(1); + params.put(Queries.PARAM_TERM, "siAB"); + params.put(Queries.PARAM_ORDERBY, "title asc"); + params.put("sortType", "post-query"); + response = getAll(URL_QUERIES_LSS, paging, params, 200); + sites = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Site.class); + assertEquals(sCount, sites.size()); + assertEquals(Arrays.asList(new String[] {s4, s5, s2, s3, s1}), getSiteIds(sites)); + + // sort order - title desc (post query - using Alfresco Collator which does not ignore spaces, unlike default RuleBasedCollator) + params = new HashMap<>(1); + params.put(Queries.PARAM_TERM, "siAB"); + params.put(Queries.PARAM_ORDERBY, "title desc"); + params.put("sortType", "post-query"); + response = getAll(URL_QUERIES_LSS, paging, params, 200); + sites = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Site.class); + assertEquals(sCount, sites.size()); + assertEquals(Arrays.asList(new String[] {s1, s3, s2, s5, s4}), getSiteIds(sites)); + + // sort order - description asc + params = new HashMap<>(1); + params.put(Queries.PARAM_TERM, "siAB"); + params.put(Queries.PARAM_ORDERBY, "description asc"); + params.put("sortType", "in-query"); + response = getAll(URL_QUERIES_LSS, paging, params, 200); + sites = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Site.class); + assertEquals(sCount, sites.size()); + assertEquals(Arrays.asList(new String[] {s4, s5, s2, s3, s1}), getSiteIds(sites)); + + // sort order - description desc + params = new HashMap<>(1); + params.put(Queries.PARAM_TERM, "siAB"); + params.put(Queries.PARAM_ORDERBY, "description desc"); + params.put("sortType", "in-query"); + response = getAll(URL_QUERIES_LSS, paging, params, 200); + sites = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Site.class); + assertEquals(sCount, sites.size()); + assertEquals(Arrays.asList(new String[] {s1, s3, s2, s5, s4}), getSiteIds(sites)); + } + finally + { + // some cleanup + setRequestContext(user1); + for (String siteId : siteIds) + { + deleteSite(siteId, true, 204); + } + } + } + + private List getSiteIds(List sites) + { + List siteIds = new ArrayList<>(sites.size()); + for (Site site : sites) + { + siteIds.add(site.getId()); + } + return siteIds; + } + @Override public String getScope() {