diff --git a/config/alfresco/subsystems/Search/solr/solr-search-context.xml b/config/alfresco/subsystems/Search/solr/solr-search-context.xml index 5287f76f3b..cb2e4dfd2a 100644 --- a/config/alfresco/subsystems/Search/solr/solr-search-context.xml +++ b/config/alfresco/subsystems/Search/solr/solr-search-context.xml @@ -36,6 +36,8 @@ + + diff --git a/config/alfresco/subsystems/Search/solr4/solr-search-context.xml b/config/alfresco/subsystems/Search/solr4/solr-search-context.xml index cb63cd2bb8..64cea67b03 100644 --- a/config/alfresco/subsystems/Search/solr4/solr-search-context.xml +++ b/config/alfresco/subsystems/Search/solr4/solr-search-context.xml @@ -36,6 +36,8 @@ + + diff --git a/config/alfresco/subsystems/Search/solr6/solr-search-context.xml b/config/alfresco/subsystems/Search/solr6/solr-search-context.xml index 5103cfe755..cb06e6dd40 100644 --- a/config/alfresco/subsystems/Search/solr6/solr-search-context.xml +++ b/config/alfresco/subsystems/Search/solr6/solr-search-context.xml @@ -36,6 +36,8 @@ + + diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java index dfb7e81e1e..24b596e6c4 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java @@ -25,19 +25,25 @@ */ package org.alfresco.repo.search.impl.solr; +import static org.alfresco.util.SearchDateConversion.parseDateInterval; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; import org.alfresco.repo.admin.RepositoryState; +import org.alfresco.repo.dictionary.NamespaceDAO; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.index.shard.Floc; import org.alfresco.repo.index.shard.ShardInstance; import org.alfresco.repo.index.shard.ShardRegistry; +import org.alfresco.repo.search.impl.QueryParserUtils; import org.alfresco.repo.search.impl.lucene.JSONResult; import org.alfresco.repo.search.impl.lucene.LuceneQueryParserException; import org.alfresco.repo.search.impl.lucene.SolrJSONResultSet; import org.alfresco.repo.search.impl.lucene.SolrJsonProcessor; import org.alfresco.repo.search.impl.lucene.SolrStatsResult; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; @@ -105,6 +111,8 @@ public class SolrQueryHTTPClient implements BeanFactoryAware, InitializingBean { static Log s_logger = LogFactory.getLog(SolrQueryHTTPClient.class); + private DictionaryService dictionaryService; + private NodeService nodeService; private PermissionService permissionService; @@ -141,6 +149,8 @@ public class SolrQueryHTTPClient implements BeanFactoryAware, InitializingBean private int defaultShardedFacetLimit = 20; + private NamespaceDAO namespaceDAO; + public SolrQueryHTTPClient() { } @@ -153,7 +163,8 @@ public class SolrQueryHTTPClient implements BeanFactoryAware, InitializingBean PropertyCheck.mandatory(this, "LanguageMappings", languageMappings); PropertyCheck.mandatory(this, "StoreMappings", storeMappings); PropertyCheck.mandatory(this, "RepositoryState", repositoryState); - + PropertyCheck.mandatory(this, "namespaceDAO", namespaceDAO); + PropertyCheck.mandatory(this, "dictionaryService", dictionaryService); } public void setAlternativeDictionary(String alternativeDictionary) @@ -192,6 +203,16 @@ public class SolrQueryHTTPClient implements BeanFactoryAware, InitializingBean this.tenantService = tenantService; } + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public void setNamespaceDAO(NamespaceDAO namespaceDAO) + { + this.namespaceDAO = namespaceDAO; + } + public void setShardRegistry(ShardRegistry shardRegistry) { this.shardRegistry = shardRegistry; @@ -810,6 +831,15 @@ public class SolrQueryHTTPClient implements BeanFactoryAware, InitializingBean { url.append("&facet.interval="); String intervalField = interval.getField(); + boolean isDate = false; + + PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespaceDAO, dictionaryService, interval.getField()); + if (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) + || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE)) + { + isDate = true; + } if (interval.getLabel() != null && !interval.getLabel().isEmpty()) { @@ -822,7 +852,8 @@ public class SolrQueryHTTPClient implements BeanFactoryAware, InitializingBean { for (IntervalSet aSet:interval.getSets()) { - url.append("&").append(encoder.encode("f."+intervalField+".facet.interval.set", "UTF-8")).append("=").append(encoder.encode(aSet.toParam(), "UTF-8")); + IntervalSet validated = parseDateInterval(aSet,isDate); + url.append("&").append(encoder.encode("f."+intervalField+".facet.interval.set", "UTF-8")).append("=").append(encoder.encode(validated.toParam(), "UTF-8")); } } } diff --git a/source/test-java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClientTest.java b/source/test-java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClientTest.java index f88f45dba2..73c26843ac 100644 --- a/source/test-java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClientTest.java +++ b/source/test-java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClientTest.java @@ -25,22 +25,45 @@ */ package org.alfresco.repo.search.impl.solr; +import static org.alfresco.service.namespace.NamespaceService.CONTENT_MODEL_PREFIX; import static org.junit.Assert.*; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.notNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.UnsupportedEncodingException; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.alfresco.model.ContentModel; +import org.alfresco.repo.admin.RepositoryState; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.forms.processor.node.MockClassAttributeDefinition; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +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.SearchParameters; import org.alfresco.service.cmr.search.SearchParameters.SortDefinition; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.StatsParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.PropertyCheck; import org.apache.commons.codec.net.URLCodec; import org.json.JSONArray; import org.json.JSONException; @@ -57,28 +80,53 @@ import org.junit.Test; */ public class SolrQueryHTTPClientTest { - - static Map languageMappings; - + static SolrQueryHTTPClient client = new SolrQueryHTTPClient(); + static URLCodec encoder = new URLCodec(); + @BeforeClass public static void setUpBeforeClass() throws Exception { - languageMappings = new HashMap(); + Map languageMappings = new HashMap(); languageMappings.put("solr-alfresco", "alfresco"); languageMappings.put("solr-fts-alfresco", "afts"); languageMappings.put("solr-cmis", "cmis"); - } - @Before - public void setUp() throws Exception - { + NamespaceDAO namespaceDAO = mock(NamespaceDAO.class); + DictionaryService dictionaryService = mock(DictionaryService.class); + + when(namespaceDAO.getPrefixes()).thenReturn(Arrays.asList(CONTENT_MODEL_PREFIX, "exif")); + when(namespaceDAO.getNamespaceURI(anyString())).thenReturn(NamespaceService.CONTENT_MODEL_1_0_URI); + + when(dictionaryService.getProperty(notNull(QName.class))).thenAnswer(invocation -> + { + Object[] args = invocation.getArguments(); + QName qName = (QName)args[0]; + if (qName.getLocalName().contains("created")) + { + return MockClassAttributeDefinition.mockPropertyDefinition(qName, DataTypeDefinition.DATE); + } + else + { + return MockClassAttributeDefinition.mockPropertyDefinition(qName, DataTypeDefinition.ANY); + } + + }); + + client.setLanguageMappings(languageMappings); + client.setDictionaryService(dictionaryService); + client.setNamespaceDAO(namespaceDAO); + + //required for init() but not used. + client.setNodeService(mock(NodeService.class)); + client.setTenantService(mock(TenantService.class)); + client.setStoreMappings(Collections.emptyList()); + client.setRepositoryState(mock(RepositoryState.class)); + client.init(); } @Test public void testBuildStatsUrl() throws UnsupportedEncodingException { - SolrQueryHTTPClient client = new SolrQueryHTTPClient(); - client.setLanguageMappings(languageMappings); StatsParameters params = getParameters(); String url = client.buildStatsUrl(params, "http://localhost:8080/solr/alfresco/select", Locale.CANADA_FRENCH, null); assertNotNull(url); @@ -91,7 +139,7 @@ public class SolrQueryHTTPClientTest @Test public void testBuildStatsBody() throws JSONException { - SolrQueryHTTPClient client = new SolrQueryHTTPClient(); + StatsParameters params = getParameters(); JSONObject body = client.buildStatsBody(params, "myTenant", Locale.US); assertNotNull(body); @@ -108,7 +156,6 @@ public class SolrQueryHTTPClientTest StringBuilder luceneQuery = new StringBuilder(); luceneQuery.append(" +TYPE:\"" + ContentModel.TYPE_CONTENT + "\""); - String filterQuery = "ANCESTOR:\"workspace://SpacesStore/a1c1a0a1-9d68-4912-b853-b3b277f31288\""; StatsParameters params = new StatsParameters(SearchService.LANGUAGE_SOLR_FTS_ALFRESCO, luceneQuery.toString(), filterQuery, false); params.addSort(new SortDefinition(SortDefinition.SortType.FIELD, "contentsize", false)); @@ -122,9 +169,6 @@ public class SolrQueryHTTPClientTest @Test public void testBuildHighlightQuery() throws UnsupportedEncodingException { - SolrQueryHTTPClient client = new SolrQueryHTTPClient(); - client.setLanguageMappings(languageMappings); - URLCodec encoder = new URLCodec(); SearchParameters params = new SearchParameters(); params.setSearchTerm("bob"); StringBuilder urlBuilder = new StringBuilder(); @@ -191,7 +235,47 @@ public class SolrQueryHTTPClientTest assertTrue(url.contains("&f.title.hl.simple.pre="+encoder.encode("*", "UTF-8"))); assertTrue(url.contains("&f.title.hl.simple.post="+encoder.encode("¿", "UTF-8"))); + } + @Test + public void testBuildFacetIntervalQuery() throws UnsupportedEncodingException + { + SearchParameters params = new SearchParameters(); + params.setSearchTerm("bob"); + + IntervalSet intervalSet = new IntervalSet("8", "12", null, null, null); + params.setInterval(new IntervalParameters(Arrays.asList(intervalSet), null)); + StringBuilder urlBuilder = new StringBuilder(); + client.buildFacetIntervalParameters(params, encoder, urlBuilder); + String url = urlBuilder.toString(); + assertNotNull(url); + assertTrue(url.contains("&facet=true")); + assertTrue(url.contains(encoder.encode("{!afts}[8,12]", "UTF-8"))); + + intervalSet = new IntervalSet("1", "10", "numbers", false, true); + params.setInterval(new IntervalParameters(Arrays.asList(intervalSet), null)); + urlBuilder = new StringBuilder(); + client.buildFacetIntervalParameters(params, encoder, urlBuilder); + url = urlBuilder.toString(); + assertNotNull(url); + assertTrue(url.contains("&facet=true")); + assertTrue(url.contains(encoder.encode("{!afts key=numbers}(1,10]", "UTF-8"))); + + List intervalList = Arrays.asList(new Interval("cm:price", "Price", null), new Interval("cm:created", "Created", Arrays.asList(new IntervalSet("2015", "2016-12", "special", false, true)))); + params.setInterval(new IntervalParameters(Arrays.asList(intervalSet), intervalList)); + urlBuilder = new StringBuilder(); + client.buildFacetIntervalParameters(params, encoder, urlBuilder); + url = urlBuilder.toString(); + assertNotNull(url); + assertTrue(url.contains("&facet=true")); + assertTrue(url.contains(encoder.encode("{!afts key=numbers}(1,10]", "UTF-8"))); + + assertTrue(url.contains(encoder.encode("{!key=Price}cm:price", "UTF-8"))); + assertTrue(url.contains(encoder.encode("{!key=Created}cm:created", "UTF-8"))); + assertTrue(url.contains("f.Created.facet.interval.set")); + assertTrue(url.contains(encoder.encode("{!afts key=numbers}", "UTF-8"))); + assertTrue(url.contains(encoder.encode("(2015-12-31T22:59:59.999Z", "UTF-8"))); + assertTrue(url.contains(encoder.encode("2016-12-31T22:59:59.999Z]", "UTF-8"))); } }