From 69757afc961e78f09629b668badf417ba40f2b0c Mon Sep 17 00:00:00 2001 From: Shreasi Khan Date: Mon, 24 Aug 2026 14:56:42 +0530 Subject: [PATCH] [MNT-25111] Shared Links API with where clause does not work with Search Enterprise (#4296) --- .../search/AbstractSearcherComponent.java | 94 ++++++++ .../ElasticsearchSearchService.java | 26 +- .../ElasticsearchSearchServiceFactory.java | 9 +- .../search/impl/solr/SolrSearchService.java | 84 +------ .../elasticsearch-community-context.xml | 1 + .../java/org/alfresco/AllUnitTestsSuite.java | 1 + .../search/AbstractSearcherComponentTest.java | 223 ++++++++++++++++++ .../ElasticsearchSearchServiceTest.java | 41 +++- 8 files changed, 394 insertions(+), 85 deletions(-) create mode 100644 repository/src/test/java/org/alfresco/repo/search/AbstractSearcherComponentTest.java diff --git a/repository/src/main/java/org/alfresco/repo/search/AbstractSearcherComponent.java b/repository/src/main/java/org/alfresco/repo/search/AbstractSearcherComponent.java index 2f2ead17f7..cf15f85cdd 100644 --- a/repository/src/main/java/org/alfresco/repo/search/AbstractSearcherComponent.java +++ b/repository/src/main/java/org/alfresco/repo/search/AbstractSearcherComponent.java @@ -26,16 +26,24 @@ package org.alfresco.repo.search; import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import org.alfresco.repo.search.impl.QueryParameterisationException; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.XPathException; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.search.QueryParameter; import org.alfresco.service.cmr.search.QueryParameterDefinition; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; import org.alfresco.util.SearchLanguageConversion; /** @@ -73,4 +81,90 @@ public abstract class AbstractSearcherComponent implements SearchService return selectProperties(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks, SearchService.LANGUAGE_XPATH); } + + @SuppressWarnings({"PMD.CognitiveComplexity", "PMD.CyclomaticComplexity", "PMD.NPathComplexity", "PMD.AssignmentInOperand"}) + protected String parameterise(String unparameterised, Map map, QueryParameter[] queryParameters, NamespacePrefixResolver nspr) + throws QueryParameterisationException + { + + Map> valueMap = new HashMap<>(); + + if (queryParameters != null) + { + for (QueryParameter parameter : queryParameters) + { + List list = valueMap.get(parameter.getQName()); + if (list == null) + { + list = new ArrayList<>(); + valueMap.put(parameter.getQName(), list); + } + list.add(parameter.getValue()); + } + } + + Map> iteratorMap = new HashMap<>(); + + List missing = new ArrayList<>(1); + StringBuilder buffer = new StringBuilder(unparameterised); + int index = 0; + while ((index = buffer.indexOf("${", index)) != -1) + { + int endIndex = buffer.indexOf("}", index); + if (endIndex == -1) + { + throw new QueryParameterisationException("Unclosed query parameter placeholder starting at index " + index); + } + String qNameString = buffer.substring(index + 2, endIndex); + QName key = QName.createQName(qNameString, nspr); + QueryParameterDefinition parameterDefinition = map.get(key); + if (parameterDefinition == null) + { + missing.add(key); + buffer.replace(index, endIndex + 1, ""); + } + else + { + ListIterator it = iteratorMap.get(key); + if ((it == null) || (!it.hasNext())) + { + List list = valueMap.get(key); + if ((list != null) && (!list.isEmpty())) + { + it = list.listIterator(); + } + if (it != null) + { + iteratorMap.put(key, it); + } + } + String value; + if (it == null) + { + if (!parameterDefinition.hasDefaultValue()) + { + throw new QueryParameterisationException("No value provided for query parameter: " + key); + } + value = parameterDefinition.getDefault(); + } + else + { + value = DefaultTypeConverter.INSTANCE.convert(String.class, it.next()); + } + buffer.replace(index, endIndex + 1, value); + } + } + if (!missing.isEmpty()) + { + StringBuilder error = new StringBuilder(); + error.append("The query uses the following parameters which are not defined: "); + for (QName qName : missing) + { + error.append(qName).append(", "); + } + error.delete(error.length() - 2, error.length()); + throw new QueryParameterisationException(error.toString()); + } + return buffer.toString(); + } } diff --git a/repository/src/main/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchService.java b/repository/src/main/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchService.java index c432011fc1..36357ba4bc 100644 --- a/repository/src/main/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchService.java +++ b/repository/src/main/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchService.java @@ -28,6 +28,7 @@ package org.alfresco.repo.search.impl.elasticsearch; import static org.alfresco.repo.search.adaptor.QueryConstants.FIELD_TAG; import java.io.Serializable; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; @@ -66,16 +67,19 @@ public class ElasticsearchSearchService extends AbstractSearcherComponent private final Map queryLanguages; private NodeService nodeService; private DictionaryService dictionaryService; + private NamespacePrefixResolver namespacePrefixResolver; public ElasticsearchSearchService(QueryRegisterComponent queryRegister, Map queryLanguages, NodeService nodeService, - DictionaryService dictionaryService) + DictionaryService dictionaryService, + NamespacePrefixResolver namespacePrefixResolver) { this.queryRegister = queryRegister; this.queryLanguages = queryLanguages; this.nodeService = nodeService; this.dictionaryService = dictionaryService; + this.namespacePrefixResolver = namespacePrefixResolver; } @Override @@ -115,6 +119,7 @@ public class ElasticsearchSearchService extends AbstractSearcherComponent public ResultSet query(SearchParameters searchParameters) { adjustSearchParameters(searchParameters); + parameteriseQuery(searchParameters); LuceneQueryLanguageSPI language = queryLanguages.get(searchParameters.getLanguage().toLowerCase()); if (language != null) { @@ -126,6 +131,25 @@ public class ElasticsearchSearchService extends AbstractSearcherComponent } } + private void parameteriseQuery(SearchParameters searchParameters) + { + if (searchParameters.getQuery() == null + || searchParameters.getQueryParameterDefinitions() == null + || searchParameters.getQueryParameterDefinitions().isEmpty()) + { + return; + } + + Map map = new HashMap<>(); + for (QueryParameterDefinition qpd : searchParameters.getQueryParameterDefinitions()) + { + map.put(qpd.getQName(), qpd); + } + + String parameterisedQueryString = parameterise(searchParameters.getQuery(), map, null, namespacePrefixResolver); + searchParameters.setQuery(parameterisedQueryString); + } + @Override public List selectNodes(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters, NamespacePrefixResolver namespacePrefixResolver, boolean followAllParentLinks) diff --git a/repository/src/main/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchServiceFactory.java b/repository/src/main/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchServiceFactory.java index 3024122806..9b8d47c98e 100644 --- a/repository/src/main/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchServiceFactory.java +++ b/repository/src/main/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchServiceFactory.java @@ -35,6 +35,7 @@ import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespacePrefixResolver; /** * Factory for Indexer and Searcher instances for Elasticsearch @@ -44,6 +45,7 @@ public class ElasticsearchSearchServiceFactory extends AbstractIndexerAndSearche private QueryRegisterComponent queryRegister; private NodeService nodeService; private DictionaryService dictionaryService; + private NamespacePrefixResolver namespacePrefixResolver; /** * Elasticsearch is indexing the repository by polling database or listening queue messages from an external service, so no Indexer is available for the Repository. @@ -57,7 +59,7 @@ public class ElasticsearchSearchServiceFactory extends AbstractIndexerAndSearche @Override public SearchService getSearcher(StoreRef storeRef, boolean searchDelta) throws SearcherException { - return new ElasticsearchSearchService(queryRegister, getQueryLanguages(), nodeService, dictionaryService); + return new ElasticsearchSearchService(queryRegister, getQueryLanguages(), nodeService, dictionaryService, namespacePrefixResolver); } @Override @@ -80,4 +82,9 @@ public class ElasticsearchSearchServiceFactory extends AbstractIndexerAndSearche { this.dictionaryService = dictionaryService; } + + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } } diff --git a/repository/src/main/java/org/alfresco/repo/search/impl/solr/SolrSearchService.java b/repository/src/main/java/org/alfresco/repo/search/impl/solr/SolrSearchService.java index 51493202e7..97cc0d3b98 100644 --- a/repository/src/main/java/org/alfresco/repo/search/impl/solr/SolrSearchService.java +++ b/repository/src/main/java/org/alfresco/repo/search/impl/solr/SolrSearchService.java @@ -30,10 +30,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.Set; +import org.alfresco.repo.search.AbstractSearcherComponent; import org.alfresco.repo.search.CannedQueryDef; import org.alfresco.repo.search.QueryRegisterComponent; import org.alfresco.repo.search.SearcherException; @@ -61,7 +61,7 @@ import org.alfresco.util.SearchLanguageConversion; /** * @author Andy */ -public class SolrSearchService implements SearchService +public class SolrSearchService extends AbstractSearcherComponent { private NodeService nodeService; @@ -227,86 +227,6 @@ public class SolrSearchService implements SearchService } } - /* Parameterise the query string - not sure if it is required to escape lucence spacials chars The parameters could be used to build the query - the contents of parameters should alread have been escaped if required. ... mush better to provide the parameters and work out what to do TODO: conditional query escapement - may be we should have a parameter type that is not escaped */ - private String parameterise(String unparameterised, Map map, QueryParameter[] queryParameters, NamespacePrefixResolver nspr) - throws QueryParameterisationException - { - - Map> valueMap = new HashMap>(); - - if (queryParameters != null) - { - for (QueryParameter parameter : queryParameters) - { - List list = valueMap.get(parameter.getQName()); - if (list == null) - { - list = new ArrayList(); - valueMap.put(parameter.getQName(), list); - } - list.add(parameter.getValue()); - } - } - - Map> iteratorMap = new HashMap>(); - - List missing = new ArrayList(1); - StringBuilder buffer = new StringBuilder(unparameterised); - int index = 0; - while ((index = buffer.indexOf("${", index)) != -1) - { - int endIndex = buffer.indexOf("}", index); - String qNameString = buffer.substring(index + 2, endIndex); - QName key = QName.createQName(qNameString, nspr); - QueryParameterDefinition parameterDefinition = map.get(key); - if (parameterDefinition == null) - { - missing.add(key); - buffer.replace(index, endIndex + 1, ""); - } - else - { - ListIterator it = iteratorMap.get(key); - if ((it == null) || (!it.hasNext())) - { - List list = valueMap.get(key); - if ((list != null) && (list.size() > 0)) - { - it = list.listIterator(); - } - if (it != null) - { - iteratorMap.put(key, it); - } - } - String value; - if (it == null) - { - value = parameterDefinition.getDefault(); - } - else - { - value = DefaultTypeConverter.INSTANCE.convert(String.class, it.next()); - } - buffer.replace(index, endIndex + 1, value); - } - } - if (missing.size() > 0) - { - StringBuilder error = new StringBuilder(); - error.append("The query uses the following parameters which are not defined: "); - for (QName qName : missing) - { - error.append(qName); - error.append(", "); - } - error.delete(error.length() - 1, error.length() - 1); - error.delete(error.length() - 1, error.length() - 1); - throw new QueryParameterisationException(error.toString()); - } - return buffer.toString(); - } - /* (non-Javadoc) * * @see org.alfresco.service.cmr.search.SearchService#query(org.alfresco.service.cmr.search.SearchParameters) */ diff --git a/repository/src/main/resources/alfresco/subsystems/Search/elasticsearch/elasticsearch-community-context.xml b/repository/src/main/resources/alfresco/subsystems/Search/elasticsearch/elasticsearch-community-context.xml index 91150b4ab6..6202ca5c66 100644 --- a/repository/src/main/resources/alfresco/subsystems/Search/elasticsearch/elasticsearch-community-context.xml +++ b/repository/src/main/resources/alfresco/subsystems/Search/elasticsearch/elasticsearch-community-context.xml @@ -164,6 +164,7 @@ + diff --git a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java index 03f800dca1..f354b9f019 100644 --- a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java +++ b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java @@ -243,6 +243,7 @@ import org.alfresco.util.testing.category.NonBuildTests; org.alfresco.repo.domain.schema.script.ScriptBundleExecutorImplTest.class, org.alfresco.repo.search.MLAnaysisModeExpansionTest.class, org.alfresco.repo.search.DocumentNavigatorTest.class, + org.alfresco.repo.search.AbstractSearcherComponentTest.class, org.alfresco.util.NumericEncodingTest.class, org.alfresco.repo.search.impl.parsers.CMIS_FTSTest.class, org.alfresco.repo.search.impl.parsers.CMISTest.class, diff --git a/repository/src/test/java/org/alfresco/repo/search/AbstractSearcherComponentTest.java b/repository/src/test/java/org/alfresco/repo/search/AbstractSearcherComponentTest.java new file mode 100644 index 0000000000..c10c67120e --- /dev/null +++ b/repository/src/test/java/org/alfresco/repo/search/AbstractSearcherComponentTest.java @@ -0,0 +1,223 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2026 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.repo.search; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.alfresco.repo.search.impl.QueryParameterisationException; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.XPathException; +import org.alfresco.service.cmr.search.QueryParameter; +import org.alfresco.service.cmr.search.QueryParameterDefinition; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; + +@RunWith(MockitoJUnitRunner.class) +public class AbstractSearcherComponentTest +{ + private static final String TEST_URI = "http://www.alfresco.org/test/1.0"; + private static final String PREFIX = "test"; + + private final AbstractSearcherComponent searcher = new StubSearcherComponent(); + + @Mock + private NamespacePrefixResolver namespacePrefixResolver; + + @Before + public void setUp() + { + lenient().when(namespacePrefixResolver.getNamespaceURI(PREFIX)).thenReturn(TEST_URI); + } + + private static QName paramQName(String localName) + { + return QName.createQName(TEST_URI, localName); + } + + @Test + public void parameterise_substitutesSuppliedParameterValue() + { + Map map = new HashMap<>(); + map.put(paramQName("param"), mock(QueryParameterDefinition.class)); + + QueryParameter parameter = mock(QueryParameter.class); + when(parameter.getQName()).thenReturn(paramQName("param")); + when(parameter.getValue()).thenReturn("hello"); + + String result = searcher.parameterise("value:${test:param}", map, new QueryParameter[]{parameter}, namespacePrefixResolver); + + assertThat(result).isEqualTo("value:hello"); + } + + @Test + public void parameterise_substitutesDefaultValueWhenNoParameterSupplied() + { + QueryParameterDefinition qpd = mock(QueryParameterDefinition.class); + when(qpd.hasDefaultValue()).thenReturn(true); + when(qpd.getDefault()).thenReturn("admin"); + + Map map = new HashMap<>(); + map.put(paramQName("param"), qpd); + + String result = searcher.parameterise("user:${test:param}", map, null, namespacePrefixResolver); + + assertThat(result).isEqualTo("user:admin"); + } + + @Test + public void parameterise_iteratesSuppliedValuesInOrderForRepeatedPlaceholder() + { + Map map = new HashMap<>(); + map.put(paramQName("param"), mock(QueryParameterDefinition.class)); + + QueryParameter first = mock(QueryParameter.class); + when(first.getQName()).thenReturn(paramQName("param")); + when(first.getValue()).thenReturn("one"); + QueryParameter second = mock(QueryParameter.class); + when(second.getQName()).thenReturn(paramQName("param")); + when(second.getValue()).thenReturn("two"); + + String result = searcher.parameterise("${test:param}-${test:param}", map, new QueryParameter[]{first, second}, namespacePrefixResolver); + + assertThat(result).isEqualTo("one-two"); + } + + @Test + public void parameterise_returnsQueryUnchangedWhenNoPlaceholders() + { + String result = searcher.parameterise("TYPE:\"cm:content\"", new HashMap<>(), null, namespacePrefixResolver); + + assertThat(result).isEqualTo("TYPE:\"cm:content\""); + } + + @Test + public void parameterise_throwsWhenPlaceholderReferencesUndefinedParameter() + { + assertThatExceptionOfType(QueryParameterisationException.class) + .isThrownBy(() -> searcher.parameterise("value:${test:param}", new HashMap<>(), null, namespacePrefixResolver)) + .withMessageContaining("not defined"); + } + + @Test + public void parameterise_throwsWhenPlaceholderIsUnclosed() + { + assertThatExceptionOfType(QueryParameterisationException.class) + .isThrownBy(() -> searcher.parameterise("value:${test:param", new HashMap<>(), null, namespacePrefixResolver)) + .withMessageContaining("Unclosed"); + } + + @Test + public void parameterise_throwsWhenParameterHasNoValueAndNoDefault() + { + QueryParameterDefinition qpd = mock(QueryParameterDefinition.class); + when(qpd.hasDefaultValue()).thenReturn(false); + + Map map = new HashMap<>(); + map.put(paramQName("param"), qpd); + + assertThatExceptionOfType(QueryParameterisationException.class) + .isThrownBy(() -> searcher.parameterise("value:${test:param}", map, null, namespacePrefixResolver)) + .withMessageContaining("No value provided"); + } + + /** + * Minimal concrete subclass so the shared {@code protected} parameterise implementation can be exercised in isolation. The remaining {@link org.alfresco.service.cmr.search.SearchService} operations are not needed here. + */ + private static final class StubSearcherComponent extends AbstractSearcherComponent + { + @Override + public ResultSet query(StoreRef store, String language, String query, QueryParameterDefinition[] queryParameterDefinitions) + { + throw new UnsupportedOperationException(); + } + + @Override + public ResultSet query(StoreRef store, QName queryId, QueryParameter[] queryParameters) + { + throw new UnsupportedOperationException(); + } + + @Override + public ResultSet query(SearchParameters searchParameters) + { + throw new UnsupportedOperationException(); + } + + @Override + public List selectNodes(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters, + NamespacePrefixResolver namespacePrefixResolver, boolean followAllParentLinks, String language) + throws InvalidNodeRefException, XPathException + { + throw new UnsupportedOperationException(); + } + + @Override + public List selectProperties(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters, + NamespacePrefixResolver namespacePrefixResolver, boolean followAllParentLinks, String language) + throws InvalidNodeRefException, XPathException + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean contains(NodeRef nodeRef, QName propertyQName, String googleLikePattern) throws InvalidNodeRefException + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean contains(NodeRef nodeRef, QName propertyQName, String googleLikePattern, SearchParameters.Operator defaultOperator) + throws InvalidNodeRefException + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean like(NodeRef nodeRef, QName propertyQName, String sqlLikePattern, boolean includeFTS) throws InvalidNodeRefException + { + throw new UnsupportedOperationException(); + } + } +} diff --git a/repository/src/test/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchServiceTest.java b/repository/src/test/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchServiceTest.java index 4a8bb523d0..d9fe3c18da 100644 --- a/repository/src/test/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchServiceTest.java +++ b/repository/src/test/java/org/alfresco/repo/search/impl/elasticsearch/ElasticsearchSearchServiceTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.*; import static org.alfresco.service.cmr.search.SearchService.LANGUAGE_FTS_ALFRESCO; import static org.alfresco.service.cmr.search.SearchService.LANGUAGE_LUCENE; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -49,8 +50,11 @@ import org.alfresco.repo.search.SearcherException; import org.alfresco.repo.search.impl.lucene.LuceneQueryLanguageSPI; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.search.QueryParameterDefinition; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; @RunWith(MockitoJUnitRunner.class) public class ElasticsearchSearchServiceTest @@ -71,6 +75,9 @@ public class ElasticsearchSearchServiceTest @Mock private DictionaryService dictionaryService; + @Mock + private NamespacePrefixResolver namespacePrefixResolver; + @Mock private SearchParameters searchParameters; @@ -83,9 +90,10 @@ public class ElasticsearchSearchServiceTest Map languages = new HashMap<>(); languages.put(LANGUAGE.toLowerCase(Locale.getDefault()), queryLanguage); - searchService = new ElasticsearchSearchService(queryRegister, languages, nodeService, dictionaryService); + searchService = new ElasticsearchSearchService(queryRegister, languages, nodeService, dictionaryService, namespacePrefixResolver); when(searchParameters.getLanguage()).thenReturn(LANGUAGE); + when(searchParameters.getQueryParameterDefinitions()).thenReturn(new ArrayList<>()); when(queryLanguage.executeQuery(any(SearchParameters.class))).thenReturn(resultSet); } @@ -152,6 +160,37 @@ public class ElasticsearchSearchServiceTest verify(searchParameters, times(1)).setLanguage(LANGUAGE_FTS_ALFRESCO); } + @Test + public void luceneQueryWithParameterDefinition_shouldSubstitutePlaceholderWithDefaultValue() + { + String qshareUri = "http://www.alfresco.org/model/qshare/1.0"; + when(namespacePrefixResolver.getNamespaceURI("qshare")).thenReturn(qshareUri); + + QName sharedBy = QName.createQName(qshareUri, "sharedBy"); + QueryParameterDefinition qpd = mock(QueryParameterDefinition.class); + when(qpd.getQName()).thenReturn(sharedBy); + when(qpd.hasDefaultValue()).thenReturn(true); + when(qpd.getDefault()).thenReturn("admin"); + + when(searchParameters.getQuery()).thenReturn("ASPECT:\"qshare:shared\" +@qshare:sharedBy:\"${qshare:sharedBy}\""); + when(searchParameters.getQueryParameterDefinitions()).thenReturn(new ArrayList<>(List.of(qpd))); + + searchService.query(searchParameters); + + verify(searchParameters, times(1)).setQuery("ASPECT:\"qshare:shared\" +@qshare:sharedBy:\"admin\""); + } + + @Test + public void luceneQueryWithoutParameterDefinitions_shouldNotSubstitute() + { + when(searchParameters.getQuery()).thenReturn("ASPECT:\"qshare:shared\""); + when(searchParameters.getQueryParameterDefinitions()).thenReturn(new ArrayList<>()); + + searchService.query(searchParameters); + + verify(searchParameters, never()).setQuery(any()); + } + @Test public void luceneQuery_whenNotMatchingTagQuery_shouldNotBeModified() {