diff --git a/source/java/org/alfresco/rest/api/search/context/SearchContext.java b/source/java/org/alfresco/rest/api/search/context/SearchContext.java index b3436c4fa0..a676b49150 100644 --- a/source/java/org/alfresco/rest/api/search/context/SearchContext.java +++ b/source/java/org/alfresco/rest/api/search/context/SearchContext.java @@ -36,8 +36,9 @@ public class SearchContext private final List facetQueries; private final SpellCheckContext spellCheck; private final List facetsFields; + private final List facetIntervals; - public SearchContext(long lastTxId, List facetQueries, List facetsFields, SpellCheckContext spellCheck) + public SearchContext(long lastTxId, List facetQueries, List facetsFields, List facetIntervals, SpellCheckContext spellCheck) { this.spellCheck = spellCheck; if (lastTxId > 0) @@ -50,6 +51,7 @@ public class SearchContext } this.facetQueries = facetQueries; this.facetsFields = facetsFields; + this.facetIntervals = facetIntervals; } public Consistency getConsistency() @@ -67,6 +69,11 @@ public class SearchContext return spellCheck; } + public List getFacetIntervals() + { + return facetIntervals; + } + public List getFacetsFields() { return facetsFields; 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 0c241beff8..01255132cd 100644 --- a/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java +++ b/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java @@ -282,23 +282,10 @@ public class ResultMapper //Field Facets Map>> facetFields = solrResultSet.getFieldFacets(); - if (facetFields != null && !facetFields.isEmpty()) - { - ffcs = new ArrayList<>(facetFields.size()); - for (Entry>> facet:facetFields.entrySet()) - { - if (facet.getValue() != null && !facet.getValue().isEmpty()) - { - List buckets = new ArrayList<>(facet.getValue().size()); - for (Pair buck:facet.getValue()) - { - Object display = propertyLookup.lookup(facet.getKey(), buck.getFirst()); - buckets.add(new Bucket(buck.getFirst(), buck.getSecond(), display)); - } - ffcs.add(new FacetFieldContext(facet.getKey(), buckets)); - } - } - } + ffcs = getFacetBuckets(facetFields, true); + + Map>> facetInterval = solrResultSet.getFacetIntervals(); + List intervals = getFacetBuckets(facetInterval, false); //Spelling SpellCheckResult spell = solrResultSet.getSpellCheckResult(); @@ -308,10 +295,34 @@ public class ResultMapper } //Put it all together - context = new SearchContext(solrResultSet.getLastIndexedTxId(), facetResults, ffcs, spellCheckContext); + context = new SearchContext(solrResultSet.getLastIndexedTxId(), facetResults, ffcs, intervals, spellCheckContext); return isNullContext(context)?null:context; } + protected List getFacetBuckets(Map>> facetFields, boolean withDisplay) + { + if (facetFields != null && !facetFields.isEmpty()) + { + List ffcs = new ArrayList<>(facetFields.size()); + for (Entry>> facet:facetFields.entrySet()) + { + if (facet.getValue() != null && !facet.getValue().isEmpty()) + { + List buckets = new ArrayList<>(facet.getValue().size()); + for (Pair buck:facet.getValue()) + { + Object display = withDisplay?propertyLookup.lookup(facet.getKey(), buck.getFirst()):null; + buckets.add(new Bucket(buck.getFirst(), buck.getSecond(), display)); + } + ffcs.add(new FacetFieldContext(facet.getKey(), buckets)); + } + } + + return ffcs; + } + return null; + } + /** * Is the context null? * @param context @@ -322,7 +333,8 @@ public class ResultMapper return (context.getFacetQueries() == null && context.getConsistency() == null && context.getSpellCheck() == null - && context.getFacetsFields() == null); + && context.getFacetsFields() == null + && context.getFacetIntervals() == null); } /** diff --git a/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java b/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java index 5b5093d940..eb4c22ceab 100644 --- a/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java +++ b/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java @@ -45,6 +45,9 @@ import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Params; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.GeneralHighlightParameters; +import org.alfresco.service.cmr.search.Interval; +import org.alfresco.service.cmr.search.IntervalParameters; +import org.alfresco.service.cmr.search.IntervalSet; import org.alfresco.service.cmr.search.LimitBy; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchParameters.FieldFacet; @@ -108,6 +111,7 @@ public class SearchMapper fromFacetFields(sp, searchQuery.getFacetFields()); fromSpellCheck(sp, searchQuery.getSpellcheck()); fromHighlight(sp, searchQuery.getHighlight()); + fromFacetIntervals(sp, searchQuery.getFacetIntervals()); fromScope(sp, searchQuery.getScope()); fromLimits(sp, searchQuery.getLimits()); @@ -452,6 +456,56 @@ public class SearchMapper } } + /** + * Sets the Interval Parameters object on search parameters + * + * It does some valiation then takes any "SETS" at the top level and sets them at every field level. + * + * @param sp SearchParameters + * @param facetIntervals IntervalParameters + */ + public void fromFacetIntervals(SearchParameters sp, IntervalParameters facetIntervals) + { + if (facetIntervals != null) + { + ParameterCheck.mandatory("facetIntervals intervals", facetIntervals.getIntervals()); + + List globalSets = facetIntervals.getSets(); + validateSets(globalSets, "facetIntervals"); + + if (facetIntervals.getIntervals() != null && !facetIntervals.getIntervals().isEmpty()) + { + for (Interval interval:facetIntervals.getIntervals()) + { + ParameterCheck.mandatory("facetIntervals intervals field", interval.getField()); + validateSets(interval.getSets(), "facetIntervals intervals "+interval.getField()); + if (interval.getSets() != null && globalSets != null) + { + interval.getSets().addAll(globalSets); + } + } + } + + if (facetIntervals.getSets() != null) + { + facetIntervals.getSets().clear(); + } + } + sp.setInterval(facetIntervals); + } + + protected void validateSets(List intervalSets, String prefix) + { + if (intervalSets != null && !intervalSets.isEmpty()) + { + for (IntervalSet aSet:intervalSets) + { + ParameterCheck.mandatory(prefix+" sets start", aSet.getStart()); + ParameterCheck.mandatory(prefix+" sets end", aSet.getEnd()); + } + } + } + /** * Sets the hightlight object on search parameters * @param sp SearchParameters diff --git a/source/java/org/alfresco/rest/api/search/model/SearchQuery.java b/source/java/org/alfresco/rest/api/search/model/SearchQuery.java index a87d51c012..05c98c0505 100644 --- a/source/java/org/alfresco/rest/api/search/model/SearchQuery.java +++ b/source/java/org/alfresco/rest/api/search/model/SearchQuery.java @@ -28,6 +28,7 @@ package org.alfresco.rest.api.search.model; import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.service.cmr.search.GeneralHighlightParameters; +import org.alfresco.service.cmr.search.IntervalParameters; import org.codehaus.jackson.annotate.JsonCreator; import org.codehaus.jackson.annotate.JsonProperty; @@ -54,8 +55,9 @@ public class SearchQuery private final Scope scope; private final Limits limits; private final GeneralHighlightParameters highlight; + private final IntervalParameters facetIntervals; - public static final SearchQuery EMPTY = new SearchQuery(null, null, null, null, null,null, null, null,null, null, null, null, null, null); + public static final SearchQuery EMPTY = new SearchQuery(null, null, null, null, null,null, null, null,null, null, null, null, null, null, null); @JsonCreator public SearchQuery(@JsonProperty("query") Query query, @@ -71,7 +73,8 @@ public class SearchQuery @JsonProperty("spellcheck") Spelling spellcheck, @JsonProperty("scope") Scope scope, @JsonProperty("limits")Limits limits, - @JsonProperty("highlight")GeneralHighlightParameters highlight) + @JsonProperty("highlight")GeneralHighlightParameters highlight, + @JsonProperty("facetIntervals")IntervalParameters facetIntervals) { this.query = query; this.paging = paging; @@ -87,6 +90,7 @@ public class SearchQuery this.facetFields = facetFields; this.limits = limits; this.highlight = highlight; + this.facetIntervals = facetIntervals; } public Query getQuery() @@ -152,6 +156,12 @@ public class SearchQuery { return highlight; } + + public IntervalParameters getFacetIntervals() + { + return facetIntervals; + } + public Limits getLimits() { return limits; 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 2268c4f0cb..bb26f9ade1 100644 --- a/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java +++ b/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java @@ -265,11 +265,12 @@ public class ResultMapperTests @Test public void testIsNullContext() throws Exception { - assertTrue(mapper.isNullContext(new SearchContext(0l,null,null,null))); - assertFalse(mapper.isNullContext(new SearchContext(1l,null,null,null))); - assertFalse(mapper.isNullContext(new SearchContext(0l,null,null,new SpellCheckContext(null, null)))); - assertFalse(mapper.isNullContext(new SearchContext(0l,Arrays.asList(new FacetQueryContext(null, 0)),null,null))); - assertFalse(mapper.isNullContext(new SearchContext(0l,null,Arrays.asList(new FacetFieldContext(null, null)),null))); + assertTrue(mapper.isNullContext(new SearchContext(0l,null,null,null,null))); + assertFalse(mapper.isNullContext(new SearchContext(1l,null,null,null,null))); + assertFalse(mapper.isNullContext(new SearchContext(0l,null,null,null,new SpellCheckContext(null, null)))); + assertFalse(mapper.isNullContext(new SearchContext(0l,Arrays.asList(new FacetQueryContext(null, 0)),null,null,null))); + assertFalse(mapper.isNullContext(new SearchContext(0l,null,Arrays.asList(new FacetFieldContext(null, null)),null,null))); + assertFalse(mapper.isNullContext(new SearchContext(0l,null,null,Arrays.asList(new FacetFieldContext(null, null)),null))); } @Test 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 b932b99bad..956e4fd363 100644 --- a/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java +++ b/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java @@ -53,6 +53,9 @@ import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.FieldHighlightParameters; import org.alfresco.service.cmr.search.GeneralHighlightParameters; +import org.alfresco.service.cmr.search.Interval; +import org.alfresco.service.cmr.search.IntervalParameters; +import org.alfresco.service.cmr.search.IntervalSet; import org.alfresco.service.cmr.search.LimitBy; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchParameters.FieldFacet; @@ -60,6 +63,7 @@ import org.alfresco.service.cmr.search.SearchService; import org.junit.BeforeClass; import org.junit.Test; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -569,10 +573,79 @@ public class SearchMapperTests assertEquals(searchParameters.getHighlight(), highlightParameters); } + @Test + public void fromInterval() throws Exception + { + SearchParameters searchParameters = new SearchParameters(); + IntervalParameters intervalParameters = new IntervalParameters(null, null); + try + { + searchMapper.fromFacetIntervals(searchParameters, intervalParameters); + fail(); + } + catch (IllegalArgumentException iae) + { + //intervals is required + assertNotNull(iae); + } + + List intervalSets = Arrays.asList(new IntervalSet(null,"1", null, null, null)); + List intervalList = Arrays.asList(new Interval(null, "bob", null)); + intervalParameters = new IntervalParameters(intervalSets,intervalList); + + try + { + searchMapper.fromFacetIntervals(searchParameters, intervalParameters); + fail(); + } + catch (IllegalArgumentException iae) + { + //facetIntervals intervals field is required + assertNotNull(iae); + } + + intervalList = Arrays.asList(new Interval("aFileld", "bob", null)); + intervalParameters = new IntervalParameters(intervalSets,intervalList); + try + { + searchMapper.fromFacetIntervals(searchParameters, intervalParameters); + fail(); + } + catch (IllegalArgumentException iae) + { + //set start is required + assertNotNull(iae); + } + + intervalSets = Arrays.asList(new IntervalSet("1",null, null, null, true)); + intervalParameters = new IntervalParameters(intervalSets,intervalList); + + try + { + searchMapper.fromFacetIntervals(searchParameters, intervalParameters); + fail(); + } + catch (IllegalArgumentException iae) + { + //set end is required + assertNotNull(iae); + } + + intervalSets = new ArrayList<>(); + intervalSets.add(new IntervalSet("0", "3", "bob", null, null)); + intervalSets.add(new IntervalSet("30", "50", "bill", true,true)); + List anIntervalSet = new ArrayList<>(); + anIntervalSet.add(new IntervalSet("1", "10", "bert", false, false)); + intervalList = Arrays.asList(new Interval("cm:price", "Price", null), new Interval("cm:price", "Price", anIntervalSet)); + intervalParameters = new IntervalParameters(intervalSets,intervalList); + searchMapper.fromFacetIntervals(searchParameters, intervalParameters); + assertEquals(searchParameters.getInterval(), intervalParameters); + } + private SearchQuery minimalQuery() { Query query = new Query("cmis", "foo", ""); - SearchQuery sq = new SearchQuery(query,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); return sq; } } diff --git a/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java b/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java index 9900f804af..29cc10fb18 100644 --- a/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java +++ b/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java @@ -27,6 +27,7 @@ package org.alfresco.rest.api.search; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import org.alfresco.rest.api.search.context.FacetFieldContext; import org.alfresco.rest.api.search.context.FacetFieldContext.Bucket; @@ -41,11 +42,15 @@ import org.alfresco.rest.api.search.context.SearchContext; import org.alfresco.rest.api.search.context.FacetQueryContext; import org.alfresco.rest.framework.tests.api.mocks.Farmer; import org.alfresco.service.cmr.search.FieldHighlightParameters; +import org.alfresco.service.cmr.search.Interval; +import org.alfresco.service.cmr.search.IntervalParameters; +import org.alfresco.service.cmr.search.IntervalSet; import org.junit.BeforeClass; import org.junit.Test; import java.io.IOException; import java.util.Arrays; +import java.util.List; /** * Tests json -> SearchQuery deserialization @@ -137,6 +142,28 @@ public class SearchQuerySerializerTests assertEquals(3, high2.getSnippetCount().intValue()); assertEquals(15, high2.getFragmentSize().intValue()); assertEquals(false,high2.getMergeContiguous()); + + //Facet intervals + IntervalParameters ip = searchQuery.getFacetIntervals(); + assertNotNull(ip); + assertEquals(1,ip.getSets().size()); + IntervalSet is = ip.getSets().get(0); + assertEquals("king", is.getLabel()); + assertEquals("1", is.getStart()); + assertEquals("2", is.getEnd()); + assertEquals(true, is.isStartInclusive()); + assertEquals(false, is.isEndInclusive()); + + assertEquals(1,ip.getIntervals().size()); + Interval interval = ip.getIntervals().get(0); + assertEquals("Creator", interval.getLabel()); + assertEquals("creator", interval.getField()); + is = interval.getSets().get(0); + assertEquals("bob", is.getLabel()); + assertEquals("a", is.getStart()); + assertEquals("b", is.getEnd()); + assertEquals(false, is.isStartInclusive()); + assertEquals(true, is.isEndInclusive()); } @Test @@ -146,7 +173,7 @@ public class SearchQuerySerializerTests FacetFieldContext ffc = new FacetFieldContext("theLabel", Arrays.asList(new Bucket("b1", 23, "displayText1"), new Bucket("b2", 34, "displayText2"))); SearchContext searchContext = new SearchContext(23l, Arrays.asList(new FacetQueryContext("f1", 15), new FacetQueryContext("f2", 20)), - Arrays.asList(ffc), + Arrays.asList(ffc), null, new SpellCheckContext("aFlag", Arrays.asList("bish", "bash"))); CollectionWithPagingInfo coll = CollectionWithPagingInfo.asPaged(null, Arrays.asList(exec1), false, 2, null, searchContext); String out = helper.writeResponse(coll); @@ -159,7 +186,7 @@ public class SearchQuerySerializerTests assertTrue("There must 'bucket1' json output", out.contains("{\"label\":\"b1\",\"count\":23,\"display\":\"displayText1\"}")); assertTrue("There must 'bucket2' json output", out.contains("{\"label\":\"b2\",\"count\":34,\"display\":\"displayText2\"}")); - searchContext = new SearchContext(-1, null, null, null); + searchContext = new SearchContext(-1, null, null,null, null); coll = CollectionWithPagingInfo.asPaged(null, Arrays.asList(exec1), false, 2, null, searchContext); out = helper.writeResponse(coll); assertTrue("There must NOT BE a 'context' json output", out.contains("\"context\":{}")); 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 c1c49d1763..5e6831b92b 100644 --- a/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java +++ b/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java @@ -62,6 +62,10 @@ public class SerializerTestHelper implements RequestReader + "\"filterQueries\": [{\"query\": \"myquery\",\"tags\": [\"tag1\", \"tag2\"]},{\"query\": \"myquery2\"}]," + "\"facetFields\": {\"facets\": [{\"field\": \"cm:creator\",\"prefix\": \"myquery2\",\"sort\": \"COUNT\",\"missing\": \"false\"}, {\"field\": \"modifier\",\"label\": \"mylabel\",\"method\": \"FC\",\"mincount\": \"5\"}]}," + "\"facetQueries\": [{\"query\": \"facquery\",\"label\": \"facnoused\"}]," + + "\"facetIntervals\": {\"sets\": [{ \"label\": \"king\", \"start\": \"1\", \"end\": \"2\",\"startInclusive\": true,\"endInclusive\": false}]" + + ",\"intervals\": [{\"field\": \"creator\",\"label\": \"Creator\"," + + "\"sets\": [{\"label\": \"bob\",\"start\": \"a\",\"end\": \"b\",\"startInclusive\": false}]" + + "}]}," + "\"spellcheck\": {\"query\": \"alfrezco\"}," + "\"limits\": {\"permissionEvaluationCount\": \"2000\",\"permissionEvaluationTime\": \"5000\"}," + "\"scope\": { \"locations\": [\"nodes\"]},"