From f2beb3875ac9854cb7f6c01005808726a1b4fbe5 Mon Sep 17 00:00:00 2001 From: elia Date: Thu, 26 Nov 2020 10:40:17 +0100 Subject: [PATCH 1/8] [SEARCH-2548] updated solr dependency --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5f59ddc39..fd2a2fcd1 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ 11 6.6.5 - ${solr.base.version}-patched.2 + ${solr.base.version}-patched.3 From 06af32d076d954106a4a18ef99fd72e0189333ec Mon Sep 17 00:00:00 2001 From: elia Date: Thu, 26 Nov 2020 10:40:36 +0100 Subject: [PATCH 2/8] [SEARCH-2548] set version field as docvalue --- .../resources/solr/instance/templates/rerank/conf/schema.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/schema.xml b/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/schema.xml index 59da63c6c..edd39f10e 100644 --- a/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/schema.xml +++ b/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/schema.xml @@ -26,7 +26,7 @@ - retrievable(stored or docValues) - not multiValued --> - + From 68130aa3abdddacdd77c072606c04d3290815b84 Mon Sep 17 00:00:00 2001 From: elia Date: Thu, 26 Nov 2020 14:29:02 +0100 Subject: [PATCH 3/8] [SEARCH-2548] updated notice with new dependency --- .../src/main/resources/licenses/notice.txt | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/search-services/packaging/src/main/resources/licenses/notice.txt b/search-services/packaging/src/main/resources/licenses/notice.txt index 7159ed12a..49cf1ee57 100644 --- a/search-services/packaging/src/main/resources/licenses/notice.txt +++ b/search-services/packaging/src/main/resources/licenses/notice.txt @@ -174,28 +174,28 @@ jul-to-slf4j-1.7.7.jar juniversalchardet-1.0.3.jar langdetect-1.1-20120112.jar log4j-1.2.17.jar -lucene-analyzers-common-6.6.5-patched.2.jar -lucene-analyzers-icu-6.6.5-patched.2.jar -lucene-analyzers-kuromoji-6.6.5-patched.2.jar -lucene-analyzers-morfologik-6.6.5-patched.2.jar -lucene-analyzers-phonetic-6.6.5-patched.2.jar -lucene-analyzers-smartcn-6.6.5-patched.2.jar -lucene-analyzers-stempel-6.6.5-patched.2.jar -lucene-backward-codecs-6.6.5-patched.2.jar -lucene-classification-6.6.5-patched.2.jar -lucene-codecs-6.6.5-patched.2.jar -lucene-core-6.6.5-patched.2.jar -lucene-expressions-6.6.5-patched.2.jar -lucene-grouping-6.6.5-patched.2.jar -lucene-highlighter-6.6.5-patched.2.jar -lucene-join-6.6.5-patched.2.jar -lucene-memory-6.6.5-patched.2.jar -lucene-misc-6.6.5-patched.2.jar -lucene-queries-6.6.5-patched.2.jar -lucene-queryparser-6.6.5-patched.2.jar -lucene-sandbox-6.6.5-patched.2.jar -lucene-spatial-extras-6.6.5-patched.2.jar -lucene-suggest-6.6.5-patched.2.jar +lucene-analyzers-common-6.6.5-patched.3.jar +lucene-analyzers-icu-6.6.5-patched.3.jar +lucene-analyzers-kuromoji-6.6.5-patched.3.jar +lucene-analyzers-morfologik-6.6.5-patched.3.jar +lucene-analyzers-phonetic-6.6.5-patched.3.jar +lucene-analyzers-smartcn-6.6.5-patched.3.jar +lucene-analyzers-stempel-6.6.5-patched.3.jar +lucene-backward-codecs-6.6.5-patched.3.jar +lucene-classification-6.6.5-patched.3.jar +lucene-codecs-6.6.5-patched.3.jar +lucene-core-6.6.5-patched.3.jar +lucene-expressions-6.6.5-patched.3.jar +lucene-grouping-6.6.5-patched.3.jar +lucene-highlighter-6.6.5-patched.3.jar +lucene-join-6.6.5-patched.3.jar +lucene-memory-6.6.5-patched.3.jar +lucene-misc-6.6.5-patched.3.jar +lucene-queries-6.6.5-patched.3.jar +lucene-queryparser-6.6.5-patched.3.jar +lucene-sandbox-6.6.5-patched.3.jar +lucene-spatial-extras-6.6.5-patched.3.jar +lucene-suggest-6.6.5-patched.3.jar metadata-extractor-2.9.1.jar metrics-core-3.2.2.jar metrics-ganglia-3.2.2.jar @@ -219,11 +219,11 @@ rome-1.5.1.jar simple-xml-2.7.1.jar slf4j-api-1.7.7.jar slf4j-log4j12-1.7.7.jar -solr-analysis-extras-6.6.5-patched.2.jar -solr-clustering-6.6.5-patched.2.jar -solr-core-6.6.5-patched.2.jar -solr-langid-6.6.5-patched.2.jar -solr-solrj-6.6.5-patched.2.jar +solr-analysis-extras-6.6.5-patched.3.jar +solr-clustering-6.6.5-patched.3.jar +solr-core-6.6.5-patched.3.jar +solr-langid-6.6.5-patched.3.jar +solr-solrj-6.6.5-patched.3.jar spatial4j-0.6.jar start.jar stax2-api-3.1.4.jar From 76c99e8acd05e59f5af1d025717de13295c99b70 Mon Sep 17 00:00:00 2001 From: elia Date: Fri, 4 Dec 2020 10:59:15 +0100 Subject: [PATCH 4/8] [SEARCH-2548] made version field not returned by default --- .../alfresco/solr/component/RewriteFieldListComponent.java | 6 +++--- .../solr/instance/templates/rerank/conf/schema.xml | 2 +- .../solr/transformer/AlfrescoFieldMapperTransformerIT.java | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java index c539c3c45..345494ec4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java @@ -50,7 +50,7 @@ import static java.util.Optional.ofNullable; * Transform the fieldlist depending on the use of cached transformer: * [cached] -> add to the field list the translations of the fiels to the internal schema notation * otherwise -> modify the field list in order to contains a subset of the following fields: - * id, DBID, _version_ and score + * id, DBID, and score */ public class RewriteFieldListComponent extends SearchComponent { @@ -63,7 +63,7 @@ public class RewriteFieldListComponent extends SearchComponent { { Set fieldListSet = new HashSet<>(); - Set defaultNonCachedFields = Set.of("id","DBID", "_version_"); + Set defaultNonCachedFields = Set.of("id","DBID"); Set allowedNonCachedFields = new HashSet<>(defaultNonCachedFields); allowedNonCachedFields.add("score"); @@ -79,7 +79,7 @@ public class RewriteFieldListComponent extends SearchComponent { // In case cache transformer is no set, we need to modify the field list in order return - // only id, DBID and _version_ fields + // only id, DBID fields if (!cacheTransformer){ if (!solrReturnFields.wantsAllFields()) { diff --git a/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/schema.xml b/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/schema.xml index edd39f10e..59da63c6c 100644 --- a/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/schema.xml +++ b/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/schema.xml @@ -26,7 +26,7 @@ - retrievable(stored or docValues) - not multiValued --> - + diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java index 5608de147..b2f69d937 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java @@ -95,12 +95,10 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute SolrDocumentList results = resp.getResults(); assertEquals("Expecting 5 rows",5, results.size()); SolrDocument doc = results.get(0); - assertEquals(3, doc.size()); + assertEquals(2, doc.size()); assertNotNull(doc); String id = (String) doc.get("id"); assertNotNull(id); - Long version = (Long) doc.get("_version_"); - assertNotNull(version); Long dbid = (Long) doc.get("DBID"); assertNotNull(dbid); //Not expected to see below as part of the solr response. From 191a8d65eedfc3bbd5e15fcaaea15a2da498b52a Mon Sep 17 00:00:00 2001 From: Angel Borroy Date: Mon, 5 Apr 2021 10:20:19 +0200 Subject: [PATCH 5/8] SEARCH-2548: Disable documentCache for SOLR Core, as properties are not re-used from SOLR side ever. --- .../solr/instance/templates/rerank/conf/solrcore.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/solrcore.properties b/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/solrcore.properties index f2c1b9a2d..9d1901b4a 100644 --- a/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/solrcore.properties +++ b/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/solrcore.properties @@ -103,8 +103,8 @@ solr.filterCache.size=256 solr.filterCache.initialSize=128 solr.queryResultCache.size=1024 solr.queryResultCache.initialSize=1024 -solr.documentCache.size=1024 -solr.documentCache.initialSize=1024 +solr.documentCache.size=0 +solr.documentCache.initialSize=0 solr.queryResultMaxDocsCached=2048 solr.authorityCache.size=128 @@ -155,7 +155,7 @@ solr.deniedCache.autowarmCount=0 solr.readerCache.autowarmCount=0 solr.ownerCache.autowarmCount=0 solr.queryResultCache.autowarmCount=4 -solr.documentCache.autowarmCount=512 +solr.documentCache.autowarmCount=0 solr.queryResultWindowSize=512 From 3dd88e1a040b5e9f0e5652d98c1fc86553448675 Mon Sep 17 00:00:00 2001 From: Angel Borroy Date: Mon, 5 Apr 2021 16:19:06 +0200 Subject: [PATCH 6/8] SEARCH-2548: Upgrade license headers --- .../java/org/alfresco/search/TestGroup.java | 114 +- .../functional/AbstractE2EFunctionalTest.java | 2 +- .../cmis/AbstractCmisE2ETest.java | 2 +- .../cmis/SolrSearchByAspectTests.java | 2 +- .../cmis/SolrSearchByIdTests.java | 2 +- .../cmis/SolrSearchByPathTests.java | 2 +- .../cmis/SolrSearchByPropertyTests.java | 2 +- .../cmis/SolrSearchInFolderTests.java | 2 +- .../cmis/SolrSearchInTreeTests.java | 2 +- .../cmis/SolrSearchScoreQueryTests.java | 2 +- .../cmis/SorlSearchSimpleQueryTests.java | 2 +- .../searchServices/sanity/SetupTest.java | 4 +- .../search/AbstractSearchServicesE2ETest.java | 2 +- .../search/ExplicitRoutingTest.java | 2 +- .../search/FacetFieldsSearchTest.java | 2 +- .../search/FacetIntervalSearchTest.java | 2 +- .../search/FacetRangeSearchTest.java | 4 +- .../search/FacetedSearchTest.java | 4 +- .../search/FieldDefinitionTest.java | 2 +- .../search/FingerPrintTest.java | 2 +- .../search/PivotFacetedSearchTest.java | 2 +- .../search/SearchAFTSInFieldTest.java | 2 +- .../search/SearchAPATHTest.java | 4 +- .../search/SearchAspectTest.java | 2 +- .../search/SearchHighLightTest.java | 2 +- .../search/SearchNonIndexedFields.java | 2 +- .../search/SearchParentField.java | 2 +- .../search/SearchPermissionsTest.java | 2 +- .../search/SearchQueryPaginationTest.java | 2 +- .../SearchSecondaryAssociationTest.java | 2 +- .../search/SearchSpellCheckTest.java | 4 +- .../searchServices/search/SearchTest.java | 2 +- .../search/SearchWithCustomModelTest.java | 2 +- .../searchServices/search/ShardInfoTest.java | 2 +- .../search/SpecialCharacterSearchTest.java | 2 +- .../search/StatsSearchTest.java | 4 +- .../tracker/CascadingIntegrationTest.java | 2 +- .../solr/SearchSolrAPITest.java | 4 +- .../solr/admin/SolrE2eActionTest.java | 2 +- .../solr/admin/SolrE2eAdminTest.java | 2 +- .../backup/AbstractBackupE2ETest.java | 2 +- .../backup/SearchServicesOnBackupTests.java | 2 +- .../backup/SearchServicesPostBackupTests.java | 2 +- .../backup/SearchServicesPreBackupTests.java | 2 +- .../upgrade/AbstractUpgradeE2ETest.java | 4 +- .../upgrade/SearchServiceUpgradeTests.java | 2 +- .../lucene/analysis/AnalysisException.java | 126 +- .../impl/lucene/analysis/MLAnalayser.java | 782 +- .../lucene/analysis/MLTokenDuplicator.java | 568 +- .../impl/lucene/analysis/PathAnalyzer.java | 94 +- .../impl/lucene/analysis/PathTokenFilter.java | 792 +- .../solr/AlfrescoAnalyzerWrapper.java | 218 +- .../AlfrescoCollatableMLTextFieldType.java | 546 +- .../solr/AlfrescoCollatableTextFieldType.java | 490 +- .../solr/AlfrescoCoreAdminHandler.java | 4 +- .../org/alfresco/solr/AlfrescoFieldType.java | 542 +- .../alfresco/solr/AlfrescoLockException.java | 2 +- ...lfrescoSolr4FunctionEvaluationContext.java | 152 +- .../alfresco/solr/AlfrescoSolrDataModel.java | 3784 +++--- .../main/java/org/alfresco/solr/Cloud.java | 336 +- .../org/alfresco/solr/ContextAwareQuery.java | 240 +- .../org/alfresco/solr/DualPivotQuickSort.java | 2 +- .../org/alfresco/solr/HandlerOfResources.java | 2 +- .../alfresco/solr/HandlerReportHelper.java | 4 +- .../org/alfresco/solr/InformationServer.java | 2 +- .../alfresco/solr/SolrInformationServer.java | 4 +- .../alfresco/solr/SolrKeyResourceLoader.java | 152 +- .../alfresco/solr/StripLocaleStrField.java | 4 +- .../alfresco/solr/TrackerStateException.java | 158 +- .../solr/adapters/SolrOpenBitSetAdapter.java | 154 +- .../solr/adapters/SolrSimpleOrderedMap.java | 78 +- .../solr/cache/AuthorityCacheRegenerator.java | 116 +- .../alfresco/solr/cache/CacheConstants.java | 96 +- .../solr/cache/PathCacheRegenerator.java | 118 +- .../solr/component/ClearLocaleComponent.java | 156 +- .../solr/component/ConsistencyComponent.java | 210 +- .../solr/component/FingerPrintComponent.java | 4 +- .../solr/component/QueryLoggingComponent.java | 2 +- .../RewriteFacetCountsComponent.java | 558 +- .../RewriteFacetParametersComponent.java | 1136 +- .../component/RewriteFieldListComponent.java | 2 +- .../solr/component/SetLocaleComponent.java | 178 +- .../SetProcessedDeniesComponent.java | 174 +- .../solr/component/TempFileWarningLogger.java | 218 +- ...AlfrescoSpellCheckBackCompatComponent.java | 2 +- .../AlfrescoSpellCheckCollation.java | 110 +- .../AlfrescoSpellCheckCollator.java | 738 +- .../AlfrescoSpellCheckComponent.java | 232 +- .../org/alfresco/solr/config/ConfigUtil.java | 2 +- .../org/alfresco/solr/data/GlobalReaders.java | 108 +- .../solr/lifecycle/SolrCoreLoadListener.java | 4 +- .../java/org/alfresco/solr/logging/Log.java | 2 +- .../AbsoluteStructuredFieldPosition.java | 284 +- .../solr/query/AbstractAuthorityQuery.java | 152 +- .../query/AbstractAuthorityQueryWeight.java | 170 +- .../solr/query/AbstractAuthoritySetQuery.java | 384 +- .../alfresco/solr/query/AbstractQParser.java | 1414 +- .../solr/query/AbstractSolrCachingScorer.java | 396 +- .../AbstractStructuredFieldPosition.java | 338 +- .../solr/query/AlfrescoDefaultTextFields.java | 2 +- .../solr/query/AlfrescoFTSQParserPlugin.java | 306 +- .../query/AlfrescoLuceneQParserPlugin.java | 298 +- .../query/AlfrescoReRankQParserPlugin.java | 1044 +- .../solr/query/AllAccessCollector.java | 74 +- .../query/AnyStructuredFieldPosition.java | 206 +- .../org/alfresco/solr/query/BitsFilter.java | 210 +- .../solr/query/CachingTermPositions.java | 420 +- .../solr/query/CmisQParserPlugin.java | 414 +- .../ContentSizeGroupingAnalyticsQuery.java | 126 +- .../query/ContentSizeGroupingCollector.java | 262 +- .../ContentSizeGroupingQParserPlugin.java | 220 +- ...cendantAndSelfStructuredFieldPosition.java | 106 +- .../alfresco/solr/query/DocValuesCache.java | 286 +- .../solr/query/EmptyHybridBitSet.java | 80 +- .../org/alfresco/solr/query/HybridBitSet.java | 160 +- .../Lucene4QueryBuilderContextSolrImpl.java | 212 +- .../solr/query/Lucene4QueryParserAdaptor.java | 854 +- .../query/MimetypeGroupingAnalyticsQuery.java | 130 +- .../solr/query/MimetypeGroupingCollector.java | 264 +- .../query/MimetypeGroupingQParserPlugin.java | 326 +- .../alfresco/solr/query/PostFilterQuery.java | 514 +- .../RelativeStructuredFieldPosition.java | 284 +- .../SelfAxisStructuredFieldPosition.java | 124 +- .../alfresco/solr/query/Solr4QueryParser.java | 11126 ++++++++-------- .../solr/query/SolrAuthorityQuery.java | 182 +- .../solr/query/SolrAuthorityScorer.java | 218 +- .../solr/query/SolrAuthoritySetQuery.java | 744 +- .../solr/query/SolrAuthoritySetScorer.java | 272 +- .../solr/query/SolrCachingPathQuery.java | 216 +- .../solr/query/SolrCachingPathScorer.java | 144 +- .../solr/query/SolrCachingPathWeight.java | 168 +- .../solr/query/SolrContainerScorer.java | 1162 +- .../alfresco/solr/query/SolrDeniedQuery.java | 180 +- .../alfresco/solr/query/SolrDeniedScorer.java | 178 +- .../alfresco/solr/query/SolrDenySetQuery.java | 312 +- .../solr/query/SolrDenySetScorer.java | 2 +- .../solr/query/SolrDenySetScorer2.java | 250 +- .../alfresco/solr/query/SolrOwnerQuery.java | 254 +- .../alfresco/solr/query/SolrOwnerScorer.java | 150 +- .../solr/query/SolrOwnerSetQuery.java | 184 +- .../solr/query/SolrOwnerSetScorer.java | 184 +- .../alfresco/solr/query/SolrPathQuery.java | 674 +- .../alfresco/solr/query/SolrPathScorer.java | 256 +- .../alfresco/solr/query/SolrReaderQuery.java | 182 +- .../alfresco/solr/query/SolrReaderScorer.java | 178 +- .../solr/query/SolrReaderSetQuery.java | 184 +- .../solr/query/SolrReaderSetScorer.java | 208 +- .../solr/query/SolrReaderSetScorer2.java | 242 +- .../alfresco/solr/query/SolrXPathHandler.java | 1066 +- .../solr/query/StructuredFieldPosition.java | 248 +- .../solr/query/StructuredFieldTerm.java | 130 +- .../schema/highlight/CompositeReader.java | 4 +- .../LanguagePrefixedTextAnalyzer.java | 4 +- .../highlight/LanguagePrefixedTextField.java | 4 +- .../LanguagePrefixedTokenStream.java | 4 +- .../solr/schema/highlight/package-info.java | 4 +- .../solr/servlet/Solr4X509ServletFilter.java | 578 +- .../alfresco/solr/tracker/ACLIDModRouter.java | 4 +- .../solr/tracker/ACLIDMurmurRouter.java | 4 +- .../solr/tracker/AbstractTracker.java | 944 +- .../alfresco/solr/tracker/AbstractWorker.java | 2 +- .../org/alfresco/solr/tracker/AclTracker.java | 1878 +-- .../solr/tracker/ActivatableTracker.java | 8 +- .../alfresco/solr/tracker/CascadeTracker.java | 2 +- .../alfresco/solr/tracker/CommitTracker.java | 2 +- .../solr/tracker/ComposableDocRouter.java | 2 +- .../alfresco/solr/tracker/ContentTracker.java | 2 +- .../solr/tracker/DBIDRangeRouter.java | 2 +- .../org/alfresco/solr/tracker/DBIDRouter.java | 4 +- .../solr/tracker/DateMonthRouter.java | 2 +- .../solr/tracker/DateQuarterRouter.java | 4 +- .../org/alfresco/solr/tracker/DocRouter.java | 2 +- .../solr/tracker/DocRouterFactory.java | 2 +- .../solr/tracker/DocRouterWithFallback.java | 2 +- ...licitShardIdWithDynamicPropertyRouter.java | 2 +- ...plicitShardIdWithStaticPropertyRouter.java | 4 +- .../solr/tracker/MetadataTracker.java | 2 +- .../alfresco/solr/tracker/ModelTracker.java | 1160 +- .../alfresco/solr/tracker/PropertyRouter.java | 2 +- .../solr/tracker/ShardStatePublisher.java | 2 +- .../solr/tracker/SolrTrackerScheduler.java | 2 +- .../org/alfresco/solr/tracker/Tracker.java | 2 +- .../org/alfresco/solr/tracker/TrackerJob.java | 148 +- .../solr/tracker/TrackerRegistry.java | 212 +- .../pool/DefaultTrackerPoolFactory.java | 358 +- .../solr/tracker/pool/TrackerPoolFactory.java | 78 +- .../AlfrescoFieldMapperTransformer.java | 4 +- ...AlfrescoFieldMapperTransformerFactory.java | 2 +- .../transformer/DocValueDocTransformer.java | 378 +- .../DocValueDocTransformerFactory.java | 100 +- ...escoDistributedUpdateProcessorFactory.java | 8 +- .../AlfrescoExplicitDocumentMerger.java | 8 +- .../solr/utils/AlfrescoFileUtils.java | 2 +- .../alfresco/solr/utils/ThrowingConsumer.java | 2 +- .../alfresco/solr/utils/ThrowingFunction.java | 2 +- .../java/org/alfresco/solr/utils/Utils.java | 4 +- .../minhash/ContextAccumulatingFilter.java | 2 +- .../ContextAccumulatingFilterFactory.java | 2 +- .../solr/core/CoreDescriptorDecorator.java | 216 +- .../component/AlfrescoHttpShardHandler.java | 102 +- .../AlfrescoHttpShardHandlerFactory.java | 100 +- .../component/AlfrescoLukeRequestHandler.java | 1704 +-- .../component/AlfrescoQueryRequest.java | 154 +- .../component/AlfrescoSearchHandler.java | 1430 +- .../component/AlfrescoSolrHighlighter.java | 924 +- .../impl/lucene/analysis/MLAnalayserTest.java | 364 +- .../lucene/analysis/PathTokenFilterTest.java | 440 +- .../solr/AbstractAlfrescoDistributedIT.java | 4 +- .../alfresco/solr/AbstractAlfrescoSolrIT.java | 2 +- .../solr/AdminHandlerDistributedIT.java | 4 +- .../org/alfresco/solr/AdminHandlerIT.java | 2 +- ...AlfrescoCollatableMLTextFieldTypeTest.java | 416 +- .../AlfrescoCollatableTextFieldTypeTest.java | 352 +- .../solr/AlfrescoCoreAdminHandlerIT.java | 4 +- .../solr/AlfrescoCoreAdminHandlerTest.java | 4 +- ...dexingStatePersistenceAcrossReloadsIT.java | 4 +- .../solr/AlfrescoJsonQueryRequest.java | 2 +- .../alfresco/solr/AlfrescoSolrConstants.java | 4 +- .../alfresco/solr/AlfrescoSolrReloadIT.java | 4 +- .../org/alfresco/solr/AlfrescoSolrUtils.java | 4 +- .../solr/AlfrescoTrackerRegistrationIT.java | 2 +- .../alfresco/solr/CMISDataCreatorTest.java | 900 +- .../solr/CoresCreateUpdateDistributedIT.java | 4 +- .../solr/CoresCreateViaPropertyIT.java | 2 +- .../alfresco/solr/DynamicCopyFieldsIT.java | 2 +- .../alfresco/solr/HandlerOfResourcesTest.java | 4 +- .../org/alfresco/solr/SolrDataModelTest.java | 112 +- .../org/alfresco/solr/SolrITInitializer.java | 4 +- .../solr/SolrInformationServerTest.java | 842 +- .../java/org/alfresco/solr/SolrTestFiles.java | 2 +- .../alfresco/solr/TemplatesDistributedIT.java | 2 +- .../alfresco/solr/basics/RandomSupplier.java | 2 +- .../solr/basics/SolrResponsesComparator.java | 2 +- .../RewriteFacetParametersComponentTest.java | 660 +- .../component/TempFileWarningLoggerTest.java | 358 +- .../alfresco/solr/config/ConfigUtilTest.java | 4 +- .../solr/dataload/TestDataProvider.java | 4 +- .../AlfrescoHighligherDistributedIT.java | 2 +- .../solr/highlight/AlfrescoHighlighterIT.java | 4 +- .../lifecycle/SolrCoreLoadListenerTest.java | 4 +- .../solr/query/AlfrescoLuceneQParserTest.java | 124 +- .../query/AlfrescoReRankQParserPluginIT.java | 1410 +- .../solr/query/AlfrescoSolrFingerprintIT.java | 2 +- .../solr/query/AlfrescoSolrSortIT.java | 4 +- .../query/AlfrescoSolrSpellcheckerIT.java | 2 +- .../org/alfresco/solr/query/AuthDataLoad.java | 2 +- .../org/alfresco/solr/query/AuthQueryIT.java | 2 +- .../DistributedAlfrescoSolrFacetingIT.java | 2 +- .../DistributedAlfrescoSolrFingerPrintIT.java | 4 +- ...DistributedAlfrescoSolrSpellcheckerIT.java | 2 +- .../solr/query/Solr4QueryParserTest.java | 2 +- .../org/alfresco/solr/query/SolrAuthIT.java | 366 +- .../solr/query/UntokenisedFieldIT.java | 2 +- .../afts/qparser/AbstractQParserPluginIT.java | 2 +- .../afts/qparser/FieldNameEscapingIT.java | 4 +- .../query/afts/qparser/QParserPluginIT.java | 4 +- .../AFTSDefaultTextQueryIT.java | 2 +- .../requestHandler/AFTSDisjunctionIT.java | 4 +- .../AFTSIdentifierFieldsIT.java | 4 +- .../afts/requestHandler/AFTSRangeQueryIT.java | 4 +- .../requestHandler/AFTSRequestHandlerIT.java | 4 +- .../AbstractRequestHandlerIT.java | 2 +- .../solr/query/afts/requestHandler/MNTIT.java | 4 +- .../cmis/AlfrescoCMISQParserPluginIT.java | 4 +- .../org/alfresco/solr/query/cmis/CmisIT.java | 2 +- .../solr/query/cmis/LoadCMISData.java | 2 +- .../solr/query/cmis/SortCMISTest.java | 2 +- .../solr/query/cmis/TrieFieldsCMISTest.java | 2 +- .../schema/highlight/CompositeReaderTest.java | 2 +- .../LanguagePrefixedTokenStreamTest.java | 2 +- .../servlet/Solr4X509ServletFilterTest.java | 2 +- .../solr/tracker/ACLIDMurmurRouterTest.java | 4 +- .../solr/tracker/AclModCountRouterIT.java | 4 +- .../solr/tracker/ActivatableTrackerTest.java | 8 +- .../AlfrescoSolrTrackerExceptionIT.java | 2 +- .../solr/tracker/AlfrescoSolrTrackerIT.java | 2 +- .../AlfrescoSolrTrackerRollbackIT.java | 2 +- .../tracker/AlfrescoSolrTrackerStateIT.java | 4 +- .../alfresco/solr/tracker/CascadingIT.java | 4 +- .../ContentPropertyValueTrackerIT.java | 4 +- .../solr/tracker/ContentTrackerIT.java | 2 +- .../solr/tracker/DBIDRangeRouterTest.java | 4 +- .../alfresco/solr/tracker/DBIDRouterTest.java | 4 +- .../solr/tracker/DateMonthRouterIT.java | 4 +- ...DistributedAclIdAlfrescoSolrTrackerIT.java | 2 +- .../DistributedAlfrescoSolrJsonIT.java | 2 +- .../DistributedAlfrescoSolrTrackerIT.java | 4 +- ...ibutedAlfrescoSolrTrackerLargeTxnIdIT.java | 4 +- .../DistributedAlfrescoSolrTrackerRaceIT.java | 4 +- ...DistributedAlfrescoSolrTrackerStateIT.java | 4 +- ...rescoTrackerWithDelayedTransactionsIT.java | 8 +- .../tracker/DistributedCascadeTrackerIT.java | 4 +- .../DistributedDateAbstractSolrTrackerIT.java | 2 +- ...ributedDateMonthAlfrescoSolrTrackerIT.java | 2 +- ...butedDateQuarterAlfrescoSolrTrackerIT.java | 2 +- ...tedDateSplitYearAlfrescoSolrTrackerIT.java | 2 +- ...ributedDbidRangeAlfrescoSolrTrackerIT.java | 2 +- ...dExpandDbidRangeAlfrescoSolrTrackerIT.java | 2 +- ...icitShardIdWithStaticPropertyRouterIT.java | 4 +- ...tributedExplicitShardRoutingTrackerIT.java | 2 +- ...tedPropertyBasedAlfrescoSolrTrackerIT.java | 2 +- ...RemovePropertiesAlfrescoSolrTrackerIT.java | 10 +- .../solr/tracker/DocRouterFactoryTest.java | 136 +- .../solr/tracker/ExplicitIDRouterIT.java | 4 +- .../tracker/ExplicitIDWithLRISRouterIT.java | 4 +- ...tShardIdWithDynamicPropertyRouterTest.java | 4 +- ...itShardIdWithStaticPropertyRouterTest.java | 4 +- .../solr/tracker/MetadataTrackerTest.java | 2 +- .../alfresco/solr/tracker/ModelTrackerIT.java | 400 +- .../solr/tracker/PropertyRouterIT.java | 4 +- .../tracker/SolrTrackerSchedulerTest.java | 432 +- .../alfresco/solr/tracker/TrackerJobTest.java | 158 +- .../solr/tracker/TrackerRegistryTest.java | 2 +- .../pool/DefaultTrackerPoolFactoryTest.java | 322 +- .../AlfrescoFieldMapperTransformerIT.java | 2 +- .../AlfrescoLukeRequestHandlerIT.java | 2 +- .../AlfrescoSearchHandlerFacetQueryIT.java | 2 +- .../java/org/alfresco/solr/AclReport.java | 2 +- ...lfrescoClientDataModelServicesFactory.java | 2 +- .../java/org/alfresco/solr/BoundedDeque.java | 2 +- .../solr/IndexTrackingShutdownException.java | 2 +- .../InformationServerCollectionProvider.java | 2 +- .../java/org/alfresco/solr/NodeReport.java | 2 +- .../java/org/alfresco/solr/TrackerState.java | 2 +- .../alfresco/solr/adapters/IOpenBitSet.java | 36 +- .../solr/adapters/ISimpleOrderedMap.java | 30 +- .../java/org/alfresco/solr/client/Acl.java | 114 +- .../alfresco/solr/client/AclChangeSet.java | 132 +- .../alfresco/solr/client/AclChangeSets.java | 2 +- .../org/alfresco/solr/client/AclReaders.java | 250 +- .../alfresco/solr/client/AlfrescoModel.java | 166 +- .../solr/client/AlfrescoModelDiff.java | 212 +- .../solr/client/ContentPropertyValue.java | 120 +- .../solr/client/GetNodesParameters.java | 346 +- .../solr/client/LookAheadBufferedReader.java | 4 +- .../solr/client/MLTextPropertyValue.java | 110 +- .../solr/client/MultiPropertyValue.java | 132 +- .../java/org/alfresco/solr/client/Node.java | 288 +- .../alfresco/solr/client/NodeMetaData.java | 364 +- .../solr/client/NodeMetaDataParameters.java | 484 +- .../alfresco/solr/client/PropertyValue.java | 24 +- .../alfresco/solr/client/SOLRAPIClient.java | 3274 ++--- .../solr/client/SOLRAPIClientFactory.java | 2 +- .../solr/client/SOLRAPIQueueClient.java | 2 +- .../java/org/alfresco/solr/client/Status.java | 382 +- .../solr/client/StringPropertyValue.java | 58 +- .../org/alfresco/solr/client/Transaction.java | 174 +- .../alfresco/solr/client/Transactions.java | 78 +- .../solr/tracker/IndexHealthReport.java | 820 +- .../alfresco/solr/tracker/TrackerStats.java | 1908 +-- .../org/alfresco/solr/BoundedDequeTest.java | 114 +- .../alfresco/solr/client/AclReadersTest.java | 122 +- .../client/LookAheadBufferedReaderTest.java | 4 +- .../solr/client/SOLRAPIClientFactoryTest.java | 202 +- .../solr/client/SOLRAPIClientTest.java | 1368 +- .../org/alfresco/ThirdPartyLicensesIT.java | 2 +- 356 files changed, 36492 insertions(+), 36492 deletions(-) diff --git a/e2e-test/src/main/java/org/alfresco/search/TestGroup.java b/e2e-test/src/main/java/org/alfresco/search/TestGroup.java index 3e981287f..1f319397e 100644 --- a/e2e-test/src/main/java/org/alfresco/search/TestGroup.java +++ b/e2e-test/src/main/java/org/alfresco/search/TestGroup.java @@ -1,57 +1,57 @@ -/* - * #%L - * Alfresco Search Services E2E Test - * %% - * Copyright (C) 2005 - 2020 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.search; - -public class TestGroup -{ - // Used for TestRail test annotation - public static final String SEARCH = "search"; - public static final String REST_API = "rest-api"; - - public static final String PREUPGRADE = "pre-upgrade"; - public static final String POSTUPGRADE = "post-upgrade"; - - public static final String CONFIG_MASTER_SLAVE = "CONFIG_Master_Slave"; // Alfresco Search Services using master slave configurations - public static final String CONFIG_MASTER ="CONFIG_Master"; // Alfresco search services using master/stand alone mode - - public static final String CONFIG_SHARDING ="CONFIG_Sharding"; // Alfresco search services using sharded environment - public static final String CONFIG_SHARDING_EXPLICIT ="CONFIG_Sharding_EXPLICIT"; // Alfresco search services using sharded environment and explicit routing - public static final String CONFIG_SHARDING_DB_ID_RANGE = "CONFIG_Sharding_DB_ID_RANGE"; // Alfresco Search Services using Sharding with DB_ID_RANGE - - public static final String CONFIG_ENABLED_CASCADE_TRACKER ="Config_Enabled_Cascade_Tracker"; // Alfresco search services does not index fields related to cascaded updates - - public static final String NOT_INSIGHT_ENGINE = "Not_InsightEngine"; // When Alfresco Insight Engine 1.0 isn't running - - public static final String ACS_52n = "ACS_52n"; // Alfresco Content Services 5.2.n - public static final String ACS_60n = "ACS_60n"; // Alfresco Content Services 6.0 or above - public static final String ACS_61n = "ACS_61n"; // Alfresco Content Services 6.1 or above - public static final String ACS_611n = "ACS_611n"; // Alfresco Content Services 6.1.1 or above - public static final String ACS_62n = "ACS_62n"; // Alfresco Content Services 6.2 or above - public static final String ACS_63n = "ACS_63n"; // Alfresco Content Services 6.3 or above - - public static final String AGS_302 = "AGS_302"; // Alfresco governance Services 3.0.2 or above -} +/* + * #%L + * Alfresco Search Services E2E Test + * %% + * Copyright (C) 2005 - 2021 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.search; + +public class TestGroup +{ + // Used for TestRail test annotation + public static final String SEARCH = "search"; + public static final String REST_API = "rest-api"; + + public static final String PREUPGRADE = "pre-upgrade"; + public static final String POSTUPGRADE = "post-upgrade"; + + public static final String CONFIG_MASTER_SLAVE = "CONFIG_Master_Slave"; // Alfresco Search Services using master slave configurations + public static final String CONFIG_MASTER ="CONFIG_Master"; // Alfresco search services using master/stand alone mode + + public static final String CONFIG_SHARDING ="CONFIG_Sharding"; // Alfresco search services using sharded environment + public static final String CONFIG_SHARDING_EXPLICIT ="CONFIG_Sharding_EXPLICIT"; // Alfresco search services using sharded environment and explicit routing + public static final String CONFIG_SHARDING_DB_ID_RANGE = "CONFIG_Sharding_DB_ID_RANGE"; // Alfresco Search Services using Sharding with DB_ID_RANGE + + public static final String CONFIG_ENABLED_CASCADE_TRACKER ="Config_Enabled_Cascade_Tracker"; // Alfresco search services does not index fields related to cascaded updates + + public static final String NOT_INSIGHT_ENGINE = "Not_InsightEngine"; // When Alfresco Insight Engine 1.0 isn't running + + public static final String ACS_52n = "ACS_52n"; // Alfresco Content Services 5.2.n + public static final String ACS_60n = "ACS_60n"; // Alfresco Content Services 6.0 or above + public static final String ACS_61n = "ACS_61n"; // Alfresco Content Services 6.1 or above + public static final String ACS_611n = "ACS_611n"; // Alfresco Content Services 6.1.1 or above + public static final String ACS_62n = "ACS_62n"; // Alfresco Content Services 6.2 or above + public static final String ACS_63n = "ACS_63n"; // Alfresco Content Services 6.3 or above + + public static final String AGS_302 = "AGS_302"; // Alfresco governance Services 3.0.2 or above +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/AbstractE2EFunctionalTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/AbstractE2EFunctionalTest.java index 6f377f23a..957a2ba0d 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/AbstractE2EFunctionalTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/AbstractE2EFunctionalTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/AbstractCmisE2ETest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/AbstractCmisE2ETest.java index 2d4eb2367..9acae472c 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/AbstractCmisE2ETest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/AbstractCmisE2ETest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByAspectTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByAspectTests.java index 9cfca22f7..f4e7b1721 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByAspectTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByAspectTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByIdTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByIdTests.java index dd9b27723..11ed7cd80 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByIdTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByIdTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPathTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPathTests.java index d135a14dc..25fd3ef8e 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPathTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPathTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPropertyTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPropertyTests.java index 8f5c6d3c3..9f395802b 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPropertyTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPropertyTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInFolderTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInFolderTests.java index 3a585d81c..1d3ab3c2b 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInFolderTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInFolderTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInTreeTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInTreeTests.java index ca455cc44..48b1d7db5 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInTreeTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInTreeTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchScoreQueryTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchScoreQueryTests.java index 7b78557e3..25847c053 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchScoreQueryTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchScoreQueryTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SorlSearchSimpleQueryTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SorlSearchSimpleQueryTests.java index 9d14a0b79..4709879ee 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SorlSearchSimpleQueryTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SorlSearchSimpleQueryTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/sanity/SetupTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/sanity/SetupTest.java index 953637033..79fac07a2 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/sanity/SetupTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/sanity/SetupTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -116,4 +116,4 @@ public class SetupTest extends AbstractE2EFunctionalTest Assert.assertTrue(waitForIndexing("cm:name:" + customFile.getName(),true),"New content could not be found"); } -} \ No newline at end of file +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/AbstractSearchServicesE2ETest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/AbstractSearchServicesE2ETest.java index 4ebb66e9f..894af7a6d 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/AbstractSearchServicesE2ETest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/AbstractSearchServicesE2ETest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ExplicitRoutingTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ExplicitRoutingTest.java index 51430f28f..0423d69e5 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ExplicitRoutingTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ExplicitRoutingTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetFieldsSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetFieldsSearchTest.java index 7d0d275e4..5e1d743f3 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetFieldsSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetFieldsSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetIntervalSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetIntervalSearchTest.java index 49f235361..5a8cc9fe3 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetIntervalSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetIntervalSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetRangeSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetRangeSearchTest.java index 9688d564e..22c800c59 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetRangeSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetRangeSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -336,4 +336,4 @@ public class FacetRangeSearchTest extends AbstractSearchServicesE2ETest facetRangeModel.setGap(gap); return facetRangeModel; } -} \ No newline at end of file +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetedSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetedSearchTest.java index 76e069d24..9a321cc65 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetedSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetedSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -339,4 +339,4 @@ public class FacetedSearchTest extends AbstractSearchServicesE2ETest bucket1.assertThat().field("filterQuery").is("modifier:\"" + testUser.getUsername() + "\""); bucket1.assertThat().field("metrics").is("[{entry=null, type=count, value={count=1}}]"); } -} \ No newline at end of file +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FieldDefinitionTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FieldDefinitionTest.java index bb38e667d..2c1801dc4 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FieldDefinitionTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FieldDefinitionTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FingerPrintTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FingerPrintTest.java index 688563809..055e9ad9e 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FingerPrintTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FingerPrintTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/PivotFacetedSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/PivotFacetedSearchTest.java index d543961ac..2b52927d5 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/PivotFacetedSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/PivotFacetedSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAFTSInFieldTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAFTSInFieldTest.java index 92130b0d3..5dba5179e 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAFTSInFieldTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAFTSInFieldTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAPATHTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAPATHTest.java index 413fd3fe5..6925de7f7 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAPATHTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAPATHTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -208,4 +208,4 @@ public class SearchAPATHTest extends AbstractSearchServicesE2ETest return buckets; } -} \ No newline at end of file +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAspectTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAspectTest.java index d1cb39268..51c3ba4e8 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAspectTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAspectTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchHighLightTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchHighLightTest.java index 678db8aa8..a93aea1dd 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchHighLightTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchHighLightTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchNonIndexedFields.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchNonIndexedFields.java index af75cbd02..556e9edeb 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchNonIndexedFields.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchNonIndexedFields.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchParentField.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchParentField.java index e3e3fda88..c6f8e97fb 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchParentField.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchParentField.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchPermissionsTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchPermissionsTest.java index 3f9754831..433000274 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchPermissionsTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchPermissionsTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchQueryPaginationTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchQueryPaginationTest.java index 2abdb2842..ff3de9edb 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchQueryPaginationTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchQueryPaginationTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSecondaryAssociationTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSecondaryAssociationTest.java index 5ccbd3ea0..e52d70933 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSecondaryAssociationTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSecondaryAssociationTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSpellCheckTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSpellCheckTest.java index 86e17ec6a..c3a3bde2e 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSpellCheckTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSpellCheckTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -509,4 +509,4 @@ public class SearchSpellCheckTest extends AbstractSearchServicesE2ETest Assert.assertTrue(isContentInSearchResponse(response, file2.getName()), "Expected file not returned in the search results: " + file2.getName()); testSearchSpellcheckResponse(response, null, null); } -} \ No newline at end of file +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchTest.java index 3ecf2d9e2..933b0f787 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchWithCustomModelTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchWithCustomModelTest.java index cce905976..7439a3aac 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchWithCustomModelTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchWithCustomModelTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ShardInfoTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ShardInfoTest.java index 669dc15da..c1a706aac 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ShardInfoTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ShardInfoTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SpecialCharacterSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SpecialCharacterSearchTest.java index 4c6a5e6e2..2c8263800 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SpecialCharacterSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SpecialCharacterSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/StatsSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/StatsSearchTest.java index 074282697..657bb1252 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/StatsSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/StatsSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -338,4 +338,4 @@ public class StatsSearchTest extends AbstractSearchServicesE2ETest assertEquals(metrics.size(),metricsCount); return metrics.stream().map(RestGenericMetricModel::getType).collect(Collectors.toSet()); } -} \ No newline at end of file +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/tracker/CascadingIntegrationTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/tracker/CascadingIntegrationTest.java index cec58be0f..0f38b9372 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/tracker/CascadingIntegrationTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/tracker/CascadingIntegrationTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/SearchSolrAPITest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/SearchSolrAPITest.java index 85b0d19c0..29cf556a9 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/SearchSolrAPITest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/SearchSolrAPITest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -127,4 +127,4 @@ public class SearchSolrAPITest extends AbstractE2EFunctionalTest String errorMsg = "No QueryObjectBuilder defined for node a in {q={!xmlparser"; Assert.assertTrue(restClient.onResponse().getResponse().body().xmlPath().getString("response").contains(errorMsg)); } -} \ No newline at end of file +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eActionTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eActionTest.java index 31bea224b..c90e46f55 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eActionTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eActionTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java index 3c3718c8b..5ea6c071f 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/AbstractBackupE2ETest.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/AbstractBackupE2ETest.java index 8ef3fef2d..875492d91 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/AbstractBackupE2ETest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/AbstractBackupE2ETest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesOnBackupTests.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesOnBackupTests.java index 663370096..b02f2579c 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesOnBackupTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesOnBackupTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPostBackupTests.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPostBackupTests.java index 774b96b8d..74127a843 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPostBackupTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPostBackupTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPreBackupTests.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPreBackupTests.java index 3371c07f0..46c3436f0 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPreBackupTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPreBackupTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/AbstractUpgradeE2ETest.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/AbstractUpgradeE2ETest.java index 9e24de781..22e407f17 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/AbstractUpgradeE2ETest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/AbstractUpgradeE2ETest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -67,4 +67,4 @@ public abstract class AbstractUpgradeE2ETest extends AbstractTestNGSpringContext { serverHealth.assertServerIsOnline(); } -} \ No newline at end of file +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/SearchServiceUpgradeTests.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/SearchServiceUpgradeTests.java index b672fc320..b7797f318 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/SearchServiceUpgradeTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/SearchServiceUpgradeTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/AnalysisException.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/AnalysisException.java index 7aa414099..a930830d7 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/AnalysisException.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/AnalysisException.java @@ -1,63 +1,63 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; - -import org.alfresco.error.AlfrescoRuntimeException; - -public class AnalysisException extends AlfrescoRuntimeException -{ - - /** - * - */ - private static final long serialVersionUID = -7722380192490118459L; - - public AnalysisException(String msgId) - { - super(msgId); - // TODO Auto-generated constructor stub - } - - public AnalysisException(String msgId, Object[] msgParams) - { - super(msgId, msgParams); - // TODO Auto-generated constructor stub - } - - public AnalysisException(String msgId, Throwable cause) - { - super(msgId, cause); - // TODO Auto-generated constructor stub - } - - public AnalysisException(String msgId, Object[] msgParams, Throwable cause) - { - super(msgId, msgParams, cause); - // TODO Auto-generated constructor stub - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; + +import org.alfresco.error.AlfrescoRuntimeException; + +public class AnalysisException extends AlfrescoRuntimeException +{ + + /** + * + */ + private static final long serialVersionUID = -7722380192490118459L; + + public AnalysisException(String msgId) + { + super(msgId); + // TODO Auto-generated constructor stub + } + + public AnalysisException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + // TODO Auto-generated constructor stub + } + + public AnalysisException(String msgId, Throwable cause) + { + super(msgId, cause); + // TODO Auto-generated constructor stub + } + + public AnalysisException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + // TODO Auto-generated constructor stub + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayser.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayser.java index 334d0c231..5870598cf 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayser.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayser.java @@ -1,391 +1,391 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; -import java.util.Locale; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.solr.AlfrescoAnalyzerWrapper.Mode; -import org.alfresco.util.Pair; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.Tokenizer; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; -import org.apache.solr.schema.FieldType; -import org.apache.solr.schema.IndexSchema; -import org.springframework.extensions.surf.util.I18NUtil; - -public class MLAnalayser extends Analyzer -{ - private static Log s_logger = LogFactory.getLog(MLAnalayser.class); - - private MLAnalysisMode mlAnalaysisMode; - - private IndexSchema schema; - - private Mode mode; - - public MLAnalayser(MLAnalysisMode mlAnalaysisMode, IndexSchema schema, Mode mode) - { - super(Analyzer.PER_FIELD_REUSE_STRATEGY); - this.mlAnalaysisMode = mlAnalaysisMode; - this.schema = schema; - this.mode = mode; - } - - - public MLAnalayser(MLAnalysisMode mlAnalaysisMode) - { - super(Analyzer.PER_FIELD_REUSE_STRATEGY); - this.mlAnalaysisMode = mlAnalaysisMode; - } - - - - @Override - protected TokenStreamComponents createComponents(String fieldName) - { - MLTokenizer mltokenizer = new MLTokenizer(fieldName, schema, mlAnalaysisMode, mode); -// try -// { -// mltokenizer.setLocaleAndPositionReaderAfterLocaleEncoding(reader); -// } -// catch (IOException e) -// { -// try{mltokenizer.close();} catch(IOException ioe) {}; -// throw new AnalysisException("Failed to init MLTokenizer", e); -// } - return new LocaleAwareTokenStreamComponents(mltokenizer); - } - - private static class LocaleAwareTokenStreamComponents extends Analyzer.TokenStreamComponents - { - MLTokenizer mltokenizer; - - /** - * @param source - */ - public LocaleAwareTokenStreamComponents(final MLTokenizer source) - { - super(source); - this.mltokenizer = source; - } - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Analyzer.TokenStreamComponents#setReader(java.io.Reader) - */ - @Override - protected void setReader(Reader reader) - { - super.setReader(mltokenizer.setLocaleAndPositionReaderAfterLocaleEncoding(reader)); - } - } - - private static class MLTokenizer extends Tokenizer - { - TokenStream ts; - - String fieldName; - - private IndexSchema schema; - - MLAnalysisMode mlAnalaysisMode; - - @SuppressWarnings("unused") - private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); - - @SuppressWarnings("unused") - private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); - - @SuppressWarnings("unused") - private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); - - @SuppressWarnings("unused") - private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); - - private Mode mode; - - MLTokenizer(String fieldName, IndexSchema schema, MLAnalysisMode mlAnalaysisMode, Mode mode) - { - this.fieldName = fieldName; - this.schema = schema; - this.mlAnalaysisMode = mlAnalaysisMode; - this.mode = mode; - } - - - - /** - * @param reader - * @throws IOException - */ - public Reader setLocaleAndPositionReaderAfterLocaleEncoding(Reader reader) - { - Pair pair = getLocaleAndPositioReaderAfterLocaleEncoding(fieldName, reader); - - if(s_logger.isDebugEnabled()) - { - s_logger.debug("Created ML analyser token stream for "+fieldName+ " with locale "+pair.getFirst()); - } - TokenStream source = getAnalyser(fieldName, pair.getFirst()).tokenStream(fieldName, pair.getSecond()); - ts = new MLTokenDuplicator(source, pair.getFirst(), pair.getSecond(), mlAnalaysisMode); - return pair.getSecond(); - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#close() - */ - @Override - public void close() throws IOException - { - ts.close(); - super.close(); - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#reset() - */ - @Override - public void reset() throws IOException - { - ts.reset(); - super.reset(); - } - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#reset() - */ - @Override - public void end() throws IOException - { - ts.end(); - super.end(); - } - - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.TokenStream#incrementToken() - */ - @Override - public boolean incrementToken() throws IOException - { - clearAttributes(); - if(ts.incrementToken()) - { - ts.copyTo(this); - return true; - - } - else - { - ts.end(); - ts.close(); - return false; - } - - - - } - - - public Pair getLocaleAndPositioReaderAfterLocaleEncoding(String fieldName, Reader reader) - { - // We use read ahead to get the language info - if this does not exist we need to restart - // an use the default - there for we need mark and restore. - BufferedReader breader; - if (reader instanceof BufferedReader) - { - breader = (BufferedReader)reader; - } - else - { - breader = new BufferedReader(reader); - } - - try - { - if (!breader.markSupported()) - { - throw new AnalysisException( - "Multilingual tokenisation requires a reader that supports marks and reset"); - } - breader.mark(100); - StringBuilder builder = new StringBuilder(); - if (breader.read() == '\u0000') - { - String language = ""; - String country = ""; - String varient = ""; - char c; - int count = 0; - while ((c = (char) breader.read()) != '\u0000') - { - if (count++ > 99) - { - breader.reset(); - return new Pair(I18NUtil.getLocale(), breader); - } - if (c == '_') - { - if (language.length() == 0) - { - language = builder.toString(); - } - else if (country.length() == 0) - { - country = builder.toString(); - } - else if (varient.length() == 0) - { - varient = builder.toString(); - } - else - { - breader.reset(); - return new Pair(I18NUtil.getLocale(), breader); - } - builder = new StringBuilder(); - } - else - { - builder.append(c); - } - } - if (builder.length() > 0) - { - if (language.length() == 0) - { - language = builder.toString(); - } - else if (country.length() == 0) - { - country = builder.toString(); - } - else if (varient.length() == 0) - { - varient = builder.toString(); - } - else - { - breader.reset(); - return new Pair(I18NUtil.getLocale(), breader); - } - } - Locale locale = new Locale(language, country, varient); - // leave the reader where it is .... - return new Pair(locale, breader); - - } - else - { - breader.reset(); - return new Pair(I18NUtil.getLocale(), breader); - } - } - catch (IOException io) - { - try - { - breader.reset(); - } - catch (IOException e) - { - throw new AnalysisException("Failed to reset buffered reader - token stream will be invalid", e); - } - return null; - } - } - - - private Analyzer getAnalyser(String fieldName, Locale locale) - { - if(schema != null) - { - if(fieldName.contains("l_@{")) - { - FieldType fieldType = schema.getFieldTypeByName("identifier"); - return selectAnalyzer(fieldType); - } - else if(fieldName.contains("lt@{")) - { - StringBuilder builder = new StringBuilder(); - builder.append("text_"); - builder.append(locale.getLanguage()); - FieldType fieldType = schema.getFieldTypeByName(builder.toString()); - if(fieldType == null) - { - fieldType = schema.getFieldTypeByName("text_en"); - } - return selectAnalyzer(fieldType); - } - else - { - return null; - } - - } - else - { - return null; - } - } - - - - private Analyzer selectAnalyzer(FieldType fieldType) { - if(mode == Mode.INDEX) - { - return fieldType.getIndexAnalyzer(); - } - else if(mode == Mode.QUERY) - { - return fieldType.getQueryAnalyzer(); - } - else - { - return null; - } - } - - - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Locale; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.solr.AlfrescoAnalyzerWrapper.Mode; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.Tokenizer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.springframework.extensions.surf.util.I18NUtil; + +public class MLAnalayser extends Analyzer +{ + private static Log s_logger = LogFactory.getLog(MLAnalayser.class); + + private MLAnalysisMode mlAnalaysisMode; + + private IndexSchema schema; + + private Mode mode; + + public MLAnalayser(MLAnalysisMode mlAnalaysisMode, IndexSchema schema, Mode mode) + { + super(Analyzer.PER_FIELD_REUSE_STRATEGY); + this.mlAnalaysisMode = mlAnalaysisMode; + this.schema = schema; + this.mode = mode; + } + + + public MLAnalayser(MLAnalysisMode mlAnalaysisMode) + { + super(Analyzer.PER_FIELD_REUSE_STRATEGY); + this.mlAnalaysisMode = mlAnalaysisMode; + } + + + + @Override + protected TokenStreamComponents createComponents(String fieldName) + { + MLTokenizer mltokenizer = new MLTokenizer(fieldName, schema, mlAnalaysisMode, mode); +// try +// { +// mltokenizer.setLocaleAndPositionReaderAfterLocaleEncoding(reader); +// } +// catch (IOException e) +// { +// try{mltokenizer.close();} catch(IOException ioe) {}; +// throw new AnalysisException("Failed to init MLTokenizer", e); +// } + return new LocaleAwareTokenStreamComponents(mltokenizer); + } + + private static class LocaleAwareTokenStreamComponents extends Analyzer.TokenStreamComponents + { + MLTokenizer mltokenizer; + + /** + * @param source + */ + public LocaleAwareTokenStreamComponents(final MLTokenizer source) + { + super(source); + this.mltokenizer = source; + } + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Analyzer.TokenStreamComponents#setReader(java.io.Reader) + */ + @Override + protected void setReader(Reader reader) + { + super.setReader(mltokenizer.setLocaleAndPositionReaderAfterLocaleEncoding(reader)); + } + } + + private static class MLTokenizer extends Tokenizer + { + TokenStream ts; + + String fieldName; + + private IndexSchema schema; + + MLAnalysisMode mlAnalaysisMode; + + @SuppressWarnings("unused") + private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); + + @SuppressWarnings("unused") + private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); + + @SuppressWarnings("unused") + private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); + + @SuppressWarnings("unused") + private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); + + private Mode mode; + + MLTokenizer(String fieldName, IndexSchema schema, MLAnalysisMode mlAnalaysisMode, Mode mode) + { + this.fieldName = fieldName; + this.schema = schema; + this.mlAnalaysisMode = mlAnalaysisMode; + this.mode = mode; + } + + + + /** + * @param reader + * @throws IOException + */ + public Reader setLocaleAndPositionReaderAfterLocaleEncoding(Reader reader) + { + Pair pair = getLocaleAndPositioReaderAfterLocaleEncoding(fieldName, reader); + + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Created ML analyser token stream for "+fieldName+ " with locale "+pair.getFirst()); + } + TokenStream source = getAnalyser(fieldName, pair.getFirst()).tokenStream(fieldName, pair.getSecond()); + ts = new MLTokenDuplicator(source, pair.getFirst(), pair.getSecond(), mlAnalaysisMode); + return pair.getSecond(); + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#close() + */ + @Override + public void close() throws IOException + { + ts.close(); + super.close(); + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#reset() + */ + @Override + public void reset() throws IOException + { + ts.reset(); + super.reset(); + } + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#reset() + */ + @Override + public void end() throws IOException + { + ts.end(); + super.end(); + } + + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.TokenStream#incrementToken() + */ + @Override + public boolean incrementToken() throws IOException + { + clearAttributes(); + if(ts.incrementToken()) + { + ts.copyTo(this); + return true; + + } + else + { + ts.end(); + ts.close(); + return false; + } + + + + } + + + public Pair getLocaleAndPositioReaderAfterLocaleEncoding(String fieldName, Reader reader) + { + // We use read ahead to get the language info - if this does not exist we need to restart + // an use the default - there for we need mark and restore. + BufferedReader breader; + if (reader instanceof BufferedReader) + { + breader = (BufferedReader)reader; + } + else + { + breader = new BufferedReader(reader); + } + + try + { + if (!breader.markSupported()) + { + throw new AnalysisException( + "Multilingual tokenisation requires a reader that supports marks and reset"); + } + breader.mark(100); + StringBuilder builder = new StringBuilder(); + if (breader.read() == '\u0000') + { + String language = ""; + String country = ""; + String varient = ""; + char c; + int count = 0; + while ((c = (char) breader.read()) != '\u0000') + { + if (count++ > 99) + { + breader.reset(); + return new Pair(I18NUtil.getLocale(), breader); + } + if (c == '_') + { + if (language.length() == 0) + { + language = builder.toString(); + } + else if (country.length() == 0) + { + country = builder.toString(); + } + else if (varient.length() == 0) + { + varient = builder.toString(); + } + else + { + breader.reset(); + return new Pair(I18NUtil.getLocale(), breader); + } + builder = new StringBuilder(); + } + else + { + builder.append(c); + } + } + if (builder.length() > 0) + { + if (language.length() == 0) + { + language = builder.toString(); + } + else if (country.length() == 0) + { + country = builder.toString(); + } + else if (varient.length() == 0) + { + varient = builder.toString(); + } + else + { + breader.reset(); + return new Pair(I18NUtil.getLocale(), breader); + } + } + Locale locale = new Locale(language, country, varient); + // leave the reader where it is .... + return new Pair(locale, breader); + + } + else + { + breader.reset(); + return new Pair(I18NUtil.getLocale(), breader); + } + } + catch (IOException io) + { + try + { + breader.reset(); + } + catch (IOException e) + { + throw new AnalysisException("Failed to reset buffered reader - token stream will be invalid", e); + } + return null; + } + } + + + private Analyzer getAnalyser(String fieldName, Locale locale) + { + if(schema != null) + { + if(fieldName.contains("l_@{")) + { + FieldType fieldType = schema.getFieldTypeByName("identifier"); + return selectAnalyzer(fieldType); + } + else if(fieldName.contains("lt@{")) + { + StringBuilder builder = new StringBuilder(); + builder.append("text_"); + builder.append(locale.getLanguage()); + FieldType fieldType = schema.getFieldTypeByName(builder.toString()); + if(fieldType == null) + { + fieldType = schema.getFieldTypeByName("text_en"); + } + return selectAnalyzer(fieldType); + } + else + { + return null; + } + + } + else + { + return null; + } + } + + + + private Analyzer selectAnalyzer(FieldType fieldType) { + if(mode == Mode.INDEX) + { + return fieldType.getIndexAnalyzer(); + } + else if(mode == Mode.QUERY) + { + return fieldType.getQueryAnalyzer(); + } + else + { + return null; + } + } + + + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLTokenDuplicator.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLTokenDuplicator.java index ce476a1f4..65c19d538 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLTokenDuplicator.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLTokenDuplicator.java @@ -1,284 +1,284 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; - -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Locale; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; - -/** - * Create duplicate tokens for multilingual varients The forms are Tokens: Token - all languages {fr}Token - if a - * language is specified {fr_CA}Token - if a language and country is specified {fr_CA_Varient}Token - for all three - * {fr__Varient}Token - for a language varient with no country - * - * @author andyh - */ -public class MLTokenDuplicator extends TokenStream -{ - private static Log s_logger = LogFactory.getLog(MLTokenDuplicator.class); - - TokenStream source; - - Locale locale; - - Iterator it; - - HashSet prefixes; - - private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); - - private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); - - private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); - - private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); - - private boolean done = false; - - public MLTokenDuplicator(TokenStream source, Locale locale, Reader reader, MLAnalysisMode mlAnalysisMode) - { - this.source = source; - this.locale = locale; - - Collection locales = MLAnalysisMode.getLocales(mlAnalysisMode, locale, false); - prefixes = new HashSet(locales.size()); - for(Locale toAdd : locales) - { - String localeString = toAdd.toString(); - if(localeString.length() == 0) - { - prefixes.add(""); - } - else - { - StringBuilder builder = new StringBuilder(16); - builder.append("{").append(localeString).append("}"); - prefixes.add(builder.toString()); - } - } - if(s_logger.isDebugEnabled()) - { - s_logger.debug("Locale "+ locale +" using "+mlAnalysisMode+" is "+prefixes); - } - - } - - /** - * - * @param locale - * @param mlAnalysisMode - */ - public MLTokenDuplicator(Locale locale, MLAnalysisMode mlAnalysisMode) - { - this.locale = locale; - - Collection locales = MLAnalysisMode.getLocales(mlAnalysisMode, locale, false); - prefixes = new HashSet(locales.size()); - for(Locale toAdd : locales) - { - String localeString = toAdd.toString(); - if(localeString.length() == 0) - { - prefixes.add(""); - } - else - { - StringBuilder builder = new StringBuilder(16); - builder.append("{").append(localeString).append("}"); - prefixes.add(builder.toString()); - } - } - if(s_logger.isDebugEnabled()) - { - s_logger.debug("Locale "+ locale +" using "+mlAnalysisMode+" is "+prefixes); - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#close() - */ - @Override - public void close() throws IOException - { - source.close(); - super.close(); - } - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#reset() - */ - @Override - public void reset() throws IOException - { - source.reset(); - super.reset(); - } - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.TokenStream#end() - */ - @Override - public void end() throws IOException - { - source.end(); - super.end(); - } - - private PackedTokenAttributeImpl next() throws IOException - { - PackedTokenAttributeImpl t = null; - if (it == null) - { - it = buildIterator(); - } - if (it == null) - { - return null; - } - if (it.hasNext()) - { - t = it.next(); - - return t; - } - else - { - it = null; - t = this.next(); - return t; - } - } - - private Iterator buildIterator() throws IOException - { - // TODO: use incrementToken() somehow? - if(!done && source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if(source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if(source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - PackedTokenAttributeImpl token = new PackedTokenAttributeImpl(); - token.setEmpty().append(new String(cta.buffer()), 0, cta.length()); - token.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if(typeAtt != null) - { - token.setType(typeAtt.type()); - } - if(posIncAtt != null) - { - token.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - return buildIterator(token); - } - else - { - done = true; - return buildIterator(null); - } - - - } - - - public Iterator buildIterator(PackedTokenAttributeImpl token) - { - if (token == null) - { - return null; - } - - ArrayList tokens = new ArrayList(prefixes.size()); - for (String prefix : prefixes) - { - - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(prefix + termText(token)); - newToken.setOffset(token.startOffset(), token.endOffset()); - newToken.setType(token.type()); - if (tokens.size() == 0) - { - newToken.setPositionIncrement(token.getPositionIncrement()); - } - else - { - newToken.setPositionIncrement(0); - } - tokens.add(newToken); - } - return tokens.iterator(); - - } - - @Override - public final boolean incrementToken() throws IOException - { - clearAttributes(); - - PackedTokenAttributeImpl next = next(); - if (next == null) - { - return false; - } - - termAtt.copyBuffer(next.buffer(), 0, next.length()); - offsetAtt.setOffset(next.startOffset(), next.endOffset()); - typeAtt.setType(next.type()); - posIncAtt.setPositionIncrement(next.getPositionIncrement()); - return true; - - } - - // TODO: temporary replacement for Token.termText() - private String termText(PackedTokenAttributeImpl token) - { - return new String(token.buffer(), 0, token.length()); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; + +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; + +/** + * Create duplicate tokens for multilingual varients The forms are Tokens: Token - all languages {fr}Token - if a + * language is specified {fr_CA}Token - if a language and country is specified {fr_CA_Varient}Token - for all three + * {fr__Varient}Token - for a language varient with no country + * + * @author andyh + */ +public class MLTokenDuplicator extends TokenStream +{ + private static Log s_logger = LogFactory.getLog(MLTokenDuplicator.class); + + TokenStream source; + + Locale locale; + + Iterator it; + + HashSet prefixes; + + private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); + + private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); + + private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); + + private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); + + private boolean done = false; + + public MLTokenDuplicator(TokenStream source, Locale locale, Reader reader, MLAnalysisMode mlAnalysisMode) + { + this.source = source; + this.locale = locale; + + Collection locales = MLAnalysisMode.getLocales(mlAnalysisMode, locale, false); + prefixes = new HashSet(locales.size()); + for(Locale toAdd : locales) + { + String localeString = toAdd.toString(); + if(localeString.length() == 0) + { + prefixes.add(""); + } + else + { + StringBuilder builder = new StringBuilder(16); + builder.append("{").append(localeString).append("}"); + prefixes.add(builder.toString()); + } + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Locale "+ locale +" using "+mlAnalysisMode+" is "+prefixes); + } + + } + + /** + * + * @param locale + * @param mlAnalysisMode + */ + public MLTokenDuplicator(Locale locale, MLAnalysisMode mlAnalysisMode) + { + this.locale = locale; + + Collection locales = MLAnalysisMode.getLocales(mlAnalysisMode, locale, false); + prefixes = new HashSet(locales.size()); + for(Locale toAdd : locales) + { + String localeString = toAdd.toString(); + if(localeString.length() == 0) + { + prefixes.add(""); + } + else + { + StringBuilder builder = new StringBuilder(16); + builder.append("{").append(localeString).append("}"); + prefixes.add(builder.toString()); + } + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Locale "+ locale +" using "+mlAnalysisMode+" is "+prefixes); + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#close() + */ + @Override + public void close() throws IOException + { + source.close(); + super.close(); + } + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#reset() + */ + @Override + public void reset() throws IOException + { + source.reset(); + super.reset(); + } + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.TokenStream#end() + */ + @Override + public void end() throws IOException + { + source.end(); + super.end(); + } + + private PackedTokenAttributeImpl next() throws IOException + { + PackedTokenAttributeImpl t = null; + if (it == null) + { + it = buildIterator(); + } + if (it == null) + { + return null; + } + if (it.hasNext()) + { + t = it.next(); + + return t; + } + else + { + it = null; + t = this.next(); + return t; + } + } + + private Iterator buildIterator() throws IOException + { + // TODO: use incrementToken() somehow? + if(!done && source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if(source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if(source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + PackedTokenAttributeImpl token = new PackedTokenAttributeImpl(); + token.setEmpty().append(new String(cta.buffer()), 0, cta.length()); + token.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if(typeAtt != null) + { + token.setType(typeAtt.type()); + } + if(posIncAtt != null) + { + token.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + return buildIterator(token); + } + else + { + done = true; + return buildIterator(null); + } + + + } + + + public Iterator buildIterator(PackedTokenAttributeImpl token) + { + if (token == null) + { + return null; + } + + ArrayList tokens = new ArrayList(prefixes.size()); + for (String prefix : prefixes) + { + + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(prefix + termText(token)); + newToken.setOffset(token.startOffset(), token.endOffset()); + newToken.setType(token.type()); + if (tokens.size() == 0) + { + newToken.setPositionIncrement(token.getPositionIncrement()); + } + else + { + newToken.setPositionIncrement(0); + } + tokens.add(newToken); + } + return tokens.iterator(); + + } + + @Override + public final boolean incrementToken() throws IOException + { + clearAttributes(); + + PackedTokenAttributeImpl next = next(); + if (next == null) + { + return false; + } + + termAtt.copyBuffer(next.buffer(), 0, next.length()); + offsetAtt.setOffset(next.startOffset(), next.endOffset()); + typeAtt.setType(next.type()); + posIncAtt.setPositionIncrement(next.getPositionIncrement()); + return true; + + } + + // TODO: temporary replacement for Token.termText() + private String termText(PackedTokenAttributeImpl token) + { + return new String(token.buffer(), 0, token.length()); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathAnalyzer.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathAnalyzer.java index b8d8dc4f3..fee36b88d 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathAnalyzer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathAnalyzer.java @@ -1,47 +1,47 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; - -import org.apache.lucene.analysis.Analyzer; - -/** - * @author Andy - * - */ -public class PathAnalyzer extends Analyzer -{ - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Analyzer#createComponents(java.lang.String, java.io.Reader) - */ - @Override - protected TokenStreamComponents createComponents(String fieldName) - { - return new TokenStreamComponents(new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true)); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; + +import org.apache.lucene.analysis.Analyzer; + +/** + * @author Andy + * + */ +public class PathAnalyzer extends Analyzer +{ + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Analyzer#createComponents(java.lang.String, java.io.Reader) + */ + @Override + protected TokenStreamComponents createComponents(String fieldName) + { + return new TokenStreamComponents(new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true)); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilter.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilter.java index 79d8c1adb..89bd14f88 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilter.java @@ -1,396 +1,396 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; - -import java.io.IOException; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Iterator; -import java.util.LinkedList; - -import org.apache.lucene.analysis.Tokenizer; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; - -/** - * @author andyh TODO To change the template for this generated type comment go to Window - Preferences - Java - Code - * Style - Code Templates - */ -public class PathTokenFilter extends Tokenizer -{ - public final static String INTEGER_FORMAT = "0000000000"; - - public final static char PATH_SEPARATOR = ';'; - - public final static char NAMESPACE_START_DELIMITER = '{'; - - public final static char NAMESPACE_END_DELIMITER = '}'; - - public final static String SEPARATOR_TOKEN_TEXT = ";"; - - public final static String NO_NS_TOKEN_TEXT = ""; - - public final static String TOKEN_TYPE_PATH_SEP = "PATH_SEPARATOR"; - - public final static String TOKEN_TYPE_PATH_LENGTH = "PATH_LENGTH"; - - public final static String TOKEN_TYPE_PATH_ELEMENT_NAME = "PATH_ELEMENT_NAME"; - - public final static String TOKEN_TYPE_PATH_ELEMENT_NAMESPACE = "PATH_ELEMENT_NAMESPACE"; - - public final static String TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX = "PATH_ELEMENT_NAMESPACE_PREFIX"; - - private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); - - private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); - - private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); - - private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); - - char pathSeparator; - - String separatorTokenText; - - String noNsTokenText; - - char nsStartDelimiter; - - int nsStartDelimiterLength; - - char nsEndDelimiter; - - int nsEndDelimiterLength; - - char nsPrefixDelimiter = ':'; - - LinkedList tokens = new LinkedList(); - - Iterator it = null; - - private boolean includeNamespace; - - private boolean endOfStream = false; - - public PathTokenFilter(char pathSeparator, String separatorTokenText, String noNsTokenText, - char nsStartDelimiter, char nsEndDelimiter, boolean includeNameSpace) - { - this.pathSeparator = pathSeparator; - this.separatorTokenText = separatorTokenText; - this.noNsTokenText = noNsTokenText; - this.nsStartDelimiter = nsStartDelimiter; - this.nsEndDelimiter = nsEndDelimiter; - this.includeNamespace = includeNameSpace; - - this.nsStartDelimiterLength = 1; - this.nsEndDelimiterLength = 1; - - } - - /* - * (non-Javadoc) - * - * @see org.apache.lucene.analysis.TokenStream#next() - */ - - public PackedTokenAttributeImpl next() throws IOException - { - PackedTokenAttributeImpl nextToken; - if (it == null) - { - buildTokenListAndIterator(); - } - if (it.hasNext()) - { - nextToken = it.next(); - } - else - { - nextToken = null; - } - return nextToken; - } - - // TODO: temporary replacement for Token.termText() - private String termText(PackedTokenAttributeImpl token) - { - return new String(token.buffer(), 0, token.length()); - } - - private void buildTokenListAndIterator() throws IOException - { - NumberFormat nf = new DecimalFormat(INTEGER_FORMAT); - - // Could optimise to read each path ata time - not just all paths - int insertCountAt = 0; - int lengthCounter = 0; - PackedTokenAttributeImpl t; - PackedTokenAttributeImpl pathSplitToken = null; - PackedTokenAttributeImpl nameToken = null; - PackedTokenAttributeImpl countToken = null; - PackedTokenAttributeImpl namespaceToken = null; - while ((t = nextToken()) != null) - { - String text = termText(t); - - if (text.length() == 0) - { - continue; // Skip if we find // or /; or ;; etc - } - - if (text.charAt(text.length() - 1) == pathSeparator) - { - text = text.substring(0, text.length() - 1); - pathSplitToken = new PackedTokenAttributeImpl(); - pathSplitToken.setEmpty().append(separatorTokenText); - pathSplitToken.setOffset(t.startOffset(), t.endOffset()); - pathSplitToken.setType( TOKEN_TYPE_PATH_SEP); - pathSplitToken.setPositionIncrement(1); - - } - - int split = -1; - boolean isPrefix = false; - - if ((text.length() > 0) && (text.charAt(0) == nsStartDelimiter)) - { - split = text.indexOf(nsEndDelimiter); - } - - if (split == -1) - { - split = text.indexOf(nsPrefixDelimiter); - isPrefix = true; - } - - if (split == -1) - { - namespaceToken = new PackedTokenAttributeImpl(); - namespaceToken.setEmpty().append(noNsTokenText); - namespaceToken.setOffset(t.startOffset(), t.startOffset()); - namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); - nameToken = new PackedTokenAttributeImpl(); - nameToken.setEmpty().append(text); - nameToken.setOffset(t.startOffset(), t.endOffset()); - - nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); - } - else - { - if (isPrefix) - { - namespaceToken = new PackedTokenAttributeImpl(); - namespaceToken.setEmpty().append(text.substring(0, split)); - namespaceToken.setOffset(t.startOffset(), t.startOffset() + split); - namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX); - nameToken = new PackedTokenAttributeImpl(); - nameToken.setEmpty().append(text.substring(split + 1)); - nameToken.setOffset(t.startOffset() + split + 1, t.endOffset()); - nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); - } - else - { - namespaceToken = new PackedTokenAttributeImpl(); - namespaceToken.setEmpty().append(text.substring(nsStartDelimiterLength, (split + nsEndDelimiterLength - 1))); - namespaceToken.setOffset(t.startOffset(), t.startOffset() + split); - namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); - nameToken = new PackedTokenAttributeImpl(); - nameToken.setEmpty().append(text.substring(split + nsEndDelimiterLength)); - nameToken.setOffset(t.startOffset() + split + nsEndDelimiterLength, t.endOffset()); - nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); - } - } - - namespaceToken.setPositionIncrement(1); - nameToken.setPositionIncrement(1); - - if (includeNamespace) - { - if (termText(namespaceToken).equals("")) - { - namespaceToken = new PackedTokenAttributeImpl(); - namespaceToken.setEmpty().append(noNsTokenText); - namespaceToken.setOffset(t.startOffset(), t.startOffset()); - namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); - namespaceToken.setPositionIncrement(1); - } - - tokens.add(namespaceToken); - - } - tokens.add(nameToken); - - lengthCounter++; - - if (pathSplitToken != null) - { - - String countString = nf.format(lengthCounter); - countToken = new PackedTokenAttributeImpl(); - countToken.setEmpty().append(countString); - countToken.setOffset(t.startOffset(), t.endOffset()); - countToken.setType(TOKEN_TYPE_PATH_SEP); - countToken.setPositionIncrement(1); - - tokens.add(insertCountAt, countToken); - tokens.add(pathSplitToken); - - lengthCounter = 0; - insertCountAt = tokens.size(); - - pathSplitToken = null; - } - } - - String countString = nf.format(lengthCounter); - countToken = new PackedTokenAttributeImpl(); - countToken.setEmpty().append(countString); - countToken.setOffset(0, 0); - countToken.setType(TOKEN_TYPE_PATH_SEP); - countToken.setPositionIncrement(1); - - tokens.add(insertCountAt, countToken); - - if ((tokens.size() == 0) || !(termText(tokens.get(tokens.size() - 1)).equals(TOKEN_TYPE_PATH_SEP))) - { - pathSplitToken = new PackedTokenAttributeImpl(); - pathSplitToken.setEmpty().append(separatorTokenText); - pathSplitToken.setOffset(0, 0); - pathSplitToken.setType(TOKEN_TYPE_PATH_SEP); - pathSplitToken.setPositionIncrement(1); - tokens.add(pathSplitToken); - } - - it = tokens.iterator(); - } - - int readerPosition = 0; - - private PackedTokenAttributeImpl nextToken() throws IOException - { - if (endOfStream) - { - return null; - } - StringBuilder buffer = new StringBuilder(64); - boolean inNameSpace = false; - int start = readerPosition; - int current; - char c; - while ((current = input.read()) != -1) - { - c = (char) current; - readerPosition++; - if (c == nsStartDelimiter) - { - inNameSpace = true; - } - else if (c == nsEndDelimiter) - { - inNameSpace = false; - } - else if (!inNameSpace && (c == '/')) - { - PackedTokenAttributeImpl qNameToken = new PackedTokenAttributeImpl(); - qNameToken.setEmpty().append(buffer.toString()); - qNameToken.setOffset(start, readerPosition - 1); - qNameToken.setType("QNAME"); - return qNameToken; - } - else if (!inNameSpace && (c == ';')) - { - buffer.append(c); - PackedTokenAttributeImpl lastQNameToken = new PackedTokenAttributeImpl(); - lastQNameToken.setEmpty().append(buffer.toString()); - lastQNameToken.setOffset(start, readerPosition); - lastQNameToken.setType("LASTQNAME"); - return lastQNameToken; - } - - buffer.append(c); - } - int end = readerPosition - 1; - // Stop the final token being returned with an end before the start. - if (start > end) - { - end = start; - } - endOfStream = true; - if (!inNameSpace) - { - PackedTokenAttributeImpl qNameToken = new PackedTokenAttributeImpl(); - qNameToken.setEmpty().append(buffer.toString()); - qNameToken.setOffset(start, end); - qNameToken.setType("QNAME"); - return qNameToken; - } - else - { - throw new IllegalStateException("QName terminated incorrectly: " + buffer.toString()); - } - - } - - @Override - public final boolean incrementToken() throws IOException - { - clearAttributes(); - - PackedTokenAttributeImpl next = next(); - if (next == null) - { - return false; - } - - termAtt.copyBuffer(next.buffer(), 0, next.length()); - offsetAtt.setOffset(correctOffset(next.startOffset()), correctOffset(next.endOffset())); - typeAtt.setType(next.type()); - posIncAtt.setPositionIncrement(next.getPositionIncrement()); - return true; - } - - @Override - public void reset() throws IOException - { - super.reset(); - tokens.clear(); - it = null; - readerPosition = 0; - endOfStream = false; - } - - @Override - public void end() throws IOException - { - super.end(); - int finalOffset = correctOffset(readerPosition); - offsetAtt.setOffset(finalOffset, finalOffset); - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; + +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Iterator; +import java.util.LinkedList; + +import org.apache.lucene.analysis.Tokenizer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; + +/** + * @author andyh TODO To change the template for this generated type comment go to Window - Preferences - Java - Code + * Style - Code Templates + */ +public class PathTokenFilter extends Tokenizer +{ + public final static String INTEGER_FORMAT = "0000000000"; + + public final static char PATH_SEPARATOR = ';'; + + public final static char NAMESPACE_START_DELIMITER = '{'; + + public final static char NAMESPACE_END_DELIMITER = '}'; + + public final static String SEPARATOR_TOKEN_TEXT = ";"; + + public final static String NO_NS_TOKEN_TEXT = ""; + + public final static String TOKEN_TYPE_PATH_SEP = "PATH_SEPARATOR"; + + public final static String TOKEN_TYPE_PATH_LENGTH = "PATH_LENGTH"; + + public final static String TOKEN_TYPE_PATH_ELEMENT_NAME = "PATH_ELEMENT_NAME"; + + public final static String TOKEN_TYPE_PATH_ELEMENT_NAMESPACE = "PATH_ELEMENT_NAMESPACE"; + + public final static String TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX = "PATH_ELEMENT_NAMESPACE_PREFIX"; + + private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); + + private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); + + private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); + + private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); + + char pathSeparator; + + String separatorTokenText; + + String noNsTokenText; + + char nsStartDelimiter; + + int nsStartDelimiterLength; + + char nsEndDelimiter; + + int nsEndDelimiterLength; + + char nsPrefixDelimiter = ':'; + + LinkedList tokens = new LinkedList(); + + Iterator it = null; + + private boolean includeNamespace; + + private boolean endOfStream = false; + + public PathTokenFilter(char pathSeparator, String separatorTokenText, String noNsTokenText, + char nsStartDelimiter, char nsEndDelimiter, boolean includeNameSpace) + { + this.pathSeparator = pathSeparator; + this.separatorTokenText = separatorTokenText; + this.noNsTokenText = noNsTokenText; + this.nsStartDelimiter = nsStartDelimiter; + this.nsEndDelimiter = nsEndDelimiter; + this.includeNamespace = includeNameSpace; + + this.nsStartDelimiterLength = 1; + this.nsEndDelimiterLength = 1; + + } + + /* + * (non-Javadoc) + * + * @see org.apache.lucene.analysis.TokenStream#next() + */ + + public PackedTokenAttributeImpl next() throws IOException + { + PackedTokenAttributeImpl nextToken; + if (it == null) + { + buildTokenListAndIterator(); + } + if (it.hasNext()) + { + nextToken = it.next(); + } + else + { + nextToken = null; + } + return nextToken; + } + + // TODO: temporary replacement for Token.termText() + private String termText(PackedTokenAttributeImpl token) + { + return new String(token.buffer(), 0, token.length()); + } + + private void buildTokenListAndIterator() throws IOException + { + NumberFormat nf = new DecimalFormat(INTEGER_FORMAT); + + // Could optimise to read each path ata time - not just all paths + int insertCountAt = 0; + int lengthCounter = 0; + PackedTokenAttributeImpl t; + PackedTokenAttributeImpl pathSplitToken = null; + PackedTokenAttributeImpl nameToken = null; + PackedTokenAttributeImpl countToken = null; + PackedTokenAttributeImpl namespaceToken = null; + while ((t = nextToken()) != null) + { + String text = termText(t); + + if (text.length() == 0) + { + continue; // Skip if we find // or /; or ;; etc + } + + if (text.charAt(text.length() - 1) == pathSeparator) + { + text = text.substring(0, text.length() - 1); + pathSplitToken = new PackedTokenAttributeImpl(); + pathSplitToken.setEmpty().append(separatorTokenText); + pathSplitToken.setOffset(t.startOffset(), t.endOffset()); + pathSplitToken.setType( TOKEN_TYPE_PATH_SEP); + pathSplitToken.setPositionIncrement(1); + + } + + int split = -1; + boolean isPrefix = false; + + if ((text.length() > 0) && (text.charAt(0) == nsStartDelimiter)) + { + split = text.indexOf(nsEndDelimiter); + } + + if (split == -1) + { + split = text.indexOf(nsPrefixDelimiter); + isPrefix = true; + } + + if (split == -1) + { + namespaceToken = new PackedTokenAttributeImpl(); + namespaceToken.setEmpty().append(noNsTokenText); + namespaceToken.setOffset(t.startOffset(), t.startOffset()); + namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); + nameToken = new PackedTokenAttributeImpl(); + nameToken.setEmpty().append(text); + nameToken.setOffset(t.startOffset(), t.endOffset()); + + nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); + } + else + { + if (isPrefix) + { + namespaceToken = new PackedTokenAttributeImpl(); + namespaceToken.setEmpty().append(text.substring(0, split)); + namespaceToken.setOffset(t.startOffset(), t.startOffset() + split); + namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX); + nameToken = new PackedTokenAttributeImpl(); + nameToken.setEmpty().append(text.substring(split + 1)); + nameToken.setOffset(t.startOffset() + split + 1, t.endOffset()); + nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); + } + else + { + namespaceToken = new PackedTokenAttributeImpl(); + namespaceToken.setEmpty().append(text.substring(nsStartDelimiterLength, (split + nsEndDelimiterLength - 1))); + namespaceToken.setOffset(t.startOffset(), t.startOffset() + split); + namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); + nameToken = new PackedTokenAttributeImpl(); + nameToken.setEmpty().append(text.substring(split + nsEndDelimiterLength)); + nameToken.setOffset(t.startOffset() + split + nsEndDelimiterLength, t.endOffset()); + nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); + } + } + + namespaceToken.setPositionIncrement(1); + nameToken.setPositionIncrement(1); + + if (includeNamespace) + { + if (termText(namespaceToken).equals("")) + { + namespaceToken = new PackedTokenAttributeImpl(); + namespaceToken.setEmpty().append(noNsTokenText); + namespaceToken.setOffset(t.startOffset(), t.startOffset()); + namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); + namespaceToken.setPositionIncrement(1); + } + + tokens.add(namespaceToken); + + } + tokens.add(nameToken); + + lengthCounter++; + + if (pathSplitToken != null) + { + + String countString = nf.format(lengthCounter); + countToken = new PackedTokenAttributeImpl(); + countToken.setEmpty().append(countString); + countToken.setOffset(t.startOffset(), t.endOffset()); + countToken.setType(TOKEN_TYPE_PATH_SEP); + countToken.setPositionIncrement(1); + + tokens.add(insertCountAt, countToken); + tokens.add(pathSplitToken); + + lengthCounter = 0; + insertCountAt = tokens.size(); + + pathSplitToken = null; + } + } + + String countString = nf.format(lengthCounter); + countToken = new PackedTokenAttributeImpl(); + countToken.setEmpty().append(countString); + countToken.setOffset(0, 0); + countToken.setType(TOKEN_TYPE_PATH_SEP); + countToken.setPositionIncrement(1); + + tokens.add(insertCountAt, countToken); + + if ((tokens.size() == 0) || !(termText(tokens.get(tokens.size() - 1)).equals(TOKEN_TYPE_PATH_SEP))) + { + pathSplitToken = new PackedTokenAttributeImpl(); + pathSplitToken.setEmpty().append(separatorTokenText); + pathSplitToken.setOffset(0, 0); + pathSplitToken.setType(TOKEN_TYPE_PATH_SEP); + pathSplitToken.setPositionIncrement(1); + tokens.add(pathSplitToken); + } + + it = tokens.iterator(); + } + + int readerPosition = 0; + + private PackedTokenAttributeImpl nextToken() throws IOException + { + if (endOfStream) + { + return null; + } + StringBuilder buffer = new StringBuilder(64); + boolean inNameSpace = false; + int start = readerPosition; + int current; + char c; + while ((current = input.read()) != -1) + { + c = (char) current; + readerPosition++; + if (c == nsStartDelimiter) + { + inNameSpace = true; + } + else if (c == nsEndDelimiter) + { + inNameSpace = false; + } + else if (!inNameSpace && (c == '/')) + { + PackedTokenAttributeImpl qNameToken = new PackedTokenAttributeImpl(); + qNameToken.setEmpty().append(buffer.toString()); + qNameToken.setOffset(start, readerPosition - 1); + qNameToken.setType("QNAME"); + return qNameToken; + } + else if (!inNameSpace && (c == ';')) + { + buffer.append(c); + PackedTokenAttributeImpl lastQNameToken = new PackedTokenAttributeImpl(); + lastQNameToken.setEmpty().append(buffer.toString()); + lastQNameToken.setOffset(start, readerPosition); + lastQNameToken.setType("LASTQNAME"); + return lastQNameToken; + } + + buffer.append(c); + } + int end = readerPosition - 1; + // Stop the final token being returned with an end before the start. + if (start > end) + { + end = start; + } + endOfStream = true; + if (!inNameSpace) + { + PackedTokenAttributeImpl qNameToken = new PackedTokenAttributeImpl(); + qNameToken.setEmpty().append(buffer.toString()); + qNameToken.setOffset(start, end); + qNameToken.setType("QNAME"); + return qNameToken; + } + else + { + throw new IllegalStateException("QName terminated incorrectly: " + buffer.toString()); + } + + } + + @Override + public final boolean incrementToken() throws IOException + { + clearAttributes(); + + PackedTokenAttributeImpl next = next(); + if (next == null) + { + return false; + } + + termAtt.copyBuffer(next.buffer(), 0, next.length()); + offsetAtt.setOffset(correctOffset(next.startOffset()), correctOffset(next.endOffset())); + typeAtt.setType(next.type()); + posIncAtt.setPositionIncrement(next.getPositionIncrement()); + return true; + } + + @Override + public void reset() throws IOException + { + super.reset(); + tokens.clear(); + it = null; + readerPosition = 0; + endOfStream = false; + } + + @Override + public void end() throws IOException + { + super.end(); + int finalOffset = correctOffset(readerPosition); + offsetAtt.setOffset(finalOffset, finalOffset); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoAnalyzerWrapper.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoAnalyzerWrapper.java index 0da879e34..0e017486a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoAnalyzerWrapper.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoAnalyzerWrapper.java @@ -1,109 +1,109 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.impl.lucene.analysis.MLAnalayser; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.AnalyzerWrapper; -import org.apache.solr.schema.IndexSchema; - -/** - * Wraps SOLR access to for localising tokens - * As analysers are cached, and anylysers themselves cache token streams we have to be able to switch locales - * inside the MLAnalyser. - * - * @author Andy - * - */ -public class AlfrescoAnalyzerWrapper extends AnalyzerWrapper -{ - public static enum Mode - { - INDEX, QUERY; - } - - IndexSchema schema; - - Mode mode; - - /** - * @param schema - * @param index - */ - public AlfrescoAnalyzerWrapper(IndexSchema schema, Mode mode) - { - super(Analyzer.PER_FIELD_REUSE_STRATEGY); - this.schema = schema; - this.mode = mode; - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.AnalyzerWrapper#getPositionIncrementGap(java.lang.String) - */ - @Override - public int getPositionIncrementGap(String fieldName) - { - return 100; - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.AnalyzerWrapper#getWrappedAnalyzer(java.lang.String) - */ - @Override - protected Analyzer getWrappedAnalyzer(String fieldName) - { - if(fieldName.contains("l_@{")) - { - return new MLAnalayser(MLAnalysisMode.EXACT_LANGUAGE, schema, mode); - } - else if(fieldName.contains("lt@{")) - { - return new MLAnalayser(MLAnalysisMode.EXACT_LANGUAGE, schema, mode); - } - else - { - if(mode == Mode.INDEX) - { - return schema.getFieldTypeByName("text___").getIndexAnalyzer(); - } - else if(mode == Mode.QUERY) - { - return schema.getFieldTypeByName("text___").getQueryAnalyzer(); - } - else - { - throw new IllegalStateException(); - } - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.impl.lucene.analysis.MLAnalayser; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.AnalyzerWrapper; +import org.apache.solr.schema.IndexSchema; + +/** + * Wraps SOLR access to for localising tokens + * As analysers are cached, and anylysers themselves cache token streams we have to be able to switch locales + * inside the MLAnalyser. + * + * @author Andy + * + */ +public class AlfrescoAnalyzerWrapper extends AnalyzerWrapper +{ + public static enum Mode + { + INDEX, QUERY; + } + + IndexSchema schema; + + Mode mode; + + /** + * @param schema + * @param index + */ + public AlfrescoAnalyzerWrapper(IndexSchema schema, Mode mode) + { + super(Analyzer.PER_FIELD_REUSE_STRATEGY); + this.schema = schema; + this.mode = mode; + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.AnalyzerWrapper#getPositionIncrementGap(java.lang.String) + */ + @Override + public int getPositionIncrementGap(String fieldName) + { + return 100; + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.AnalyzerWrapper#getWrappedAnalyzer(java.lang.String) + */ + @Override + protected Analyzer getWrappedAnalyzer(String fieldName) + { + if(fieldName.contains("l_@{")) + { + return new MLAnalayser(MLAnalysisMode.EXACT_LANGUAGE, schema, mode); + } + else if(fieldName.contains("lt@{")) + { + return new MLAnalayser(MLAnalysisMode.EXACT_LANGUAGE, schema, mode); + } + else + { + if(mode == Mode.INDEX) + { + return schema.getFieldTypeByName("text___").getIndexAnalyzer(); + } + else if(mode == Mode.QUERY) + { + return schema.getFieldTypeByName("text___").getQueryAnalyzer(); + } + else + { + throw new IllegalStateException(); + } + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldType.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldType.java index 8028cfd6a..6210511c2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldType.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldType.java @@ -1,273 +1,273 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import java.io.IOException; -import java.text.Collator; -import java.util.Locale; - -import org.alfresco.service.cmr.repository.MLText; -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.FieldComparator; -import org.apache.lucene.search.FieldComparatorSource; -import org.apache.lucene.search.LeafFieldComparator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.SortField; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.schema.StrField; -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * @author Andy - */ -public class AlfrescoCollatableMLTextFieldType extends StrField -{ - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#getSortField(org.apache.solr.schema.SchemaField, boolean) - */ - @Override - public SortField getSortField(SchemaField field, boolean reverse) - { - return new SortField(field.getName(), new MLTextSortFieldComparatorSource(), reverse); - } - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#marshalSortValue(java.lang.Object) - */ - @Override - public Object marshalSortValue(Object value) - { - return value; - } - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#unmarshalSortValue(java.lang.Object) - */ - @Override - public Object unmarshalSortValue(Object value) - { - return value; - } - - - public static class MLTextSortFieldComparatorSource extends FieldComparatorSource - { - - /* - * (non-Javadoc) - * @see org.apache.lucene.search.FieldComparatorSource#newComparator(java.lang.String, int, int, boolean) - */ - @Override - public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) - { - return new MLTextSortFieldComparator(numHits, fieldname, I18NUtil.getLocale()); - } - - } - - - /* - * Adapted from org.apache.lucene.search.FieldComparator.TermValComparator - */ - public static final class MLTextSortFieldComparator extends FieldComparator implements LeafFieldComparator - { - - private final String[] values; - - private BinaryDocValues docTerms; - - /** - * An array of flags - one for each document in the segment. Each bit is set to true if the document has the - * field or false otherwise. If this is set to null then all docs in the segment have the field. - */ - Bits docsWithField; - - private final String field; - - Collator collator; - - String bottom; - - String top; - - Locale collatorLocale; - - MLTextSortFieldComparator(int numHits, String field, Locale collatorLocale) - { - values = new String[numHits]; - this.field = field; - this.collatorLocale = collatorLocale; - collator = Collator.getInstance(collatorLocale); - } - - public int compare(int slot1, int slot2) - { - final String val1 = values[slot1]; - final String val2 = values[slot2]; - return compareValues(val1, val2); - } - - public void setBottom(final int bottom) - { - this.bottom = values[bottom]; - } - - public int compareBottom(int doc) - { - final String comparableString = findBestValue(doc, docTerms.get(doc)); - return compareValues(bottom, comparableString); - - } - - public void copy(int slot, int doc) - { - values[slot] = findBestValue(doc, docTerms.get(doc)); - } - - public String value(int slot) - { - return values[slot]; - } - - private String findBestValue(int doc, BytesRef term) - { - if (term.length == 0 && docsWithField != null && docsWithField.get(doc) == false) - { - return null; - } - - String withLocale = term.utf8ToString(); - - // split string into MLText object - if (withLocale == null) - { - return withLocale; - } - else if (withLocale.startsWith("\u0000")) - { - MLText mlText = new MLText(); - String[] parts = withLocale.split("\u0000"); - for (int i = 0; (i + 2) <= parts.length; i += 3) - { - Locale locale = null; - String[] localeParts = parts[i + 1].split("_"); - if (localeParts.length == 1) - { - locale = new Locale(localeParts[0]); - } - else if (localeParts.length == 2) - { - locale = new Locale(localeParts[0], localeParts[1]); - } - else if (localeParts.length == 3) - { - locale = new Locale(localeParts[0], localeParts[1], localeParts[2]); - } - if (locale != null) - { - if (i + 2 == parts.length) - { - mlText.addValue(locale, ""); - } - else - { - mlText.addValue(locale, parts[i + 2]); - } - } - } - return mlText.getClosestValue(collatorLocale); - } - else - { - return withLocale; - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#setTopValue(java.lang.Object) - */ - @Override - public void setTopValue(String value) - { - this.top = value; - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#compareTop(int) - */ - @Override - public int compareTop(int doc) throws IOException - { - final String comparableString = findBestValue(doc, docTerms.get(doc)); - return compareValues(top, comparableString); - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#setNextReader(org.apache.lucene.index.AtomicReaderContext) - */ - @Override - public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException - { - docTerms = DocValues.getBinary(context.reader(), field); - docsWithField = DocValues.getDocsWithField(context.reader(), field); - if (docsWithField instanceof Bits.MatchAllBits) - { - docsWithField = null; - } - return this; - } - - @Override - public int compareValues(String val1, String val2) - { - if (val1 == null) - { - if (val2 == null) - { - return 0; - } - return -1; - } - else if (val2 == null) - { - return 1; - } - return collator.compare(val1, val2); - } - - @Override - public void setScorer(Scorer scorer) - { - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import java.io.IOException; +import java.text.Collator; +import java.util.Locale; + +import org.alfresco.service.cmr.repository.MLText; +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.FieldComparator; +import org.apache.lucene.search.FieldComparatorSource; +import org.apache.lucene.search.LeafFieldComparator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.SortField; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.schema.StrField; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + */ +public class AlfrescoCollatableMLTextFieldType extends StrField +{ + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#getSortField(org.apache.solr.schema.SchemaField, boolean) + */ + @Override + public SortField getSortField(SchemaField field, boolean reverse) + { + return new SortField(field.getName(), new MLTextSortFieldComparatorSource(), reverse); + } + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#marshalSortValue(java.lang.Object) + */ + @Override + public Object marshalSortValue(Object value) + { + return value; + } + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#unmarshalSortValue(java.lang.Object) + */ + @Override + public Object unmarshalSortValue(Object value) + { + return value; + } + + + public static class MLTextSortFieldComparatorSource extends FieldComparatorSource + { + + /* + * (non-Javadoc) + * @see org.apache.lucene.search.FieldComparatorSource#newComparator(java.lang.String, int, int, boolean) + */ + @Override + public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) + { + return new MLTextSortFieldComparator(numHits, fieldname, I18NUtil.getLocale()); + } + + } + + + /* + * Adapted from org.apache.lucene.search.FieldComparator.TermValComparator + */ + public static final class MLTextSortFieldComparator extends FieldComparator implements LeafFieldComparator + { + + private final String[] values; + + private BinaryDocValues docTerms; + + /** + * An array of flags - one for each document in the segment. Each bit is set to true if the document has the + * field or false otherwise. If this is set to null then all docs in the segment have the field. + */ + Bits docsWithField; + + private final String field; + + Collator collator; + + String bottom; + + String top; + + Locale collatorLocale; + + MLTextSortFieldComparator(int numHits, String field, Locale collatorLocale) + { + values = new String[numHits]; + this.field = field; + this.collatorLocale = collatorLocale; + collator = Collator.getInstance(collatorLocale); + } + + public int compare(int slot1, int slot2) + { + final String val1 = values[slot1]; + final String val2 = values[slot2]; + return compareValues(val1, val2); + } + + public void setBottom(final int bottom) + { + this.bottom = values[bottom]; + } + + public int compareBottom(int doc) + { + final String comparableString = findBestValue(doc, docTerms.get(doc)); + return compareValues(bottom, comparableString); + + } + + public void copy(int slot, int doc) + { + values[slot] = findBestValue(doc, docTerms.get(doc)); + } + + public String value(int slot) + { + return values[slot]; + } + + private String findBestValue(int doc, BytesRef term) + { + if (term.length == 0 && docsWithField != null && docsWithField.get(doc) == false) + { + return null; + } + + String withLocale = term.utf8ToString(); + + // split string into MLText object + if (withLocale == null) + { + return withLocale; + } + else if (withLocale.startsWith("\u0000")) + { + MLText mlText = new MLText(); + String[] parts = withLocale.split("\u0000"); + for (int i = 0; (i + 2) <= parts.length; i += 3) + { + Locale locale = null; + String[] localeParts = parts[i + 1].split("_"); + if (localeParts.length == 1) + { + locale = new Locale(localeParts[0]); + } + else if (localeParts.length == 2) + { + locale = new Locale(localeParts[0], localeParts[1]); + } + else if (localeParts.length == 3) + { + locale = new Locale(localeParts[0], localeParts[1], localeParts[2]); + } + if (locale != null) + { + if (i + 2 == parts.length) + { + mlText.addValue(locale, ""); + } + else + { + mlText.addValue(locale, parts[i + 2]); + } + } + } + return mlText.getClosestValue(collatorLocale); + } + else + { + return withLocale; + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#setTopValue(java.lang.Object) + */ + @Override + public void setTopValue(String value) + { + this.top = value; + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#compareTop(int) + */ + @Override + public int compareTop(int doc) throws IOException + { + final String comparableString = findBestValue(doc, docTerms.get(doc)); + return compareValues(top, comparableString); + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#setNextReader(org.apache.lucene.index.AtomicReaderContext) + */ + @Override + public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException + { + docTerms = DocValues.getBinary(context.reader(), field); + docsWithField = DocValues.getDocsWithField(context.reader(), field); + if (docsWithField instanceof Bits.MatchAllBits) + { + docsWithField = null; + } + return this; + } + + @Override + public int compareValues(String val1, String val2) + { + if (val1 == null) + { + if (val2 == null) + { + return 0; + } + return -1; + } + else if (val2 == null) + { + return 1; + } + return collator.compare(val1, val2); + } + + @Override + public void setScorer(Scorer scorer) + { + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableTextFieldType.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableTextFieldType.java index 0bd6f84ab..5ab5783bc 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableTextFieldType.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableTextFieldType.java @@ -1,245 +1,245 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import java.io.IOException; -import java.text.Collator; -import java.util.Locale; - -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.FieldComparator; -import org.apache.lucene.search.FieldComparatorSource; -import org.apache.lucene.search.LeafFieldComparator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.SortField; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.schema.StrField; -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * @author Andy - * - */ -public class AlfrescoCollatableTextFieldType extends StrField -{ - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#getSortField(org.apache.solr.schema.SchemaField, boolean) - */ - @Override - public SortField getSortField(SchemaField field, boolean reverse) - { - return new SortField(field.getName(), new TextSortFieldComparatorSource(), reverse); - } - - - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#marshalSortValue(java.lang.Object) - */ - @Override - public Object marshalSortValue(Object value) - { - return value; - } - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#unmarshalSortValue(java.lang.Object) - */ - @Override - public Object unmarshalSortValue(Object value) - { - return value; - } - - - - - public static class TextSortFieldComparatorSource extends FieldComparatorSource - { - /* - * (non-Javadoc) - * @see org.apache.lucene.search.FieldComparatorSource#newComparator(java.lang.String, int, int, boolean) - */ - @Override - public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) - { - return new TextSortFieldComparator(numHits, fieldname, I18NUtil.getLocale()); - } - } - - - /* - * Adapted from org.apache.lucene.search.FieldComparator.TermValComparator - */ - public static final class TextSortFieldComparator extends FieldComparator implements LeafFieldComparator - { - - private final String[] values; - - private BinaryDocValues docTerms; - - /** - * An array of flags - one for each document in the segment. Each bit is set to true if the document has the - * field or false otherwise. If this is set to null then all docs in the segment have the field. - */ - Bits docsWithField; - - private final String field; - - Collator collator; - - String bottom; - - String top; - - Locale collatorLocale; - - TextSortFieldComparator(int numHits, String field, Locale collatorLocale) - { - values = new String[numHits]; - this.field = field; - this.collatorLocale = collatorLocale; - collator = Collator.getInstance(collatorLocale); - } - - public int compare(int slot1, int slot2) - { - final String val1 = values[slot1]; - final String val2 = values[slot2]; - return compareValues(val1, val2); - } - - public void setBottom(final int bottom) - { - this.bottom = values[bottom]; - } - - public int compareBottom(int doc) - { - final String comparableString = findBestValue(doc, docTerms.get(doc)); - return compareValues(bottom, comparableString); - } - - public void copy(int slot, int doc) - { - values[slot] = findBestValue(doc, docTerms.get(doc)); - } - - public String value(int slot) - { - return values[slot]; - } - - private String findBestValue(int doc, BytesRef term) - { - if (term.length == 0 && docsWithField != null && docsWithField.get(doc) == false) - { - return null; - } - - String withLocale = term.utf8ToString(); - - // split strin into MLText object - if (withLocale == null) - { - return withLocale; - } - else if (withLocale.startsWith("\u0000")) - { - String[] parts = withLocale.split("\u0000"); - return parts[2]; - } - else - { - return withLocale; - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#setTopValue(java.lang.Object) - */ - @Override - public void setTopValue(String value) - { - this.top = value; - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#compareTop(int) - */ - @Override - public int compareTop(int doc) throws IOException - { - final String comparableString = findBestValue(doc, docTerms.get(doc)); - return compareValues(top, comparableString); - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#setNextReader(org.apache.lucene.index.AtomicReaderContext) - */ - @Override - public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException - { - docTerms = DocValues.getBinary(context.reader(), field); - docsWithField = DocValues.getDocsWithField(context.reader(), field); - if (docsWithField instanceof Bits.MatchAllBits) { - docsWithField = null; - } - return this; - } - - @Override - public int compareValues(String val1, String val2) - { - if (val1 == null) - { - if (val2 == null) - { - return 0; - } - return -1; - } - else if (val2 == null) - { - return 1; - } - return collator.compare(val1, val2); - } - - @Override - public void setScorer(Scorer scorer) {} - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import java.io.IOException; +import java.text.Collator; +import java.util.Locale; + +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.FieldComparator; +import org.apache.lucene.search.FieldComparatorSource; +import org.apache.lucene.search.LeafFieldComparator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.SortField; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.schema.StrField; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + * + */ +public class AlfrescoCollatableTextFieldType extends StrField +{ + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#getSortField(org.apache.solr.schema.SchemaField, boolean) + */ + @Override + public SortField getSortField(SchemaField field, boolean reverse) + { + return new SortField(field.getName(), new TextSortFieldComparatorSource(), reverse); + } + + + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#marshalSortValue(java.lang.Object) + */ + @Override + public Object marshalSortValue(Object value) + { + return value; + } + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#unmarshalSortValue(java.lang.Object) + */ + @Override + public Object unmarshalSortValue(Object value) + { + return value; + } + + + + + public static class TextSortFieldComparatorSource extends FieldComparatorSource + { + /* + * (non-Javadoc) + * @see org.apache.lucene.search.FieldComparatorSource#newComparator(java.lang.String, int, int, boolean) + */ + @Override + public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) + { + return new TextSortFieldComparator(numHits, fieldname, I18NUtil.getLocale()); + } + } + + + /* + * Adapted from org.apache.lucene.search.FieldComparator.TermValComparator + */ + public static final class TextSortFieldComparator extends FieldComparator implements LeafFieldComparator + { + + private final String[] values; + + private BinaryDocValues docTerms; + + /** + * An array of flags - one for each document in the segment. Each bit is set to true if the document has the + * field or false otherwise. If this is set to null then all docs in the segment have the field. + */ + Bits docsWithField; + + private final String field; + + Collator collator; + + String bottom; + + String top; + + Locale collatorLocale; + + TextSortFieldComparator(int numHits, String field, Locale collatorLocale) + { + values = new String[numHits]; + this.field = field; + this.collatorLocale = collatorLocale; + collator = Collator.getInstance(collatorLocale); + } + + public int compare(int slot1, int slot2) + { + final String val1 = values[slot1]; + final String val2 = values[slot2]; + return compareValues(val1, val2); + } + + public void setBottom(final int bottom) + { + this.bottom = values[bottom]; + } + + public int compareBottom(int doc) + { + final String comparableString = findBestValue(doc, docTerms.get(doc)); + return compareValues(bottom, comparableString); + } + + public void copy(int slot, int doc) + { + values[slot] = findBestValue(doc, docTerms.get(doc)); + } + + public String value(int slot) + { + return values[slot]; + } + + private String findBestValue(int doc, BytesRef term) + { + if (term.length == 0 && docsWithField != null && docsWithField.get(doc) == false) + { + return null; + } + + String withLocale = term.utf8ToString(); + + // split strin into MLText object + if (withLocale == null) + { + return withLocale; + } + else if (withLocale.startsWith("\u0000")) + { + String[] parts = withLocale.split("\u0000"); + return parts[2]; + } + else + { + return withLocale; + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#setTopValue(java.lang.Object) + */ + @Override + public void setTopValue(String value) + { + this.top = value; + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#compareTop(int) + */ + @Override + public int compareTop(int doc) throws IOException + { + final String comparableString = findBestValue(doc, docTerms.get(doc)); + return compareValues(top, comparableString); + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#setNextReader(org.apache.lucene.index.AtomicReaderContext) + */ + @Override + public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException + { + docTerms = DocValues.getBinary(context.reader(), field); + docsWithField = DocValues.getDocsWithField(context.reader(), field); + if (docsWithField instanceof Bits.MatchAllBits) { + docsWithField = null; + } + return this; + } + + @Override + public int compareValues(String val1, String val2) + { + if (val1 == null) + { + if (val2 == null) + { + return 0; + } + return -1; + } + else if (val2 == null) + { + return 1; + } + return collator.compare(val1, val2); + } + + @Override + public void setScorer(Scorer scorer) {} + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java index 57c46ba39..4b2a75041 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -2232,4 +2232,4 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler return response; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoFieldType.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoFieldType.java index 1f000e4a4..757a25591 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoFieldType.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoFieldType.java @@ -1,271 +1,271 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.core.KeywordAnalyzer; -import org.apache.lucene.analysis.core.KeywordTokenizerFactory; -import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; -import org.apache.lucene.analysis.util.AbstractAnalysisFactory; -import org.apache.lucene.analysis.util.CharFilterFactory; -import org.apache.lucene.analysis.util.MultiTermAwareComponent; -import org.apache.lucene.analysis.util.TokenFilterFactory; -import org.apache.lucene.analysis.util.TokenizerFactory; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.SortField; -import org.apache.lucene.search.TermRangeQuery; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.IOUtils; -import org.apache.lucene.util.QueryBuilder; -import org.apache.solr.analysis.TokenizerChain; -import org.apache.solr.common.SolrException; -import org.apache.solr.response.TextResponseWriter; -import org.apache.solr.schema.FieldType; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.QParser; -import org.apache.solr.uninverting.UninvertingReader.Type; - -/** - * Basic behaviour filtched from TextField - * - * @author Andy - */ -public class AlfrescoFieldType extends FieldType -{ - IndexSchema schema; - - /* - * (non-Javadoc) - * @see org.apache.solr.schema.FieldType#init(org.apache.solr.schema.IndexSchema, java.util.Map) - */ - @Override - protected void init(IndexSchema schema, Map args) - { - this.schema = schema; - properties |= TOKENIZED; - properties &= ~OMIT_TF_POSITIONS; - super.init(schema, args); - - // TODO: Wire up localised analysis driven from the schema - // for now we do something basic - setIndexAnalyzer(new AlfrescoAnalyzerWrapper(schema, AlfrescoAnalyzerWrapper.Mode.INDEX)); - setQueryAnalyzer(new AlfrescoAnalyzerWrapper(schema, AlfrescoAnalyzerWrapper.Mode.QUERY)); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.schema.FieldType#getSortField(org.apache.solr.schema.SchemaField, boolean) - */ - @Override - public SortField getSortField(SchemaField field, boolean reverse) - { - return getStringSort(field, reverse); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.schema.FieldType#write(org.apache.solr.response.TextResponseWriter, java.lang.String, - * org.apache.lucene.index.IndexableField) - */ - @Override - public void write(TextResponseWriter writer, String name, IndexableField f) throws IOException - { - writer.writeStr(name, f.stringValue(), true); - } - - @Override - public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) - { - return parseFieldQuery(parser, getQueryAnalyzer(), field.getName(), externalVal); - } - - @Override - public Object toObject(SchemaField sf, BytesRef term) - { - return term.utf8ToString(); - } - - @Override - public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive, boolean maxInclusive) - { - Analyzer multiAnalyzer = constructMultiTermAnalyzer(getQueryAnalyzer()); - BytesRef lower = analyzeMultiTerm(field.getName(), part1, multiAnalyzer); - BytesRef upper = analyzeMultiTerm(field.getName(), part2, multiAnalyzer); - return new TermRangeQuery(field.getName(), lower, upper, minInclusive, maxInclusive); - } - - private Analyzer constructMultiTermAnalyzer(Analyzer queryAnalyzer) - { - if (queryAnalyzer == null) - return null; - - if (!(queryAnalyzer instanceof TokenizerChain)) - { - return new KeywordAnalyzer(); - } - - TokenizerChain tc = (TokenizerChain) queryAnalyzer; - MultiTermChainBuilder builder = new MultiTermChainBuilder(); - - CharFilterFactory[] charFactories = tc.getCharFilterFactories(); - if (charFactories != null) - { - for (CharFilterFactory fact : charFactories) - { - builder.add(fact); - } - } - - builder.add(tc.getTokenizerFactory()); - - for (TokenFilterFactory fact : tc.getTokenFilterFactories()) - { - builder.add(fact); - } - - return builder.build(); - } - - private static class MultiTermChainBuilder - { - static final KeywordTokenizerFactory keyFactory = new KeywordTokenizerFactory(new HashMap()); - - ArrayList charFilters = null; - - ArrayList filters = new ArrayList(2); - - TokenizerFactory tokenizer = keyFactory; - - public void add(Object current) - { - if (!(current instanceof MultiTermAwareComponent)) - return; - AbstractAnalysisFactory newComponent = ((MultiTermAwareComponent) current).getMultiTermComponent(); - if (newComponent instanceof TokenFilterFactory) - { - if (filters == null) - { - filters = new ArrayList(2); - } - filters.add((TokenFilterFactory) newComponent); - } - else if (newComponent instanceof TokenizerFactory) - { - tokenizer = (TokenizerFactory) newComponent; - } - else if (newComponent instanceof CharFilterFactory) - { - if (charFilters == null) - { - charFilters = new ArrayList(1); - } - charFilters.add((CharFilterFactory) newComponent); - - } - else - { - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown analysis component from MultiTermAwareComponent: " + newComponent); - } - } - - public TokenizerChain build() - { - CharFilterFactory[] charFilterArr = charFilters == null ? null : charFilters.toArray(new CharFilterFactory[charFilters.size()]); - TokenFilterFactory[] filterArr = filters == null ? new TokenFilterFactory[0] : filters.toArray(new TokenFilterFactory[filters.size()]); - return new TokenizerChain(charFilterArr, tokenizer, filterArr); - } - - } - - public static BytesRef analyzeMultiTerm(String field, String part, Analyzer analyzerIn) - { - if (part == null || analyzerIn == null) - return null; - - TokenStream source = null; - try - { - source = analyzerIn.tokenStream(field, part); - source.reset(); - - TermToBytesRefAttribute termAtt = source.getAttribute(TermToBytesRefAttribute.class); - BytesRef bytes = termAtt.getBytesRef(); - - if (!source.incrementToken()) - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "analyzer returned no terms for multiTerm term: " + part); - if (source.incrementToken()) - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "analyzer returned too many terms for multiTerm term: " + part); - - source.end(); - return BytesRef.deepCopyOf(bytes); - } - catch (IOException e) - { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "error analyzing range part: " + part, e); - } - finally - { - IOUtils.closeWhileHandlingException(source); - } - } - - static Query parseFieldQuery(QParser parser, Analyzer analyzer, String field, String queryText) - { - // note, this method always worked this way (but nothing calls it?) because it has no idea of quotes... - return new QueryBuilder(analyzer).createPhraseQuery(field, queryText); - } - - @Override - public Object marshalSortValue(Object value) - { - return marshalStringSortValue(value); - } - - @Override - public Object unmarshalSortValue(Object value) - { - return unmarshalStringSortValue(value); - } - - protected boolean supportsAnalyzers() - { - return true; - } - - @Override - public Type getUninversionType(SchemaField sf) { - return Type.SORTED_SET_BINARY; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.core.KeywordAnalyzer; +import org.apache.lucene.analysis.core.KeywordTokenizerFactory; +import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; +import org.apache.lucene.analysis.util.AbstractAnalysisFactory; +import org.apache.lucene.analysis.util.CharFilterFactory; +import org.apache.lucene.analysis.util.MultiTermAwareComponent; +import org.apache.lucene.analysis.util.TokenFilterFactory; +import org.apache.lucene.analysis.util.TokenizerFactory; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.TermRangeQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.IOUtils; +import org.apache.lucene.util.QueryBuilder; +import org.apache.solr.analysis.TokenizerChain; +import org.apache.solr.common.SolrException; +import org.apache.solr.response.TextResponseWriter; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.QParser; +import org.apache.solr.uninverting.UninvertingReader.Type; + +/** + * Basic behaviour filtched from TextField + * + * @author Andy + */ +public class AlfrescoFieldType extends FieldType +{ + IndexSchema schema; + + /* + * (non-Javadoc) + * @see org.apache.solr.schema.FieldType#init(org.apache.solr.schema.IndexSchema, java.util.Map) + */ + @Override + protected void init(IndexSchema schema, Map args) + { + this.schema = schema; + properties |= TOKENIZED; + properties &= ~OMIT_TF_POSITIONS; + super.init(schema, args); + + // TODO: Wire up localised analysis driven from the schema + // for now we do something basic + setIndexAnalyzer(new AlfrescoAnalyzerWrapper(schema, AlfrescoAnalyzerWrapper.Mode.INDEX)); + setQueryAnalyzer(new AlfrescoAnalyzerWrapper(schema, AlfrescoAnalyzerWrapper.Mode.QUERY)); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.schema.FieldType#getSortField(org.apache.solr.schema.SchemaField, boolean) + */ + @Override + public SortField getSortField(SchemaField field, boolean reverse) + { + return getStringSort(field, reverse); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.schema.FieldType#write(org.apache.solr.response.TextResponseWriter, java.lang.String, + * org.apache.lucene.index.IndexableField) + */ + @Override + public void write(TextResponseWriter writer, String name, IndexableField f) throws IOException + { + writer.writeStr(name, f.stringValue(), true); + } + + @Override + public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) + { + return parseFieldQuery(parser, getQueryAnalyzer(), field.getName(), externalVal); + } + + @Override + public Object toObject(SchemaField sf, BytesRef term) + { + return term.utf8ToString(); + } + + @Override + public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive, boolean maxInclusive) + { + Analyzer multiAnalyzer = constructMultiTermAnalyzer(getQueryAnalyzer()); + BytesRef lower = analyzeMultiTerm(field.getName(), part1, multiAnalyzer); + BytesRef upper = analyzeMultiTerm(field.getName(), part2, multiAnalyzer); + return new TermRangeQuery(field.getName(), lower, upper, minInclusive, maxInclusive); + } + + private Analyzer constructMultiTermAnalyzer(Analyzer queryAnalyzer) + { + if (queryAnalyzer == null) + return null; + + if (!(queryAnalyzer instanceof TokenizerChain)) + { + return new KeywordAnalyzer(); + } + + TokenizerChain tc = (TokenizerChain) queryAnalyzer; + MultiTermChainBuilder builder = new MultiTermChainBuilder(); + + CharFilterFactory[] charFactories = tc.getCharFilterFactories(); + if (charFactories != null) + { + for (CharFilterFactory fact : charFactories) + { + builder.add(fact); + } + } + + builder.add(tc.getTokenizerFactory()); + + for (TokenFilterFactory fact : tc.getTokenFilterFactories()) + { + builder.add(fact); + } + + return builder.build(); + } + + private static class MultiTermChainBuilder + { + static final KeywordTokenizerFactory keyFactory = new KeywordTokenizerFactory(new HashMap()); + + ArrayList charFilters = null; + + ArrayList filters = new ArrayList(2); + + TokenizerFactory tokenizer = keyFactory; + + public void add(Object current) + { + if (!(current instanceof MultiTermAwareComponent)) + return; + AbstractAnalysisFactory newComponent = ((MultiTermAwareComponent) current).getMultiTermComponent(); + if (newComponent instanceof TokenFilterFactory) + { + if (filters == null) + { + filters = new ArrayList(2); + } + filters.add((TokenFilterFactory) newComponent); + } + else if (newComponent instanceof TokenizerFactory) + { + tokenizer = (TokenizerFactory) newComponent; + } + else if (newComponent instanceof CharFilterFactory) + { + if (charFilters == null) + { + charFilters = new ArrayList(1); + } + charFilters.add((CharFilterFactory) newComponent); + + } + else + { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown analysis component from MultiTermAwareComponent: " + newComponent); + } + } + + public TokenizerChain build() + { + CharFilterFactory[] charFilterArr = charFilters == null ? null : charFilters.toArray(new CharFilterFactory[charFilters.size()]); + TokenFilterFactory[] filterArr = filters == null ? new TokenFilterFactory[0] : filters.toArray(new TokenFilterFactory[filters.size()]); + return new TokenizerChain(charFilterArr, tokenizer, filterArr); + } + + } + + public static BytesRef analyzeMultiTerm(String field, String part, Analyzer analyzerIn) + { + if (part == null || analyzerIn == null) + return null; + + TokenStream source = null; + try + { + source = analyzerIn.tokenStream(field, part); + source.reset(); + + TermToBytesRefAttribute termAtt = source.getAttribute(TermToBytesRefAttribute.class); + BytesRef bytes = termAtt.getBytesRef(); + + if (!source.incrementToken()) + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "analyzer returned no terms for multiTerm term: " + part); + if (source.incrementToken()) + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "analyzer returned too many terms for multiTerm term: " + part); + + source.end(); + return BytesRef.deepCopyOf(bytes); + } + catch (IOException e) + { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "error analyzing range part: " + part, e); + } + finally + { + IOUtils.closeWhileHandlingException(source); + } + } + + static Query parseFieldQuery(QParser parser, Analyzer analyzer, String field, String queryText) + { + // note, this method always worked this way (but nothing calls it?) because it has no idea of quotes... + return new QueryBuilder(analyzer).createPhraseQuery(field, queryText); + } + + @Override + public Object marshalSortValue(Object value) + { + return marshalStringSortValue(value); + } + + @Override + public Object unmarshalSortValue(Object value) + { + return unmarshalStringSortValue(value); + } + + protected boolean supportsAnalyzers() + { + return true; + } + + @Override + public Type getUninversionType(SchemaField sf) { + return Type.SORTED_SET_BINARY; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoLockException.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoLockException.java index a5a346e3f..3c8e677b1 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoLockException.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoLockException.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolr4FunctionEvaluationContext.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolr4FunctionEvaluationContext.java index 8ab2990a0..33c49d575 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolr4FunctionEvaluationContext.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolr4FunctionEvaluationContext.java @@ -1,76 +1,76 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; -import org.alfresco.repo.search.impl.parsers.FTSQueryException; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.apache.solr.schema.IndexSchema; - -/** - * @author Andy - * - */ -public class AlfrescoSolr4FunctionEvaluationContext extends AlfrescoFunctionEvaluationContext -{ - - private IndexSchema indexSchema; - - /** - * @param namespacePrefixResolver - * @param dictionaryService - * @param defaultNamespace - * @param indexSchema - */ - public AlfrescoSolr4FunctionEvaluationContext(NamespacePrefixResolver namespacePrefixResolver, DictionaryService dictionaryService, String defaultNamespace, IndexSchema indexSchema) - { - super(namespacePrefixResolver, dictionaryService, defaultNamespace); - this.indexSchema = indexSchema; - } - - public String getLuceneFieldName(String propertyName) - { - - if(indexSchema.getFieldOrNull(propertyName) != null) - { - return propertyName; - } - else - { - try - { - return super.getLuceneFieldName(propertyName); - } - catch(FTSQueryException e) - { - // unknown - return "_dummy_"; - } - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; +import org.alfresco.repo.search.impl.parsers.FTSQueryException; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.apache.solr.schema.IndexSchema; + +/** + * @author Andy + * + */ +public class AlfrescoSolr4FunctionEvaluationContext extends AlfrescoFunctionEvaluationContext +{ + + private IndexSchema indexSchema; + + /** + * @param namespacePrefixResolver + * @param dictionaryService + * @param defaultNamespace + * @param indexSchema + */ + public AlfrescoSolr4FunctionEvaluationContext(NamespacePrefixResolver namespacePrefixResolver, DictionaryService dictionaryService, String defaultNamespace, IndexSchema indexSchema) + { + super(namespacePrefixResolver, dictionaryService, defaultNamespace); + this.indexSchema = indexSchema; + } + + public String getLuceneFieldName(String propertyName) + { + + if(indexSchema.getFieldOrNull(propertyName) != null) + { + return propertyName; + } + else + { + try + { + return super.getLuceneFieldName(propertyName); + } + catch(FTSQueryException e) + { + // unknown + return "_dummy_"; + } + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolrDataModel.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolrDataModel.java index d8c4a7cd3..f32dab46e 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolrDataModel.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolrDataModel.java @@ -1,1892 +1,1892 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import static java.util.Collections.unmodifiableList; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.stream.Collectors; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.opencmis.dictionary.CMISAbstractDictionaryService; -import org.alfresco.opencmis.dictionary.CMISDictionaryService; -import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; -import org.alfresco.opencmis.dictionary.QNameFilter; -import org.alfresco.opencmis.search.CMISQueryOptions; -import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; -import org.alfresco.opencmis.search.CMISQueryParser; -import org.alfresco.opencmis.search.CmisFunctionEvaluationContext; -import org.alfresco.repo.dictionary.CompiledModelsCache; -import org.alfresco.repo.dictionary.DictionaryComponent; -import org.alfresco.repo.dictionary.DictionaryDAOImpl; -import org.alfresco.repo.dictionary.Facetable; -import org.alfresco.repo.dictionary.IndexTokenisationMode; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.M2ModelDiff; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.repo.i18n.StaticMessageLookup; -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.repo.search.impl.QueryParserUtils; -import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; -import org.alfresco.repo.search.impl.parsers.FTSParser; -import org.alfresco.repo.search.impl.parsers.FTSQueryParser; -import org.alfresco.repo.search.impl.querymodel.Constraint; -import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; -import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective; -import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilder; -import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryModelFactory; -import org.alfresco.repo.search.impl.querymodel.impl.lucene.QueryBuilderContext; -import org.alfresco.repo.tenant.SingleTServiceImpl; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.dictionary.DictionaryException; -import org.alfresco.service.cmr.dictionary.ModelDefinition; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.namespace.NamespaceException; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.AlfrescoClientDataModelServicesFactory.DictionaryKey; -import org.alfresco.solr.client.AlfrescoModel; -import org.alfresco.solr.query.Lucene4QueryBuilderContextSolrImpl; -import org.alfresco.solr.query.Solr4QueryParser; -import org.alfresco.solr.tracker.pool.DefaultTrackerPoolFactory; -import org.alfresco.solr.tracker.pool.TrackerPoolFactory; -import org.alfresco.util.NumericEncoder; -import org.alfresco.util.Pair; -import org.alfresco.util.cache.DefaultAsynchronouslyRefreshedCacheRegistry; -import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; -import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin; -import org.apache.chemistry.opencmis.commons.enums.CmisVersion; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Sort; -import org.apache.lucene.util.Version; -import org.apache.solr.core.CoreDescriptorDecorator; -import org.apache.solr.core.SolrResourceLoader; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.context.support.FileSystemXmlApplicationContext; - -import static java.util.Optional.ofNullable; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_HOUR_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MONTH_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_SECOND_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_YEAR_FIELD_SUFFIX; - -/** - * @author Andy - * - */ -public class AlfrescoSolrDataModel implements QueryConstants -{ - public static class TenantDbId - { - public String tenant; - public Long dbId; - - public Map optionalBag = new HashMap<>(); - - public void setProperty(String name, Object value) - { - optionalBag.put(name, value); - } - } - - public enum FieldUse - { - FTS, // Term/Phrase/Range/Fuzzy/Prefix/Proximity - ID, // ExactRange - Comparison, In, Upper, Lower, Wildcard - EXACT, // Exact - FACET, // Field, Range, Query - MULTI_FACET, // Text fields will require cross language support to avoid tokenisation for facets - STATS, // Stats - SORT, // Locale - SUGGESTION, - COMPLETION, - HIGHLIGHT - } - - public enum SpecializedFieldType - { - CONTENT_DOCID, - CONTENT_SIZE, - CONTENT_LOCALE, - CONTENT_MIMETYPE, - CONTENT_ENCODING, - TRANSFORMATION_STATUS, - TRANSFORMATION_TIME, - TRANSFORMATION_EXCEPTION, - UNIT_OF_TIME_SECOND, - UNIT_OF_TIME_MINUTE, - UNIT_OF_TIME_HOUR, - UNIT_OF_TIME_DAY, - UNIT_OF_TIME_DAY_OF_WEEK, - UNIT_OF_TIME_DAY_OF_YEAR, - UNIT_OF_TIME_MONTH, - UNIT_OF_TIME_QUARTER, - UNIT_OF_TIME_YEAR - } - - public static final String CONTENT_S_LOCALE_PREFIX = "content@s__locale@"; - - static final String PART_FIELDNAME_PREFIX = "part@sd@"; - static final Set DATE_PART_SUFFIXES = - Set.of(UNIT_OF_TIME_YEAR_FIELD_SUFFIX, - UNIT_OF_TIME_QUARTER_FIELD_SUFFIX, - UNIT_OF_TIME_MONTH_FIELD_SUFFIX, - UNIT_OF_TIME_DAY_FIELD_SUFFIX, - UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX, - UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX, - UNIT_OF_TIME_HOUR_FIELD_SUFFIX, - UNIT_OF_TIME_MINUTE_FIELD_SUFFIX, - UNIT_OF_TIME_SECOND_FIELD_SUFFIX); - /** - * Infix used for denoting a primitive single valued field with no doc values enabled - */ - private static final String SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER = "@s_@"; - - /** - * Infix used for denoting a primitive single valued field with no doc values enabled - */ - private static final String SINGLE_VALUE_WITH_DOC_VALUES_MARKER = "@sd@"; - - static final String SHARED_PROPERTIES = "shared.properties"; - - protected final static Logger log = LoggerFactory.getLogger(AlfrescoSolrDataModel.class); - - private static final String CHANGE_SET = "CHANGE_SET"; - - private static final String TX = "TX"; - - public static final String DEFAULT_TENANT = "_DEFAULT_"; - - private final static AlfrescoSolrDataModel INSTANCE = new AlfrescoSolrDataModel(); - - private final TenantService tenantService; - - private final NamespaceDAO namespaceDAO; - - private final DictionaryDAOImpl dictionaryDAO; - - private final Map dictionaryServices; - - private final Map cmisDictionaryServices; - - private final Map> modelErrors = new HashMap<>(); - private final Set suggestableProperties = new HashSet<>(); - private final Set crossLocaleSearchDataTypes = new HashSet<>(); - private final Set crossLocaleSearchProperties = new HashSet<>(); - private final Set identifierProperties = new HashSet<>(); - private final ThreadPoolExecutor threadPool; - - public void close() { - threadPool.shutdown(); - } - - public AlfrescoSolrDataModel() - { - tenantService = new SingleTServiceImpl(); - - dictionaryDAO = new DictionaryDAOImpl(); - dictionaryDAO.setTenantService(tenantService); - - try - { - CompiledModelsCache compiledModelsCache = new CompiledModelsCache(); - compiledModelsCache.setDictionaryDAO(dictionaryDAO); - compiledModelsCache.setTenantService(tenantService); - compiledModelsCache.setRegistry(new DefaultAsynchronouslyRefreshedCacheRegistry()); - TrackerPoolFactory trackerPoolFactory = new DefaultTrackerPoolFactory(new Properties(), "_dictionary_", "_internal_"); - threadPool = trackerPoolFactory.create(); - compiledModelsCache.setThreadPoolExecutor(threadPool); - - dictionaryDAO.setDictionaryRegistryCache(compiledModelsCache); - dictionaryDAO.setResourceClassLoader(getResourceClassLoader()); - dictionaryDAO.init(); - } - catch (Exception e) - { - throw new AlfrescoRuntimeException("Failed to create dictionaryDAO ", e); - } - - namespaceDAO = dictionaryDAO; - - QNameFilter qnameFilter = getQNameFilter(); - dictionaryServices = AlfrescoClientDataModelServicesFactory.constructDictionaryServices(qnameFilter, dictionaryDAO); - DictionaryComponent dictionaryComponent = getDictionaryService(CMISStrictDictionaryService.DEFAULT); - dictionaryComponent.setMessageLookup(new StaticMessageLookup()); - - cmisDictionaryServices = AlfrescoClientDataModelServicesFactory.constructDictionaries(qnameFilter, namespaceDAO, dictionaryComponent, dictionaryDAO); - - Properties props = getCommonConfig(); - for (Object key : props.keySet()) - { - String stringKey = (String) key; - if (stringKey.startsWith("alfresco.suggestable.property.")) - { - QName qName = QName.createQName(props.getProperty(stringKey)); - suggestableProperties.add(qName); - } - else if (stringKey.startsWith("alfresco.cross.locale.property.")) - { - QName qName = QName.createQName(props.getProperty(stringKey)); - crossLocaleSearchProperties.add(qName); - } - else if (stringKey.startsWith("alfresco.cross.locale.datatype.")) - { - QName qName = QName.createQName(props.getProperty(stringKey)); - crossLocaleSearchDataTypes.add(qName); - } - else if (stringKey.startsWith("alfresco.identifier.property.")) - { - QName qName = QName.createQName(props.getProperty(stringKey)); - identifierProperties.add(qName); - } - } - - if(props.isEmpty()) - { - suggestableProperties.add(ContentModel.PROP_NAME); - suggestableProperties.add(ContentModel.PROP_TITLE); - suggestableProperties.add(ContentModel.PROP_DESCRIPTION); - suggestableProperties.add(ContentModel.PROP_CONTENT); - - crossLocaleSearchDataTypes.add(DataTypeDefinition.TEXT); - crossLocaleSearchDataTypes.add(DataTypeDefinition.CONTENT); - crossLocaleSearchDataTypes.add(DataTypeDefinition.MLTEXT); - - identifierProperties.add(ContentModel.PROP_CREATOR); - identifierProperties.add(ContentModel.PROP_MODIFIER); - identifierProperties.add(ContentModel.PROP_USERNAME); - identifierProperties.add(ContentModel.PROP_AUTHORITY_NAME); - } - } - - public static String getTenantId(String tenant) - { - if((tenant == null) || tenant.equals(TenantService.DEFAULT_DOMAIN)) - { - return DEFAULT_TENANT; - } - else - { - return tenant.replaceAll("!", "_-._"); - } - } - - /** - * Returns the ACL Document identifier in the following format <TENANT>!<ACLID>!ACL - * - * @param tenant the tenant code. - * @param aclId the acl identifer - * @return the ACL Document identifier (<TENANT>!<ACLID>!ACL) - */ - public static String getAclDocumentId(String tenant, Long aclId) - { - return getTenantId(tenant) + "!" + NumericEncoder.encode(aclId) + "!ACL"; - } - - /** - * Returns the ACL ChangeSet Document identifier in the following format TRACKER!CHANGESET!<ACL_CHANGESET_ID> - * - * @param aclChangeSetId the ACL changeset identifier. - * @return the ACL ChangeSet Document identifier (TRACKER!CHANGESET!<ACL_CHANGESET_ID>) - */ - public static String getAclChangeSetDocumentId(Long aclChangeSetId) - { - return "TRACKER" + "!" + CHANGE_SET + "!" + NumericEncoder.encode(aclChangeSetId); - } - - - /** - * Extracts the transaction identifier from the input document identifier. - * The "input document identifier" is the alphanumeric identifier used for Solr documents. - * - * @param documentId the document identifier. - * @return the numeric ACL changeset identifier. - */ - public static Long parseTransactionId(String documentId) - { - return parseIdFromDocumentId(documentId); - } - - private static Long parseIdFromDocumentId(String documentId) - { - String[] split = documentId.split("!"); - if (split.length > 0) - return NumericEncoder.decodeLong(split[split.length - 1]); - else - return null; - } - - /** - * Returns the Solr document identifier in the following format <TENANT>!<!<DBID> - * - * @param tenant the tenant code. - * @param dbid the DB identifier. - * @return the Solr document identifier in the following format <TENANT>!<!<DBID> - */ - public static String getNodeDocumentId(String tenant, Long dbid) - { - return getTenantId(tenant) + "!" + NumericEncoder.encode(dbid); - } - - /** - * Destructures a document identifier in the three compounding parts (tenant and dbid). - * - * @param id the document identifier. - * @return a {@link TenantDbId} instance containing the destructured identifier. - */ - public static TenantDbId decodeNodeDocumentId(String id) - { - TenantDbId ids = new TenantDbId(); - String[] split = id.split("!"); - if (split.length > 0) - ids.tenant = split[0]; - if (split.length > 1) - ids.dbId = NumericEncoder.decodeLong(split[1]); - return ids; - } - - /** - * Returns the Transaction Document identifier in the following format TRACKER!TX!<TX_ID> - * - * @param txId the transaction identifier. - * @return the Transaction Document identifier in the following format TRACKER!TX!<TX_ID> - */ - public static String getTransactionDocumentId(Long txId) - { - // TODO - check and encode for ":" - return "TRACKER" + - "!" + - TX + - "!" + - NumericEncoder.encode(txId); - } - - public String getAlfrescoPropertyFromSchemaField(String schemaField) - { - int index = schemaField.lastIndexOf("@{"); - if(index == -1) - { - return schemaField; - } - - String alfrescoQueryField = schemaField.substring(index+1); - QName qName = QName.createQName(alfrescoQueryField); - alfrescoQueryField = qName.toPrefixString(namespaceDAO); - - PropertyDefinition propertyDefinition = getPropertyDefinition(qName); - if((propertyDefinition == null)) - { - return alfrescoQueryField; - } - if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) - { - return alfrescoQueryField; - } - - DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); - if(dataTypeDefinition.getName().equals(DataTypeDefinition.CONTENT)) - { - if(schemaField.contains("__size@")) - { - return alfrescoQueryField + ".size"; - } - else if(schemaField.contains("__locale@")) - { - return alfrescoQueryField + ".locale"; - } - else if(schemaField.contains("__mimetype@")) - { - return alfrescoQueryField + ".mimetype"; - } - else if(schemaField.contains("__encoding@")) - { - return alfrescoQueryField + ".encoding"; - } - else if(schemaField.contains("__docid@")) - { - return alfrescoQueryField + ".docid"; - } - else if(schemaField.contains("__tr_ex@")) - { - return alfrescoQueryField + ".tr_ex"; - } - else if(schemaField.contains("__tr_time@")) - { - return alfrescoQueryField + ".tr_time"; - } - else if(schemaField.contains("__tr_status@")) - { - return alfrescoQueryField + ".tr_status"; - } - else - { - return alfrescoQueryField; - } - } - else - { - return alfrescoQueryField; - } - } - - public static AlfrescoSolrDataModel getInstance() - { - return INSTANCE; - } - - public NamespaceDAO getNamespaceDAO() - { - return namespaceDAO; - } - - /** - * TODO: Fix to load type filter/exclusions from somewhere sensible - * @return QNameFilter - */ - private QNameFilter getQNameFilter() - { - QNameFilter qnameFilter = null; - FileSystemXmlApplicationContext ctx = null; - - File resourceDirectory = getResourceDirectory(); - // If we ever need to filter out models in the future, then we must put a filter somewhere. - // Currently, we do not need to filter any models, so this filter does not exist. - File filterContext = new File(resourceDirectory, "opencmis-qnamefilter-context.xml"); - - if(!filterContext.exists()) - { - log.info("No type filter context found at " + filterContext.getAbsolutePath() + ", no type filtering"); - return qnameFilter; - } - - try - { - ctx = new FileSystemXmlApplicationContext(new String[] { "file:" + filterContext.getAbsolutePath() }, false); - ctx.setClassLoader(this.getClass().getClassLoader()); - ctx.refresh(); - qnameFilter = (QNameFilter)ctx.getBean("cmisTypeExclusions"); - if(qnameFilter == null) - { - log.warn("Unable to find type filter at " + filterContext.getAbsolutePath() + ", no type filtering"); - } - } - catch(BeansException e) - { - log.warn("Unable to parse type filter at " + filterContext.getAbsolutePath() + ", no type filtering"); - } - finally - { - if(ctx != null && ctx.getBeanFactory() != null && ctx.isActive()) - { - ctx.close(); - } - } - - return qnameFilter; - } - - public static Properties getCommonConfig() - { - File resourceDirectory = getResourceDirectory(); - // If we ever need to filter out models in the future, then we must put a filter somewhere. - // Currently, we do not need to filter any models, so this filter does not exist. - File propertiesFile = new File(resourceDirectory, SHARED_PROPERTIES); - - Properties props = new Properties(); - if(!propertiesFile.exists()) - { - log.info("No shared properties found at " + propertiesFile.getAbsolutePath()); - return props; - } - - try(InputStream is = new FileInputStream(propertiesFile)) - { - props.load(is); - } - catch (IOException e) - { - log.info("Failed to read shared properties at " + propertiesFile.getAbsolutePath()); - } - - return props; - } - - /** - * @return ClassLoader - */ - public ClassLoader getResourceClassLoader() - { - File f = getResourceDirectory(); - if (f.canRead() && f.isDirectory()) - { - URL[] urls = new URL[1]; - - try - { - URL url = f.toURI().normalize().toURL(); - urls[0] = url; - } - catch (MalformedURLException e) - { - throw new AlfrescoRuntimeException("Failed to add resources to classpath ", e); - } - - return URLClassLoader.newInstance(urls, this.getClass().getClassLoader()); - } - else - { - return this.getClass().getClassLoader(); - } - } - - public static File getResourceDirectory() - { - return new File(SolrResourceLoader.locateSolrHome().toFile(), "conf"); - } - - /** - * Gets a DictionaryService, if an Alternative dictionary is specified it tries to get that. - * It will attempt to get the DEFAULT dictionary service if null is specified or it can't find - * a dictionary with the name of "alternativeDictionary" - * @param alternativeDictionary - can be null; - * @return DictionaryService - */ - public DictionaryComponent getDictionaryService(String alternativeDictionary) - { - DictionaryComponent dictionaryComponent = null; - - if (alternativeDictionary != null && !alternativeDictionary.trim().isEmpty()) - { - dictionaryComponent = dictionaryServices.get(alternativeDictionary); - } - - if (dictionaryComponent == null) - { - dictionaryComponent = dictionaryServices.get(CMISStrictDictionaryService.DEFAULT); - } - return dictionaryComponent; - } - - public IndexedField getIndexedFieldForSpecializedPropertyMetadata(QName propertyQName, SpecializedFieldType type) - { - IndexedField indexedField = new IndexedField(); - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - if((propertyDefinition == null)) - { - return indexedField; - } - if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) - { - return indexedField; - } - - DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); - if(dataTypeDefinition.getName().equals(DataTypeDefinition.CONTENT)) - { - StringBuilder builder = new StringBuilder(); - builder.append(dataTypeDefinition.getName().getLocalName()); - builder.append('@'); - // TODO wher we support multi value propertis correctly .... builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); - builder.append('s'); - builder.append("_"); - builder.append('_'); - switch (type) - { - case CONTENT_DOCID: - builder.append("docid"); - break; - case CONTENT_ENCODING: - builder.append("encoding"); - break; - case CONTENT_LOCALE: - builder.append("locale"); - break; - case CONTENT_MIMETYPE: - builder.append("mimetype"); - break; - case CONTENT_SIZE: - builder.append("size"); - break; - case TRANSFORMATION_EXCEPTION: - builder.append("tr_ex"); - break; - case TRANSFORMATION_STATUS: - builder.append("tr_status"); - break; - case TRANSFORMATION_TIME: - builder.append("tr_time"); - break; - default: - break; - } - - builder.append('@'); - builder.append(propertyQName); - indexedField.addField(builder.toString(), false, false); - } - else if (isDateOrDatetime(dataTypeDefinition)) - { - String dateDerivedSuffix = getDateDerivedSuffix(type); - if (dateDerivedSuffix != null) - { - indexedField.addField(getDateDerivedField(propertyQName, dateDerivedSuffix), false, true); - } - } - return indexedField; - - } - - - public IndexedField getQueryableFields(QName propertyQName, SpecializedFieldType type, FieldUse fieldUse) - { - if(type != null) - { - return getIndexedFieldForSpecializedPropertyMetadata(propertyQName, type); - } - - IndexedField indexedField = new IndexedField(); - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - if((propertyDefinition == null)) - { - indexedField.addField("_dummy_", false, false); - return indexedField; - } - if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) - { - indexedField.addField("_dummy_", false, false); - return indexedField; - } - - if(isTextField(propertyDefinition)) - { - switch(fieldUse) - { - case COMPLETION: - addCompletionFields(propertyDefinition, indexedField); - break; - case FACET: - addFacetSearchFields(propertyDefinition, indexedField); - break; - case FTS: - addFullTextSearchFields(propertyDefinition, indexedField); - break; - case ID: - addIdentifierSearchFields(propertyDefinition, indexedField); - break; - case EXACT: - addExactSearchFields(propertyDefinition, indexedField); - break; - case MULTI_FACET: - addMultiSearchFields(propertyDefinition, indexedField); - break; - case SORT: - addSortSearchFields(propertyDefinition, indexedField); - break; - case STATS: - addStatsSearchFields(propertyDefinition, indexedField); - break; - case SUGGESTION: - if(isSuggestable(propertyQName)) - { - indexedField.addField("suggest", false, false); - } - addCompletionFields(propertyDefinition, indexedField); - break; - case HIGHLIGHT: - addHighlightSearchFields(propertyDefinition, indexedField); - break; - } - } - else - { - indexedField.addField(getFieldForNonText(propertyDefinition), false, false); - } - return indexedField; - } - - /* - * Adds best completion fields in order of preference - */ - private void addCompletionFields(PropertyDefinition propertyDefinition, IndexedField indexedField) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - else if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE)) - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - else if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE)) - { - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - } - - /* - * Adds best fts fields in order of preference - */ - - private void addFullTextSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - if (((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - && !isIdentifierTextProperty(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - } - else - { - indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - } - - private void addHighlightSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - QName propertyName = propertyDefinition.getName(); - QName propertyDataTypeQName = propertyDefinition.getDataType().getName(); - String fieldName; - - if(propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) - { - fieldName = getStoredMLTextField(propertyName); - } - else if(propertyDataTypeQName.equals(DataTypeDefinition.CONTENT)) - { - fieldName = getStoredContentField(propertyName); - } - else - { - fieldName = getStoredTextField(propertyName); - } - - indexedField.addField(fieldName, false, false); - } - - /* - * If the untokenised version is available, go for it. - * If not it takes the tokenised versions available. - */ - private void addIdentifierSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - - indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - } - } - - /* - * If only the untokenised version is available, go for it. - * If not it takes the tokenised cross locale version. - */ - private void addExactSearchFields(PropertyDefinition propertyDefinition, IndexedField indexedField) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || !(propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - - indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } else{ - throw new UnsupportedOperationException("Exact Term search is not supported unless you configure the field <"+propertyDefinition.getName()+"> for cross locale search"); - } - } - } - - /* - * Adds best identifier fields in order of preference - */ - private void addFacetSearchFields(PropertyDefinition propertyDefinition, IndexedField indexedField) - { - if(propertyDefinition.getDataType().getName().equals(DataTypeDefinition.TEXT)) - { - if (!isIdentifierTextProperty(propertyDefinition.getName())) - { - if(propertyDefinition.getFacetable() == Facetable.TRUE) - { - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - } - } - - - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH) - || isIdentifierTextProperty(propertyDefinition.getName())) - { - - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - } - - private void addMultiSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH) - || isIdentifierTextProperty(propertyDefinition.getName())) - { - - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - } - - private void addStatsSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - addFacetSearchFields(propertyDefinition, indexedField); - } - - private void addSortSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - // Can only order on single valued fields - DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); - if(dataTypeDefinition.getName().equals(DataTypeDefinition.TEXT)) - { - if(!propertyDefinition.isMultiValued()) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); - } - else if (isIdentifierTextProperty(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - } - } - - if(dataTypeDefinition.getName().equals(DataTypeDefinition.MLTEXT)) - { - if(!propertyDefinition.isMultiValued()) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - } - } - } - - - public String getStoredTextField(QName propertyQName) - { - return getStoredTextField(propertyQName, null); - } - - public String getStoredTextField(QName propertyQName, String suffix) - { - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - - StringBuilder sb = new StringBuilder(); - sb.append("text@" + (propertyDefinition.isMultiValued()? "m" : "s") + "_stored_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || - isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); - - sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || - crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || - isIdentifierTextProperty(propertyDefinition.getName())) && !propertyDefinition.isMultiValued()? "s" : "_"); - - sb.append(isSuggestable(propertyQName)? "s": "_"); - - sb.append("@"); - sb.append(propertyDefinition.getName().toString()); - - if (suffix != null) - { - sb.append(suffix); - } - - return sb.toString(); - - } - - public String getStoredMLTextField(QName propertyQName) - { - return getStoredMLTextField(propertyQName, null); - } - - public String getStoredMLTextField(QName propertyQName, String suffix) - { - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - - StringBuilder sb = new StringBuilder(); - sb.append("mltext@m_stored_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || - isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); - - sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || - crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); - - sb.append("_"); - - sb.append(isSuggestable(propertyQName)? "s": "_"); - - sb.append("@"); - sb.append(propertyDefinition.getName().toString()); - - if (suffix != null) - { - sb.append(suffix); - } - - return sb.toString(); - - } - - public String getStoredContentField(QName propertyQName) - { - return getStoredContentField(propertyQName, null); - } - - public String getStoredContentField(QName propertyQName, String suffix) - { - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - - StringBuilder sb = new StringBuilder(); - sb.append("content@s_stored_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || - isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); - - sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || - crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); - - - sb.append("_"); - sb.append(isSuggestable(propertyQName)? "s": "_"); - - sb.append("@"); - sb.append(propertyDefinition.getName().toString()); - - if (suffix != null) - { - sb.append(suffix); - } - - return sb.toString(); - - } - - public String getDateDerivedField(QName propertyQName, String suffix) - { - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - return "part@sd@" + propertyDefinition.getName().toString() + suffix; - } - - public String getDateDerivedSuffix(SpecializedFieldType type) - { - switch (type) - { - case UNIT_OF_TIME_SECOND: - return UNIT_OF_TIME_SECOND_FIELD_SUFFIX; - case UNIT_OF_TIME_MINUTE: - return UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; - case UNIT_OF_TIME_HOUR: - return UNIT_OF_TIME_HOUR_FIELD_SUFFIX; - case UNIT_OF_TIME_DAY: - return UNIT_OF_TIME_DAY_FIELD_SUFFIX; - case UNIT_OF_TIME_DAY_OF_WEEK: - return UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX; - case UNIT_OF_TIME_DAY_OF_YEAR: - return UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX; - case UNIT_OF_TIME_MONTH: - return UNIT_OF_TIME_MONTH_FIELD_SUFFIX; - case UNIT_OF_TIME_QUARTER: - return UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; - case UNIT_OF_TIME_YEAR: - return UNIT_OF_TIME_YEAR_FIELD_SUFFIX; - default: - return null; - } - } - - - /** - * Get all the field names into which we must copy the source data - * - * @param propertyQName QName - * @return IndexedField - */ - public IndexedField getIndexedFieldNamesForProperty(QName propertyQName) - { - // TODO: Cache and throw on model refresh - - IndexedField indexedField = new IndexedField(); - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - if((propertyDefinition == null)) - { - return indexedField; - } - if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) - { - return indexedField; - } - - DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); - if(isTextField(propertyDefinition)) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - } - - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH - || isIdentifierTextProperty(propertyDefinition.getName()))) - { - indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - - if(dataTypeDefinition.getName().equals(DataTypeDefinition.TEXT)) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - if(!propertyDefinition.isMultiValued()) - { - indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); - } - } - else if (!isIdentifierTextProperty(propertyDefinition.getName())) - { - if(propertyDefinition.getFacetable() == Facetable.TRUE) - { - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - } - } - - if(dataTypeDefinition.getName().equals(DataTypeDefinition.MLTEXT)) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - if(!propertyDefinition.isMultiValued()) - { - indexedField.addField(getFieldForText(true, false, true, propertyDefinition), true, true); - } - } - } - - if(isSuggestable(propertyQName)) - { - indexedField.addField("suggest_@" + propertyDefinition.getName().toString(), false, false); - } - } - else - { - indexedField.addField(getFieldForNonText(propertyDefinition), false, false); - } - - return indexedField; - } - - private boolean isIdentifierTextProperty(QName propertyQName) - { - return identifierProperties.contains(propertyQName); - } - - public boolean isTextField(PropertyDefinition propertyDefinition) - { - return ofNullable(propertyDefinition) - .map(PropertyDefinition::getDataType) - .map(DataTypeDefinition::getName) - .map(name -> - name.equals(DataTypeDefinition.MLTEXT) - || - name.equals(DataTypeDefinition.CONTENT) - || - name.equals(DataTypeDefinition.TEXT)) - .orElse(false); - } - - private boolean isSuggestable(QName propertyQName) - { - if(propertyQName == null) - { - return false; - } - return suggestableProperties.contains(propertyQName); - } - - private boolean hasDocValues(PropertyDefinition propertyDefinition) - { - - if(isTextField(propertyDefinition)) - { - // We only call this if text is untokenised and localised - return propertyDefinition.getFacetable() != Facetable.FALSE; - } - else - { - if(propertyDefinition.getFacetable() == Facetable.FALSE) - { - return false; - } - else if(propertyDefinition.getFacetable() == Facetable.TRUE) - { - return true; - } - else - { - if(propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) - { - return false; - } - else - { - return isPrimitive(propertyDefinition.getDataType()); - } - } - } - } - - public String getFieldForNonText(PropertyDefinition propertyDefinition) - { - StringBuilder builder = new StringBuilder(); - QName qName = propertyDefinition.getDataType().getName(); - builder.append(qName.getLocalName()); - builder.append("@"); - builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); - builder.append(hasDocValues(propertyDefinition) ? "d" : "_"); - builder.append("@"); - builder.append(propertyDefinition.getName().toString()); - return builder.toString(); - } - - private String getFieldForText(boolean localised, boolean tokenised, boolean sort, PropertyDefinition propertyDefinition) - { - StringBuilder builder = new StringBuilder(); - QName qName = propertyDefinition.getDataType().getName(); - builder.append(qName.getLocalName()); - builder.append("@"); - QName propertyDataTypeQName = propertyDefinition.getDataType().getName(); - if(propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) - { - builder.append('m'); - } - else if(propertyDataTypeQName.equals(DataTypeDefinition.CONTENT)) - { - builder.append('s'); - } - else - { - builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); - } - if(sort || localised || tokenised || propertyDataTypeQName.equals(DataTypeDefinition.CONTENT) || propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) - { - builder.append('_'); - } - else - { - builder.append(hasDocValues(propertyDefinition) ? "d" : "_"); - } - builder.append('_'); - if(!sort) - { - builder.append(localised ? "l" : "_"); - builder.append(tokenised ? "t" : "_"); - } - else - { - builder.append("sort"); - } - builder.append("@"); - builder.append(propertyDefinition.getName().toString()); - return builder.toString(); - } - - public PropertyDefinition getPropertyDefinition(QName propertyQName) - { - return getDictionaryService(CMISStrictDictionaryService.DEFAULT).getProperty(propertyQName); - } - - public boolean putModel(M2Model model) - { - modelErrors.remove(model.getName()); - dictionaryDAO.putModelIgnoringConstraints(model); - return true; - } - - public void removeModel(QName modelQName) - { - modelErrors.remove(getM2Model(modelQName).getName()); - dictionaryDAO.removeModel(modelQName); - } - - M2Model getM2Model(QName modelQName) - { - return dictionaryDAO.getCompiledModel(modelQName).getM2Model(); - } - - public void afterInitModels() - { - for (CMISAbstractDictionaryService cds : cmisDictionaryServices.values()) - { - cds.afterDictionaryInit(); - } - } - - public org.alfresco.repo.search.impl.querymodel.Query parseCMISQueryToAlfrescoAbstractQuery(CMISQueryMode mode, SearchParameters searchParameters, - SolrQueryRequest req, String alternativeDictionary, CmisVersion cmisVersion) - { - // convert search parameters to cmis query options - // TODO: how to handle store ref - CMISQueryOptions options = new CMISQueryOptions(searchParameters.getQuery(), StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); - options.setQueryMode(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS); - options.setDefaultFieldName(searchParameters.getDefaultFieldName()); - // TODO: options.setDefaultFTSConnective() - // TODO: options.setDefaultFTSFieldConnective() - options.setIncludeInTransactionData(!searchParameters.excludeDataInTheCurrentTransaction()); - options.setLocales(searchParameters.getLocales()); - options.setMlAnalaysisMode(searchParameters.getMlAnalaysisMode()); - options.setQueryParameterDefinitions(searchParameters.getQueryParameterDefinitions()); - for(String name : searchParameters.getQueryTemplates().keySet()) - { - String template = searchParameters.getQueryTemplates().get(name); - options.addQueryTemplate(name, template); - } - - // parse cmis syntax - CapabilityJoin joinSupport = (mode == CMISQueryMode.CMS_STRICT) ? CapabilityJoin.NONE : CapabilityJoin.INNERANDOUTER; - CmisFunctionEvaluationContext functionContext = getCMISFunctionEvaluationContext(mode, cmisVersion, alternativeDictionary); - - CMISDictionaryService cmisDictionary = getCMISDictionary(alternativeDictionary, cmisVersion); - - CMISQueryParser parser = new CMISQueryParser(options, cmisDictionary, joinSupport); - org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = parser.parse(new LuceneQueryModelFactory(), functionContext); - - if (queryModelQuery.getSource() != null) - { - List> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext); - if (selectorGroups.size() == 0) - { - throw new UnsupportedOperationException("No selectors"); - } - } - return queryModelQuery; - } - - public CmisFunctionEvaluationContext getCMISFunctionEvaluationContext(CMISQueryMode mode, CmisVersion cmisVersion, String alternativeDictionary) - { - BaseTypeId[] validScopes = (mode == CMISQueryMode.CMS_STRICT) ? CmisFunctionEvaluationContext.STRICT_SCOPES : CmisFunctionEvaluationContext.ALFRESCO_SCOPES; - CmisFunctionEvaluationContext functionContext = new CmisFunctionEvaluationContext(); - functionContext.setCmisDictionaryService(getCMISDictionary(alternativeDictionary, cmisVersion)); - functionContext.setValidScopes(validScopes); - return functionContext; - } - - /** - * Gets the CMISDictionaryService, if an Alternative dictionary is specified it tries to get that. - * It will attempt to get the DEFAULT dictionary service if null is specified or it can't find - * a dictionary with the name of "alternativeDictionary" - * - * @param alternativeDictionary - can be null; - * @return CMISDictionaryService - */ - public CMISDictionaryService getCMISDictionary(String alternativeDictionary, CmisVersion cmisVersion) - { - CMISDictionaryService cmisDictionary = null; - - if (alternativeDictionary != null && !alternativeDictionary.trim().isEmpty()) - { - DictionaryKey key = new DictionaryKey(cmisVersion, alternativeDictionary); - cmisDictionary = cmisDictionaryServices.get(key); - } - - if (cmisDictionary == null) - { - DictionaryKey key = new DictionaryKey(cmisVersion, CMISStrictDictionaryService.DEFAULT); - cmisDictionary = cmisDictionaryServices.get(key); - } - return cmisDictionary; - } - - /** - * Returns the Alfresco models associated with the current dictionary. - * - * @return the Alfresco models associated with the current dictionary. - */ - public List getAlfrescoModels() - { - return dictionaryDAO.getModels().stream() - .map(qname -> { - M2Model m2Model = dictionaryDAO.getCompiledModel(qname).getM2Model(); - return new AlfrescoModel( - m2Model, - getDictionaryService( - CMISStrictDictionaryService.DEFAULT).getModel(qname).getChecksum(ModelDefinition.XMLBindingType.DEFAULT));}) - .collect(Collectors.toList()); - } - - Map> getModelErrors() - { - return modelErrors; - } - - public static class IndexedField - { - private final List fields = new LinkedList<>(); - - public List getFields() - { - return unmodifiableList(fields); - } - - public void addField(String prefix, boolean localised, boolean sort) - { - fields.add(new FieldInstance(prefix, localised, sort)); - } - } - - public static class FieldInstance - { - final String field; - final boolean localised; - final boolean sort; - - public FieldInstance(String field, boolean localised, boolean sort) - { - this.field = field; - this.localised = localised; - this.sort = sort; - } - - public String getField() - { - return field; - } - - public boolean isLocalised() - { - return localised; - } - - public boolean isSort() - { - return sort; - } - } - - public Query getCMISQuery(CMISQueryMode mode, Pair searchParametersAndFilter, SolrQueryRequest req, org.alfresco.repo.search.impl.querymodel.Query queryModelQuery, CmisVersion cmisVersion, String alternativeDictionary) throws ParseException - { - SearchParameters searchParameters = searchParametersAndFilter.getFirst(); - Boolean isFilter = searchParametersAndFilter.getSecond(); - - CmisFunctionEvaluationContext functionContext = getCMISFunctionEvaluationContext(mode, cmisVersion, alternativeDictionary); - - Set selectorGroup = queryModelQuery.getSource().getSelectorGroups(functionContext).get(0); - - QueryBuilderContext luceneContext = getLuceneQueryBuilderContext(searchParameters, req, alternativeDictionary, FTSQueryParser.RerankPhase.SINGLE_PASS); - @SuppressWarnings("unchecked") - LuceneQueryBuilder builder = (LuceneQueryBuilder) queryModelQuery; - org.apache.lucene.search.Query luceneQuery = builder.buildQuery(selectorGroup, luceneContext, functionContext); - - return new ContextAwareQuery(luceneQuery, Boolean.TRUE.equals(isFilter) ? null : searchParameters); - } - - public QueryBuilderContext getLuceneQueryBuilderContext(SearchParameters searchParameters, SolrQueryRequest req, String alternativeDictionary, FTSQueryParser.RerankPhase rerankPhase) - { - return new Lucene4QueryBuilderContextSolrImpl( - getDictionaryService(alternativeDictionary), - namespaceDAO, - tenantService, - searchParameters, - MLAnalysisMode.EXACT_LANGUAGE, - req, - this, - rerankPhase); - } - - public Solr4QueryParser getLuceneQueryParser(SearchParameters searchParameters, SolrQueryRequest req, FTSQueryParser.RerankPhase rerankPhase) - { - Analyzer analyzer = req.getSchema().getQueryAnalyzer(); - Solr4QueryParser parser = new Solr4QueryParser(req, Version.LATEST, searchParameters.getDefaultFieldName(), analyzer, rerankPhase); - parser.setNamespacePrefixResolver(namespaceDAO); - parser.setDictionaryService(getDictionaryService(CMISStrictDictionaryService.DEFAULT)); - parser.setTenantService(tenantService); - parser.setSearchParameters(searchParameters); - parser.setAllowLeadingWildcard(true); - - Properties props = new CoreDescriptorDecorator(req.getCore().getCoreDescriptor()).getProperties(); - int topTermSpanRewriteLimit = Integer.parseInt(props.getProperty("alfresco.topTermSpanRewriteLimit", "1000")); - parser.setTopTermSpanRewriteLimit(topTermSpanRewriteLimit); - - return parser; - } - - public Query getFTSQuery(Pair searchParametersAndFilter, SolrQueryRequest req, FTSQueryParser.RerankPhase rerankPhase) throws ParseException - { - - SearchParameters searchParameters = searchParametersAndFilter.getFirst(); - Boolean isFilter = searchParametersAndFilter.getSecond(); - - QueryModelFactory factory = new LuceneQueryModelFactory(); - AlfrescoFunctionEvaluationContext functionContext = new AlfrescoSolr4FunctionEvaluationContext(namespaceDAO, getDictionaryService(CMISStrictDictionaryService.DEFAULT), NamespaceService.CONTENT_MODEL_1_0_URI, req.getSchema()); - - FTSParser.Mode mode; - - if (searchParameters.getDefaultFTSOperator() == org.alfresco.service.cmr.search.SearchParameters.Operator.AND) - { - mode = FTSParser.Mode.DEFAULT_CONJUNCTION; - } - else - { - mode = FTSParser.Mode.DEFAULT_DISJUNCTION; - } - - Constraint constraint = FTSQueryParser.buildFTS(searchParameters.getQuery(), factory, functionContext, null, null, mode, - searchParameters.getDefaultFTSOperator() == org.alfresco.service.cmr.search.SearchParameters.Operator.OR ? Connective.OR : Connective.AND, - searchParameters.getQueryTemplates(), searchParameters.getDefaultFieldName(), rerankPhase); - org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = factory.createQuery(null, null, constraint, new ArrayList<>()); - - @SuppressWarnings("unchecked") - LuceneQueryBuilder builder = (LuceneQueryBuilder) queryModelQuery; - - QueryBuilderContext luceneContext = getLuceneQueryBuilderContext(searchParameters, req, CMISStrictDictionaryService.DEFAULT, rerankPhase); - - Set selectorGroup = null; - if (queryModelQuery.getSource() != null) - { - List> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext); - - if (selectorGroups.size() == 0) - { - throw new UnsupportedOperationException("No selectors"); - } - - if (selectorGroups.size() > 1) - { - throw new UnsupportedOperationException("Advanced join is not supported"); - } - - selectorGroup = selectorGroups.get(0); - } - Query luceneQuery = builder.buildQuery(selectorGroup, luceneContext, functionContext); - // query needs some search parameters fro correct caching .... - - return new ContextAwareQuery(luceneQuery, Boolean.TRUE.equals(isFilter) ? null : searchParameters); - } - - private PropertyDefinition getPropertyDefinition(String identifier) - { - return QueryParserUtils.matchPropertyDefinition(NamespaceService.CONTENT_MODEL_1_0_URI, - getNamespaceDAO(), - getDictionaryService(CMISStrictDictionaryService.DEFAULT), - identifier); - } - - public String mapProperty(String potentialProperty, FieldUse fieldUse, SolrQueryRequest req) - { - return mapProperty(potentialProperty, fieldUse, req, 0); - } - - /** - * - * return the stored field associated to potentialProperty parameter - */ - public String mapStoredProperty(String potentialProperty, SolrQueryRequest req) - { - if(potentialProperty.equals("asc") || potentialProperty.equals("desc") || potentialProperty.equals("_docid_")) - { - return potentialProperty; - } - - if(potentialProperty.equalsIgnoreCase("score") || potentialProperty.equalsIgnoreCase("SEARCH_SCORE")) - { - return "score"; - } - - AlfrescoFunctionEvaluationContext functionContext = - new AlfrescoSolr4FunctionEvaluationContext( - getNamespaceDAO(), - getDictionaryService(CMISStrictDictionaryService.DEFAULT), - NamespaceService.CONTENT_MODEL_1_0_URI, - req.getSchema()); - - - Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(potentialProperty); - String luceneField = functionContext.getLuceneFieldName(fieldNameAndEnding.getFirst()); - - PropertyDefinition propertyDef = getPropertyDefinition(fieldNameAndEnding.getFirst()); - //Retry scan using luceneField. - if(propertyDef == null) - { - if(luceneField.contains("@")) - { - int index = luceneField.lastIndexOf("@"); - propertyDef = getPropertyDefinition(luceneField.substring(index +1)); - } - } - - if (propertyDef == null || propertyDef.getName() == null) - { - return mapNonPropertyFields(luceneField); - } - - if (isDateOrDatetime(propertyDef.getDataType()) && isDerivedDateField(fieldNameAndEnding.getSecond())) - { - return getDateDerivedField(propertyDef.getName(), fieldNameAndEnding.getSecond()); - } - else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) - { - return getStoredTextField(propertyDef.getName(), fieldNameAndEnding.getSecond()); - } - else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) - { - return getStoredMLTextField(propertyDef.getName(), fieldNameAndEnding.getSecond()); - } - else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) - { - return getStoredContentField(propertyDef.getName(), fieldNameAndEnding.getSecond()); - } - else - { - return mapAlfrescoField(FieldUse.FTS, 0, fieldNameAndEnding, luceneField, propertyDef) - + fieldNameAndEnding.getSecond(); - } - } - - public boolean isDerivedDateField(String suffix) - { - return DATE_PART_SUFFIXES.contains(suffix); - } - - public String mapProperty(String potentialProperty, FieldUse fieldUse, SolrQueryRequest req, int position) - { - if(potentialProperty.equals("asc") || potentialProperty.equals("desc") || potentialProperty.equals("_docid_")) - { - return potentialProperty; - } - - if(potentialProperty.equalsIgnoreCase("score") || potentialProperty.equalsIgnoreCase("SEARCH_SCORE")) - { - return "score"; - } - - if(req.getSchema().getFieldOrNull(potentialProperty) != null) - { - return mapNonPropertyFields(potentialProperty); - } - - AlfrescoFunctionEvaluationContext functionContext = - new AlfrescoSolr4FunctionEvaluationContext( - getNamespaceDAO(), - getDictionaryService(CMISStrictDictionaryService.DEFAULT), - NamespaceService.CONTENT_MODEL_1_0_URI, - req.getSchema()); - - Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(potentialProperty); - String luceneField = functionContext.getLuceneFieldName(fieldNameAndEnding.getFirst()); - - PropertyDefinition propertyDef = getPropertyDefinition(fieldNameAndEnding.getFirst()); - //Retry scan using luceneField. - if(propertyDef == null) - { - if(luceneField.contains("@")) - { - int index = luceneField.lastIndexOf("@"); - propertyDef = getPropertyDefinition(luceneField.substring(index +1)); - } - } - String solrSortField; - solrSortField = mapAlfrescoField(fieldUse, position, fieldNameAndEnding, luceneField, propertyDef); - return solrSortField; - } - - public String mapNonPropertyFields(String queryField) - { - switch(queryField) - { - case "ID": - return "LID"; - case "EXACTTYPE": - return "TYPE"; - default: - return queryField; - - } - } - - /** - * @param ending String - * @return SpecializedFieldType - */ - public SpecializedFieldType getTextField(String ending) - { - switch(ending) - { - case FIELD_MIMETYPE_SUFFIX: - return SpecializedFieldType.CONTENT_MIMETYPE; - case FIELD_SIZE_SUFFIX: - return SpecializedFieldType.CONTENT_SIZE; - case FIELD_LOCALE_SUFFIX: - return SpecializedFieldType.CONTENT_LOCALE; - case FIELD_ENCODING_SUFFIX: - return SpecializedFieldType.CONTENT_ENCODING; - case UNIT_OF_TIME_SECOND_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_SECOND; - case UNIT_OF_TIME_MINUTE_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_MINUTE; - case UNIT_OF_TIME_HOUR_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_HOUR; - case UNIT_OF_TIME_DAY_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_DAY; - case UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_DAY_OF_WEEK; - case UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_DAY_OF_YEAR; - case UNIT_OF_TIME_MONTH_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_MONTH; - case UNIT_OF_TIME_QUARTER_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_QUARTER; - case UNIT_OF_TIME_YEAR_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_YEAR; - case FIELD_TRANSFORMATION_STATUS_SUFFIX: - case FIELD_TRANSFORMATION_TIME_SUFFIX: - case FIELD_TRANSFORMATION_EXCEPTION_SUFFIX: - default: - return null; - } - } - - public void setCMDefaultUri() - { - if(getNamespaceDAO().getURIs().contains(NamespaceService.CONTENT_MODEL_1_0_URI)) - { - getNamespaceDAO().addPrefix("", NamespaceService.CONTENT_MODEL_1_0_URI); - } - } - - /** - * Returns the prefix used for denoting a field which is meant to represent a constituent part of a - * date or datetime (e.g. year, month, second, minute). - * - * @param sourceFieldName the date/datetime source field name. - * @return the prefix that can be used for denoting a date or datetime part - */ - public String destructuredDateTimePartFieldNamePrefix(String sourceFieldName) { - // source field name example: datetime@sd@{http://www.alfresco.org/model/content/1.0}created - // prefix: datetime - String prefix = sourceFieldName.substring(0, sourceFieldName.indexOf("@")); - - // prefix, docValues disabled option: datetime@s_@ - String sourceFieldNamePrefixWithoutDocValues = prefix + SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER; - - // prefix, docValues enabled option: datetime@sd@ - String sourceFieldNamePrefixWithDocValues = prefix + SINGLE_VALUE_WITH_DOC_VALUES_MARKER; - - return sourceFieldName.replace(sourceFieldNamePrefixWithoutDocValues, PART_FIELDNAME_PREFIX) - .replace(sourceFieldNamePrefixWithDocValues, PART_FIELDNAME_PREFIX); - } - - private boolean isDateOrDatetime(DataTypeDefinition dataType) - { - return dataType.getName().equals(DataTypeDefinition.DATE) || - dataType.getName().equals(DataTypeDefinition.DATETIME); - } - - private boolean isPrimitive(DataTypeDefinition dataType) - { - QName name = dataType.getName(); - return name.equals(DataTypeDefinition.INT) - || name.equals(DataTypeDefinition.LONG) - || name.equals(DataTypeDefinition.FLOAT) - || name.equals(DataTypeDefinition.DOUBLE) - || isDateOrDatetime(dataType) - || name.equals(DataTypeDefinition.BOOLEAN) - || name.equals(DataTypeDefinition.CATEGORY) - || name.equals(DataTypeDefinition.NODE_REF); - } - - private String mapAlfrescoField(FieldUse fieldUse, int position, Pair fieldNameAndEnding, String luceneField, PropertyDefinition propertyDef) - { - String solrSortField; - if(propertyDef != null) - { - - IndexedField fields = AlfrescoSolrDataModel.getInstance().getQueryableFields(propertyDef.getName(), getTextField(fieldNameAndEnding.getSecond()), fieldUse); - if(fields.getFields().size() > 0) - { - if(fields.getFields().size() > position) - { - solrSortField = fields.getFields().get(position).getField(); - } - else - { - solrSortField = fields.getFields().get(0).getField(); - } - } - else - { - solrSortField = mapNonPropertyFields(luceneField); - } - } - else - { - solrSortField = mapNonPropertyFields(luceneField); - } - return solrSortField; - } - - } \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import static java.util.Collections.unmodifiableList; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.stream.Collectors; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.opencmis.dictionary.CMISAbstractDictionaryService; +import org.alfresco.opencmis.dictionary.CMISDictionaryService; +import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; +import org.alfresco.opencmis.dictionary.QNameFilter; +import org.alfresco.opencmis.search.CMISQueryOptions; +import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; +import org.alfresco.opencmis.search.CMISQueryParser; +import org.alfresco.opencmis.search.CmisFunctionEvaluationContext; +import org.alfresco.repo.dictionary.CompiledModelsCache; +import org.alfresco.repo.dictionary.DictionaryComponent; +import org.alfresco.repo.dictionary.DictionaryDAOImpl; +import org.alfresco.repo.dictionary.Facetable; +import org.alfresco.repo.dictionary.IndexTokenisationMode; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.M2ModelDiff; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.repo.search.impl.QueryParserUtils; +import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; +import org.alfresco.repo.search.impl.parsers.FTSParser; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective; +import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilder; +import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.impl.lucene.QueryBuilderContext; +import org.alfresco.repo.tenant.SingleTServiceImpl; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.AlfrescoClientDataModelServicesFactory.DictionaryKey; +import org.alfresco.solr.client.AlfrescoModel; +import org.alfresco.solr.query.Lucene4QueryBuilderContextSolrImpl; +import org.alfresco.solr.query.Solr4QueryParser; +import org.alfresco.solr.tracker.pool.DefaultTrackerPoolFactory; +import org.alfresco.solr.tracker.pool.TrackerPoolFactory; +import org.alfresco.util.NumericEncoder; +import org.alfresco.util.Pair; +import org.alfresco.util.cache.DefaultAsynchronouslyRefreshedCacheRegistry; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.util.Version; +import org.apache.solr.core.CoreDescriptorDecorator; +import org.apache.solr.core.SolrResourceLoader; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.support.FileSystemXmlApplicationContext; + +import static java.util.Optional.ofNullable; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_HOUR_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MONTH_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_SECOND_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_YEAR_FIELD_SUFFIX; + +/** + * @author Andy + * + */ +public class AlfrescoSolrDataModel implements QueryConstants +{ + public static class TenantDbId + { + public String tenant; + public Long dbId; + + public Map optionalBag = new HashMap<>(); + + public void setProperty(String name, Object value) + { + optionalBag.put(name, value); + } + } + + public enum FieldUse + { + FTS, // Term/Phrase/Range/Fuzzy/Prefix/Proximity + ID, // ExactRange - Comparison, In, Upper, Lower, Wildcard + EXACT, // Exact + FACET, // Field, Range, Query + MULTI_FACET, // Text fields will require cross language support to avoid tokenisation for facets + STATS, // Stats + SORT, // Locale + SUGGESTION, + COMPLETION, + HIGHLIGHT + } + + public enum SpecializedFieldType + { + CONTENT_DOCID, + CONTENT_SIZE, + CONTENT_LOCALE, + CONTENT_MIMETYPE, + CONTENT_ENCODING, + TRANSFORMATION_STATUS, + TRANSFORMATION_TIME, + TRANSFORMATION_EXCEPTION, + UNIT_OF_TIME_SECOND, + UNIT_OF_TIME_MINUTE, + UNIT_OF_TIME_HOUR, + UNIT_OF_TIME_DAY, + UNIT_OF_TIME_DAY_OF_WEEK, + UNIT_OF_TIME_DAY_OF_YEAR, + UNIT_OF_TIME_MONTH, + UNIT_OF_TIME_QUARTER, + UNIT_OF_TIME_YEAR + } + + public static final String CONTENT_S_LOCALE_PREFIX = "content@s__locale@"; + + static final String PART_FIELDNAME_PREFIX = "part@sd@"; + static final Set DATE_PART_SUFFIXES = + Set.of(UNIT_OF_TIME_YEAR_FIELD_SUFFIX, + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX, + UNIT_OF_TIME_MONTH_FIELD_SUFFIX, + UNIT_OF_TIME_DAY_FIELD_SUFFIX, + UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX, + UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX, + UNIT_OF_TIME_HOUR_FIELD_SUFFIX, + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX, + UNIT_OF_TIME_SECOND_FIELD_SUFFIX); + /** + * Infix used for denoting a primitive single valued field with no doc values enabled + */ + private static final String SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER = "@s_@"; + + /** + * Infix used for denoting a primitive single valued field with no doc values enabled + */ + private static final String SINGLE_VALUE_WITH_DOC_VALUES_MARKER = "@sd@"; + + static final String SHARED_PROPERTIES = "shared.properties"; + + protected final static Logger log = LoggerFactory.getLogger(AlfrescoSolrDataModel.class); + + private static final String CHANGE_SET = "CHANGE_SET"; + + private static final String TX = "TX"; + + public static final String DEFAULT_TENANT = "_DEFAULT_"; + + private final static AlfrescoSolrDataModel INSTANCE = new AlfrescoSolrDataModel(); + + private final TenantService tenantService; + + private final NamespaceDAO namespaceDAO; + + private final DictionaryDAOImpl dictionaryDAO; + + private final Map dictionaryServices; + + private final Map cmisDictionaryServices; + + private final Map> modelErrors = new HashMap<>(); + private final Set suggestableProperties = new HashSet<>(); + private final Set crossLocaleSearchDataTypes = new HashSet<>(); + private final Set crossLocaleSearchProperties = new HashSet<>(); + private final Set identifierProperties = new HashSet<>(); + private final ThreadPoolExecutor threadPool; + + public void close() { + threadPool.shutdown(); + } + + public AlfrescoSolrDataModel() + { + tenantService = new SingleTServiceImpl(); + + dictionaryDAO = new DictionaryDAOImpl(); + dictionaryDAO.setTenantService(tenantService); + + try + { + CompiledModelsCache compiledModelsCache = new CompiledModelsCache(); + compiledModelsCache.setDictionaryDAO(dictionaryDAO); + compiledModelsCache.setTenantService(tenantService); + compiledModelsCache.setRegistry(new DefaultAsynchronouslyRefreshedCacheRegistry()); + TrackerPoolFactory trackerPoolFactory = new DefaultTrackerPoolFactory(new Properties(), "_dictionary_", "_internal_"); + threadPool = trackerPoolFactory.create(); + compiledModelsCache.setThreadPoolExecutor(threadPool); + + dictionaryDAO.setDictionaryRegistryCache(compiledModelsCache); + dictionaryDAO.setResourceClassLoader(getResourceClassLoader()); + dictionaryDAO.init(); + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Failed to create dictionaryDAO ", e); + } + + namespaceDAO = dictionaryDAO; + + QNameFilter qnameFilter = getQNameFilter(); + dictionaryServices = AlfrescoClientDataModelServicesFactory.constructDictionaryServices(qnameFilter, dictionaryDAO); + DictionaryComponent dictionaryComponent = getDictionaryService(CMISStrictDictionaryService.DEFAULT); + dictionaryComponent.setMessageLookup(new StaticMessageLookup()); + + cmisDictionaryServices = AlfrescoClientDataModelServicesFactory.constructDictionaries(qnameFilter, namespaceDAO, dictionaryComponent, dictionaryDAO); + + Properties props = getCommonConfig(); + for (Object key : props.keySet()) + { + String stringKey = (String) key; + if (stringKey.startsWith("alfresco.suggestable.property.")) + { + QName qName = QName.createQName(props.getProperty(stringKey)); + suggestableProperties.add(qName); + } + else if (stringKey.startsWith("alfresco.cross.locale.property.")) + { + QName qName = QName.createQName(props.getProperty(stringKey)); + crossLocaleSearchProperties.add(qName); + } + else if (stringKey.startsWith("alfresco.cross.locale.datatype.")) + { + QName qName = QName.createQName(props.getProperty(stringKey)); + crossLocaleSearchDataTypes.add(qName); + } + else if (stringKey.startsWith("alfresco.identifier.property.")) + { + QName qName = QName.createQName(props.getProperty(stringKey)); + identifierProperties.add(qName); + } + } + + if(props.isEmpty()) + { + suggestableProperties.add(ContentModel.PROP_NAME); + suggestableProperties.add(ContentModel.PROP_TITLE); + suggestableProperties.add(ContentModel.PROP_DESCRIPTION); + suggestableProperties.add(ContentModel.PROP_CONTENT); + + crossLocaleSearchDataTypes.add(DataTypeDefinition.TEXT); + crossLocaleSearchDataTypes.add(DataTypeDefinition.CONTENT); + crossLocaleSearchDataTypes.add(DataTypeDefinition.MLTEXT); + + identifierProperties.add(ContentModel.PROP_CREATOR); + identifierProperties.add(ContentModel.PROP_MODIFIER); + identifierProperties.add(ContentModel.PROP_USERNAME); + identifierProperties.add(ContentModel.PROP_AUTHORITY_NAME); + } + } + + public static String getTenantId(String tenant) + { + if((tenant == null) || tenant.equals(TenantService.DEFAULT_DOMAIN)) + { + return DEFAULT_TENANT; + } + else + { + return tenant.replaceAll("!", "_-._"); + } + } + + /** + * Returns the ACL Document identifier in the following format <TENANT>!<ACLID>!ACL + * + * @param tenant the tenant code. + * @param aclId the acl identifer + * @return the ACL Document identifier (<TENANT>!<ACLID>!ACL) + */ + public static String getAclDocumentId(String tenant, Long aclId) + { + return getTenantId(tenant) + "!" + NumericEncoder.encode(aclId) + "!ACL"; + } + + /** + * Returns the ACL ChangeSet Document identifier in the following format TRACKER!CHANGESET!<ACL_CHANGESET_ID> + * + * @param aclChangeSetId the ACL changeset identifier. + * @return the ACL ChangeSet Document identifier (TRACKER!CHANGESET!<ACL_CHANGESET_ID>) + */ + public static String getAclChangeSetDocumentId(Long aclChangeSetId) + { + return "TRACKER" + "!" + CHANGE_SET + "!" + NumericEncoder.encode(aclChangeSetId); + } + + + /** + * Extracts the transaction identifier from the input document identifier. + * The "input document identifier" is the alphanumeric identifier used for Solr documents. + * + * @param documentId the document identifier. + * @return the numeric ACL changeset identifier. + */ + public static Long parseTransactionId(String documentId) + { + return parseIdFromDocumentId(documentId); + } + + private static Long parseIdFromDocumentId(String documentId) + { + String[] split = documentId.split("!"); + if (split.length > 0) + return NumericEncoder.decodeLong(split[split.length - 1]); + else + return null; + } + + /** + * Returns the Solr document identifier in the following format <TENANT>!<!<DBID> + * + * @param tenant the tenant code. + * @param dbid the DB identifier. + * @return the Solr document identifier in the following format <TENANT>!<!<DBID> + */ + public static String getNodeDocumentId(String tenant, Long dbid) + { + return getTenantId(tenant) + "!" + NumericEncoder.encode(dbid); + } + + /** + * Destructures a document identifier in the three compounding parts (tenant and dbid). + * + * @param id the document identifier. + * @return a {@link TenantDbId} instance containing the destructured identifier. + */ + public static TenantDbId decodeNodeDocumentId(String id) + { + TenantDbId ids = new TenantDbId(); + String[] split = id.split("!"); + if (split.length > 0) + ids.tenant = split[0]; + if (split.length > 1) + ids.dbId = NumericEncoder.decodeLong(split[1]); + return ids; + } + + /** + * Returns the Transaction Document identifier in the following format TRACKER!TX!<TX_ID> + * + * @param txId the transaction identifier. + * @return the Transaction Document identifier in the following format TRACKER!TX!<TX_ID> + */ + public static String getTransactionDocumentId(Long txId) + { + // TODO - check and encode for ":" + return "TRACKER" + + "!" + + TX + + "!" + + NumericEncoder.encode(txId); + } + + public String getAlfrescoPropertyFromSchemaField(String schemaField) + { + int index = schemaField.lastIndexOf("@{"); + if(index == -1) + { + return schemaField; + } + + String alfrescoQueryField = schemaField.substring(index+1); + QName qName = QName.createQName(alfrescoQueryField); + alfrescoQueryField = qName.toPrefixString(namespaceDAO); + + PropertyDefinition propertyDefinition = getPropertyDefinition(qName); + if((propertyDefinition == null)) + { + return alfrescoQueryField; + } + if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) + { + return alfrescoQueryField; + } + + DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); + if(dataTypeDefinition.getName().equals(DataTypeDefinition.CONTENT)) + { + if(schemaField.contains("__size@")) + { + return alfrescoQueryField + ".size"; + } + else if(schemaField.contains("__locale@")) + { + return alfrescoQueryField + ".locale"; + } + else if(schemaField.contains("__mimetype@")) + { + return alfrescoQueryField + ".mimetype"; + } + else if(schemaField.contains("__encoding@")) + { + return alfrescoQueryField + ".encoding"; + } + else if(schemaField.contains("__docid@")) + { + return alfrescoQueryField + ".docid"; + } + else if(schemaField.contains("__tr_ex@")) + { + return alfrescoQueryField + ".tr_ex"; + } + else if(schemaField.contains("__tr_time@")) + { + return alfrescoQueryField + ".tr_time"; + } + else if(schemaField.contains("__tr_status@")) + { + return alfrescoQueryField + ".tr_status"; + } + else + { + return alfrescoQueryField; + } + } + else + { + return alfrescoQueryField; + } + } + + public static AlfrescoSolrDataModel getInstance() + { + return INSTANCE; + } + + public NamespaceDAO getNamespaceDAO() + { + return namespaceDAO; + } + + /** + * TODO: Fix to load type filter/exclusions from somewhere sensible + * @return QNameFilter + */ + private QNameFilter getQNameFilter() + { + QNameFilter qnameFilter = null; + FileSystemXmlApplicationContext ctx = null; + + File resourceDirectory = getResourceDirectory(); + // If we ever need to filter out models in the future, then we must put a filter somewhere. + // Currently, we do not need to filter any models, so this filter does not exist. + File filterContext = new File(resourceDirectory, "opencmis-qnamefilter-context.xml"); + + if(!filterContext.exists()) + { + log.info("No type filter context found at " + filterContext.getAbsolutePath() + ", no type filtering"); + return qnameFilter; + } + + try + { + ctx = new FileSystemXmlApplicationContext(new String[] { "file:" + filterContext.getAbsolutePath() }, false); + ctx.setClassLoader(this.getClass().getClassLoader()); + ctx.refresh(); + qnameFilter = (QNameFilter)ctx.getBean("cmisTypeExclusions"); + if(qnameFilter == null) + { + log.warn("Unable to find type filter at " + filterContext.getAbsolutePath() + ", no type filtering"); + } + } + catch(BeansException e) + { + log.warn("Unable to parse type filter at " + filterContext.getAbsolutePath() + ", no type filtering"); + } + finally + { + if(ctx != null && ctx.getBeanFactory() != null && ctx.isActive()) + { + ctx.close(); + } + } + + return qnameFilter; + } + + public static Properties getCommonConfig() + { + File resourceDirectory = getResourceDirectory(); + // If we ever need to filter out models in the future, then we must put a filter somewhere. + // Currently, we do not need to filter any models, so this filter does not exist. + File propertiesFile = new File(resourceDirectory, SHARED_PROPERTIES); + + Properties props = new Properties(); + if(!propertiesFile.exists()) + { + log.info("No shared properties found at " + propertiesFile.getAbsolutePath()); + return props; + } + + try(InputStream is = new FileInputStream(propertiesFile)) + { + props.load(is); + } + catch (IOException e) + { + log.info("Failed to read shared properties at " + propertiesFile.getAbsolutePath()); + } + + return props; + } + + /** + * @return ClassLoader + */ + public ClassLoader getResourceClassLoader() + { + File f = getResourceDirectory(); + if (f.canRead() && f.isDirectory()) + { + URL[] urls = new URL[1]; + + try + { + URL url = f.toURI().normalize().toURL(); + urls[0] = url; + } + catch (MalformedURLException e) + { + throw new AlfrescoRuntimeException("Failed to add resources to classpath ", e); + } + + return URLClassLoader.newInstance(urls, this.getClass().getClassLoader()); + } + else + { + return this.getClass().getClassLoader(); + } + } + + public static File getResourceDirectory() + { + return new File(SolrResourceLoader.locateSolrHome().toFile(), "conf"); + } + + /** + * Gets a DictionaryService, if an Alternative dictionary is specified it tries to get that. + * It will attempt to get the DEFAULT dictionary service if null is specified or it can't find + * a dictionary with the name of "alternativeDictionary" + * @param alternativeDictionary - can be null; + * @return DictionaryService + */ + public DictionaryComponent getDictionaryService(String alternativeDictionary) + { + DictionaryComponent dictionaryComponent = null; + + if (alternativeDictionary != null && !alternativeDictionary.trim().isEmpty()) + { + dictionaryComponent = dictionaryServices.get(alternativeDictionary); + } + + if (dictionaryComponent == null) + { + dictionaryComponent = dictionaryServices.get(CMISStrictDictionaryService.DEFAULT); + } + return dictionaryComponent; + } + + public IndexedField getIndexedFieldForSpecializedPropertyMetadata(QName propertyQName, SpecializedFieldType type) + { + IndexedField indexedField = new IndexedField(); + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + if((propertyDefinition == null)) + { + return indexedField; + } + if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) + { + return indexedField; + } + + DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); + if(dataTypeDefinition.getName().equals(DataTypeDefinition.CONTENT)) + { + StringBuilder builder = new StringBuilder(); + builder.append(dataTypeDefinition.getName().getLocalName()); + builder.append('@'); + // TODO wher we support multi value propertis correctly .... builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); + builder.append('s'); + builder.append("_"); + builder.append('_'); + switch (type) + { + case CONTENT_DOCID: + builder.append("docid"); + break; + case CONTENT_ENCODING: + builder.append("encoding"); + break; + case CONTENT_LOCALE: + builder.append("locale"); + break; + case CONTENT_MIMETYPE: + builder.append("mimetype"); + break; + case CONTENT_SIZE: + builder.append("size"); + break; + case TRANSFORMATION_EXCEPTION: + builder.append("tr_ex"); + break; + case TRANSFORMATION_STATUS: + builder.append("tr_status"); + break; + case TRANSFORMATION_TIME: + builder.append("tr_time"); + break; + default: + break; + } + + builder.append('@'); + builder.append(propertyQName); + indexedField.addField(builder.toString(), false, false); + } + else if (isDateOrDatetime(dataTypeDefinition)) + { + String dateDerivedSuffix = getDateDerivedSuffix(type); + if (dateDerivedSuffix != null) + { + indexedField.addField(getDateDerivedField(propertyQName, dateDerivedSuffix), false, true); + } + } + return indexedField; + + } + + + public IndexedField getQueryableFields(QName propertyQName, SpecializedFieldType type, FieldUse fieldUse) + { + if(type != null) + { + return getIndexedFieldForSpecializedPropertyMetadata(propertyQName, type); + } + + IndexedField indexedField = new IndexedField(); + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + if((propertyDefinition == null)) + { + indexedField.addField("_dummy_", false, false); + return indexedField; + } + if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) + { + indexedField.addField("_dummy_", false, false); + return indexedField; + } + + if(isTextField(propertyDefinition)) + { + switch(fieldUse) + { + case COMPLETION: + addCompletionFields(propertyDefinition, indexedField); + break; + case FACET: + addFacetSearchFields(propertyDefinition, indexedField); + break; + case FTS: + addFullTextSearchFields(propertyDefinition, indexedField); + break; + case ID: + addIdentifierSearchFields(propertyDefinition, indexedField); + break; + case EXACT: + addExactSearchFields(propertyDefinition, indexedField); + break; + case MULTI_FACET: + addMultiSearchFields(propertyDefinition, indexedField); + break; + case SORT: + addSortSearchFields(propertyDefinition, indexedField); + break; + case STATS: + addStatsSearchFields(propertyDefinition, indexedField); + break; + case SUGGESTION: + if(isSuggestable(propertyQName)) + { + indexedField.addField("suggest", false, false); + } + addCompletionFields(propertyDefinition, indexedField); + break; + case HIGHLIGHT: + addHighlightSearchFields(propertyDefinition, indexedField); + break; + } + } + else + { + indexedField.addField(getFieldForNonText(propertyDefinition), false, false); + } + return indexedField; + } + + /* + * Adds best completion fields in order of preference + */ + private void addCompletionFields(PropertyDefinition propertyDefinition, IndexedField indexedField) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + else if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE)) + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + else if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE)) + { + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + } + + /* + * Adds best fts fields in order of preference + */ + + private void addFullTextSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + if (((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + && !isIdentifierTextProperty(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + } + else + { + indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + } + + private void addHighlightSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + QName propertyName = propertyDefinition.getName(); + QName propertyDataTypeQName = propertyDefinition.getDataType().getName(); + String fieldName; + + if(propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) + { + fieldName = getStoredMLTextField(propertyName); + } + else if(propertyDataTypeQName.equals(DataTypeDefinition.CONTENT)) + { + fieldName = getStoredContentField(propertyName); + } + else + { + fieldName = getStoredTextField(propertyName); + } + + indexedField.addField(fieldName, false, false); + } + + /* + * If the untokenised version is available, go for it. + * If not it takes the tokenised versions available. + */ + private void addIdentifierSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + + indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + } + } + + /* + * If only the untokenised version is available, go for it. + * If not it takes the tokenised cross locale version. + */ + private void addExactSearchFields(PropertyDefinition propertyDefinition, IndexedField indexedField) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || !(propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + + indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } else{ + throw new UnsupportedOperationException("Exact Term search is not supported unless you configure the field <"+propertyDefinition.getName()+"> for cross locale search"); + } + } + } + + /* + * Adds best identifier fields in order of preference + */ + private void addFacetSearchFields(PropertyDefinition propertyDefinition, IndexedField indexedField) + { + if(propertyDefinition.getDataType().getName().equals(DataTypeDefinition.TEXT)) + { + if (!isIdentifierTextProperty(propertyDefinition.getName())) + { + if(propertyDefinition.getFacetable() == Facetable.TRUE) + { + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + } + } + + + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH) + || isIdentifierTextProperty(propertyDefinition.getName())) + { + + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + } + + private void addMultiSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH) + || isIdentifierTextProperty(propertyDefinition.getName())) + { + + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + } + + private void addStatsSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + addFacetSearchFields(propertyDefinition, indexedField); + } + + private void addSortSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + // Can only order on single valued fields + DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); + if(dataTypeDefinition.getName().equals(DataTypeDefinition.TEXT)) + { + if(!propertyDefinition.isMultiValued()) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); + } + else if (isIdentifierTextProperty(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + } + } + + if(dataTypeDefinition.getName().equals(DataTypeDefinition.MLTEXT)) + { + if(!propertyDefinition.isMultiValued()) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + } + } + } + + + public String getStoredTextField(QName propertyQName) + { + return getStoredTextField(propertyQName, null); + } + + public String getStoredTextField(QName propertyQName, String suffix) + { + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + + StringBuilder sb = new StringBuilder(); + sb.append("text@" + (propertyDefinition.isMultiValued()? "m" : "s") + "_stored_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || + isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); + + sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || + crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || + isIdentifierTextProperty(propertyDefinition.getName())) && !propertyDefinition.isMultiValued()? "s" : "_"); + + sb.append(isSuggestable(propertyQName)? "s": "_"); + + sb.append("@"); + sb.append(propertyDefinition.getName().toString()); + + if (suffix != null) + { + sb.append(suffix); + } + + return sb.toString(); + + } + + public String getStoredMLTextField(QName propertyQName) + { + return getStoredMLTextField(propertyQName, null); + } + + public String getStoredMLTextField(QName propertyQName, String suffix) + { + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + + StringBuilder sb = new StringBuilder(); + sb.append("mltext@m_stored_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || + isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); + + sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || + crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); + + sb.append("_"); + + sb.append(isSuggestable(propertyQName)? "s": "_"); + + sb.append("@"); + sb.append(propertyDefinition.getName().toString()); + + if (suffix != null) + { + sb.append(suffix); + } + + return sb.toString(); + + } + + public String getStoredContentField(QName propertyQName) + { + return getStoredContentField(propertyQName, null); + } + + public String getStoredContentField(QName propertyQName, String suffix) + { + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + + StringBuilder sb = new StringBuilder(); + sb.append("content@s_stored_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || + isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); + + sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || + crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); + + + sb.append("_"); + sb.append(isSuggestable(propertyQName)? "s": "_"); + + sb.append("@"); + sb.append(propertyDefinition.getName().toString()); + + if (suffix != null) + { + sb.append(suffix); + } + + return sb.toString(); + + } + + public String getDateDerivedField(QName propertyQName, String suffix) + { + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + return "part@sd@" + propertyDefinition.getName().toString() + suffix; + } + + public String getDateDerivedSuffix(SpecializedFieldType type) + { + switch (type) + { + case UNIT_OF_TIME_SECOND: + return UNIT_OF_TIME_SECOND_FIELD_SUFFIX; + case UNIT_OF_TIME_MINUTE: + return UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; + case UNIT_OF_TIME_HOUR: + return UNIT_OF_TIME_HOUR_FIELD_SUFFIX; + case UNIT_OF_TIME_DAY: + return UNIT_OF_TIME_DAY_FIELD_SUFFIX; + case UNIT_OF_TIME_DAY_OF_WEEK: + return UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX; + case UNIT_OF_TIME_DAY_OF_YEAR: + return UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX; + case UNIT_OF_TIME_MONTH: + return UNIT_OF_TIME_MONTH_FIELD_SUFFIX; + case UNIT_OF_TIME_QUARTER: + return UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; + case UNIT_OF_TIME_YEAR: + return UNIT_OF_TIME_YEAR_FIELD_SUFFIX; + default: + return null; + } + } + + + /** + * Get all the field names into which we must copy the source data + * + * @param propertyQName QName + * @return IndexedField + */ + public IndexedField getIndexedFieldNamesForProperty(QName propertyQName) + { + // TODO: Cache and throw on model refresh + + IndexedField indexedField = new IndexedField(); + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + if((propertyDefinition == null)) + { + return indexedField; + } + if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) + { + return indexedField; + } + + DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); + if(isTextField(propertyDefinition)) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + } + + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH + || isIdentifierTextProperty(propertyDefinition.getName()))) + { + indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + + if(dataTypeDefinition.getName().equals(DataTypeDefinition.TEXT)) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + if(!propertyDefinition.isMultiValued()) + { + indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); + } + } + else if (!isIdentifierTextProperty(propertyDefinition.getName())) + { + if(propertyDefinition.getFacetable() == Facetable.TRUE) + { + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + } + } + + if(dataTypeDefinition.getName().equals(DataTypeDefinition.MLTEXT)) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + if(!propertyDefinition.isMultiValued()) + { + indexedField.addField(getFieldForText(true, false, true, propertyDefinition), true, true); + } + } + } + + if(isSuggestable(propertyQName)) + { + indexedField.addField("suggest_@" + propertyDefinition.getName().toString(), false, false); + } + } + else + { + indexedField.addField(getFieldForNonText(propertyDefinition), false, false); + } + + return indexedField; + } + + private boolean isIdentifierTextProperty(QName propertyQName) + { + return identifierProperties.contains(propertyQName); + } + + public boolean isTextField(PropertyDefinition propertyDefinition) + { + return ofNullable(propertyDefinition) + .map(PropertyDefinition::getDataType) + .map(DataTypeDefinition::getName) + .map(name -> + name.equals(DataTypeDefinition.MLTEXT) + || + name.equals(DataTypeDefinition.CONTENT) + || + name.equals(DataTypeDefinition.TEXT)) + .orElse(false); + } + + private boolean isSuggestable(QName propertyQName) + { + if(propertyQName == null) + { + return false; + } + return suggestableProperties.contains(propertyQName); + } + + private boolean hasDocValues(PropertyDefinition propertyDefinition) + { + + if(isTextField(propertyDefinition)) + { + // We only call this if text is untokenised and localised + return propertyDefinition.getFacetable() != Facetable.FALSE; + } + else + { + if(propertyDefinition.getFacetable() == Facetable.FALSE) + { + return false; + } + else if(propertyDefinition.getFacetable() == Facetable.TRUE) + { + return true; + } + else + { + if(propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) + { + return false; + } + else + { + return isPrimitive(propertyDefinition.getDataType()); + } + } + } + } + + public String getFieldForNonText(PropertyDefinition propertyDefinition) + { + StringBuilder builder = new StringBuilder(); + QName qName = propertyDefinition.getDataType().getName(); + builder.append(qName.getLocalName()); + builder.append("@"); + builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); + builder.append(hasDocValues(propertyDefinition) ? "d" : "_"); + builder.append("@"); + builder.append(propertyDefinition.getName().toString()); + return builder.toString(); + } + + private String getFieldForText(boolean localised, boolean tokenised, boolean sort, PropertyDefinition propertyDefinition) + { + StringBuilder builder = new StringBuilder(); + QName qName = propertyDefinition.getDataType().getName(); + builder.append(qName.getLocalName()); + builder.append("@"); + QName propertyDataTypeQName = propertyDefinition.getDataType().getName(); + if(propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) + { + builder.append('m'); + } + else if(propertyDataTypeQName.equals(DataTypeDefinition.CONTENT)) + { + builder.append('s'); + } + else + { + builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); + } + if(sort || localised || tokenised || propertyDataTypeQName.equals(DataTypeDefinition.CONTENT) || propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) + { + builder.append('_'); + } + else + { + builder.append(hasDocValues(propertyDefinition) ? "d" : "_"); + } + builder.append('_'); + if(!sort) + { + builder.append(localised ? "l" : "_"); + builder.append(tokenised ? "t" : "_"); + } + else + { + builder.append("sort"); + } + builder.append("@"); + builder.append(propertyDefinition.getName().toString()); + return builder.toString(); + } + + public PropertyDefinition getPropertyDefinition(QName propertyQName) + { + return getDictionaryService(CMISStrictDictionaryService.DEFAULT).getProperty(propertyQName); + } + + public boolean putModel(M2Model model) + { + modelErrors.remove(model.getName()); + dictionaryDAO.putModelIgnoringConstraints(model); + return true; + } + + public void removeModel(QName modelQName) + { + modelErrors.remove(getM2Model(modelQName).getName()); + dictionaryDAO.removeModel(modelQName); + } + + M2Model getM2Model(QName modelQName) + { + return dictionaryDAO.getCompiledModel(modelQName).getM2Model(); + } + + public void afterInitModels() + { + for (CMISAbstractDictionaryService cds : cmisDictionaryServices.values()) + { + cds.afterDictionaryInit(); + } + } + + public org.alfresco.repo.search.impl.querymodel.Query parseCMISQueryToAlfrescoAbstractQuery(CMISQueryMode mode, SearchParameters searchParameters, + SolrQueryRequest req, String alternativeDictionary, CmisVersion cmisVersion) + { + // convert search parameters to cmis query options + // TODO: how to handle store ref + CMISQueryOptions options = new CMISQueryOptions(searchParameters.getQuery(), StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + options.setQueryMode(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS); + options.setDefaultFieldName(searchParameters.getDefaultFieldName()); + // TODO: options.setDefaultFTSConnective() + // TODO: options.setDefaultFTSFieldConnective() + options.setIncludeInTransactionData(!searchParameters.excludeDataInTheCurrentTransaction()); + options.setLocales(searchParameters.getLocales()); + options.setMlAnalaysisMode(searchParameters.getMlAnalaysisMode()); + options.setQueryParameterDefinitions(searchParameters.getQueryParameterDefinitions()); + for(String name : searchParameters.getQueryTemplates().keySet()) + { + String template = searchParameters.getQueryTemplates().get(name); + options.addQueryTemplate(name, template); + } + + // parse cmis syntax + CapabilityJoin joinSupport = (mode == CMISQueryMode.CMS_STRICT) ? CapabilityJoin.NONE : CapabilityJoin.INNERANDOUTER; + CmisFunctionEvaluationContext functionContext = getCMISFunctionEvaluationContext(mode, cmisVersion, alternativeDictionary); + + CMISDictionaryService cmisDictionary = getCMISDictionary(alternativeDictionary, cmisVersion); + + CMISQueryParser parser = new CMISQueryParser(options, cmisDictionary, joinSupport); + org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = parser.parse(new LuceneQueryModelFactory(), functionContext); + + if (queryModelQuery.getSource() != null) + { + List> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext); + if (selectorGroups.size() == 0) + { + throw new UnsupportedOperationException("No selectors"); + } + } + return queryModelQuery; + } + + public CmisFunctionEvaluationContext getCMISFunctionEvaluationContext(CMISQueryMode mode, CmisVersion cmisVersion, String alternativeDictionary) + { + BaseTypeId[] validScopes = (mode == CMISQueryMode.CMS_STRICT) ? CmisFunctionEvaluationContext.STRICT_SCOPES : CmisFunctionEvaluationContext.ALFRESCO_SCOPES; + CmisFunctionEvaluationContext functionContext = new CmisFunctionEvaluationContext(); + functionContext.setCmisDictionaryService(getCMISDictionary(alternativeDictionary, cmisVersion)); + functionContext.setValidScopes(validScopes); + return functionContext; + } + + /** + * Gets the CMISDictionaryService, if an Alternative dictionary is specified it tries to get that. + * It will attempt to get the DEFAULT dictionary service if null is specified or it can't find + * a dictionary with the name of "alternativeDictionary" + * + * @param alternativeDictionary - can be null; + * @return CMISDictionaryService + */ + public CMISDictionaryService getCMISDictionary(String alternativeDictionary, CmisVersion cmisVersion) + { + CMISDictionaryService cmisDictionary = null; + + if (alternativeDictionary != null && !alternativeDictionary.trim().isEmpty()) + { + DictionaryKey key = new DictionaryKey(cmisVersion, alternativeDictionary); + cmisDictionary = cmisDictionaryServices.get(key); + } + + if (cmisDictionary == null) + { + DictionaryKey key = new DictionaryKey(cmisVersion, CMISStrictDictionaryService.DEFAULT); + cmisDictionary = cmisDictionaryServices.get(key); + } + return cmisDictionary; + } + + /** + * Returns the Alfresco models associated with the current dictionary. + * + * @return the Alfresco models associated with the current dictionary. + */ + public List getAlfrescoModels() + { + return dictionaryDAO.getModels().stream() + .map(qname -> { + M2Model m2Model = dictionaryDAO.getCompiledModel(qname).getM2Model(); + return new AlfrescoModel( + m2Model, + getDictionaryService( + CMISStrictDictionaryService.DEFAULT).getModel(qname).getChecksum(ModelDefinition.XMLBindingType.DEFAULT));}) + .collect(Collectors.toList()); + } + + Map> getModelErrors() + { + return modelErrors; + } + + public static class IndexedField + { + private final List fields = new LinkedList<>(); + + public List getFields() + { + return unmodifiableList(fields); + } + + public void addField(String prefix, boolean localised, boolean sort) + { + fields.add(new FieldInstance(prefix, localised, sort)); + } + } + + public static class FieldInstance + { + final String field; + final boolean localised; + final boolean sort; + + public FieldInstance(String field, boolean localised, boolean sort) + { + this.field = field; + this.localised = localised; + this.sort = sort; + } + + public String getField() + { + return field; + } + + public boolean isLocalised() + { + return localised; + } + + public boolean isSort() + { + return sort; + } + } + + public Query getCMISQuery(CMISQueryMode mode, Pair searchParametersAndFilter, SolrQueryRequest req, org.alfresco.repo.search.impl.querymodel.Query queryModelQuery, CmisVersion cmisVersion, String alternativeDictionary) throws ParseException + { + SearchParameters searchParameters = searchParametersAndFilter.getFirst(); + Boolean isFilter = searchParametersAndFilter.getSecond(); + + CmisFunctionEvaluationContext functionContext = getCMISFunctionEvaluationContext(mode, cmisVersion, alternativeDictionary); + + Set selectorGroup = queryModelQuery.getSource().getSelectorGroups(functionContext).get(0); + + QueryBuilderContext luceneContext = getLuceneQueryBuilderContext(searchParameters, req, alternativeDictionary, FTSQueryParser.RerankPhase.SINGLE_PASS); + @SuppressWarnings("unchecked") + LuceneQueryBuilder builder = (LuceneQueryBuilder) queryModelQuery; + org.apache.lucene.search.Query luceneQuery = builder.buildQuery(selectorGroup, luceneContext, functionContext); + + return new ContextAwareQuery(luceneQuery, Boolean.TRUE.equals(isFilter) ? null : searchParameters); + } + + public QueryBuilderContext getLuceneQueryBuilderContext(SearchParameters searchParameters, SolrQueryRequest req, String alternativeDictionary, FTSQueryParser.RerankPhase rerankPhase) + { + return new Lucene4QueryBuilderContextSolrImpl( + getDictionaryService(alternativeDictionary), + namespaceDAO, + tenantService, + searchParameters, + MLAnalysisMode.EXACT_LANGUAGE, + req, + this, + rerankPhase); + } + + public Solr4QueryParser getLuceneQueryParser(SearchParameters searchParameters, SolrQueryRequest req, FTSQueryParser.RerankPhase rerankPhase) + { + Analyzer analyzer = req.getSchema().getQueryAnalyzer(); + Solr4QueryParser parser = new Solr4QueryParser(req, Version.LATEST, searchParameters.getDefaultFieldName(), analyzer, rerankPhase); + parser.setNamespacePrefixResolver(namespaceDAO); + parser.setDictionaryService(getDictionaryService(CMISStrictDictionaryService.DEFAULT)); + parser.setTenantService(tenantService); + parser.setSearchParameters(searchParameters); + parser.setAllowLeadingWildcard(true); + + Properties props = new CoreDescriptorDecorator(req.getCore().getCoreDescriptor()).getProperties(); + int topTermSpanRewriteLimit = Integer.parseInt(props.getProperty("alfresco.topTermSpanRewriteLimit", "1000")); + parser.setTopTermSpanRewriteLimit(topTermSpanRewriteLimit); + + return parser; + } + + public Query getFTSQuery(Pair searchParametersAndFilter, SolrQueryRequest req, FTSQueryParser.RerankPhase rerankPhase) throws ParseException + { + + SearchParameters searchParameters = searchParametersAndFilter.getFirst(); + Boolean isFilter = searchParametersAndFilter.getSecond(); + + QueryModelFactory factory = new LuceneQueryModelFactory(); + AlfrescoFunctionEvaluationContext functionContext = new AlfrescoSolr4FunctionEvaluationContext(namespaceDAO, getDictionaryService(CMISStrictDictionaryService.DEFAULT), NamespaceService.CONTENT_MODEL_1_0_URI, req.getSchema()); + + FTSParser.Mode mode; + + if (searchParameters.getDefaultFTSOperator() == org.alfresco.service.cmr.search.SearchParameters.Operator.AND) + { + mode = FTSParser.Mode.DEFAULT_CONJUNCTION; + } + else + { + mode = FTSParser.Mode.DEFAULT_DISJUNCTION; + } + + Constraint constraint = FTSQueryParser.buildFTS(searchParameters.getQuery(), factory, functionContext, null, null, mode, + searchParameters.getDefaultFTSOperator() == org.alfresco.service.cmr.search.SearchParameters.Operator.OR ? Connective.OR : Connective.AND, + searchParameters.getQueryTemplates(), searchParameters.getDefaultFieldName(), rerankPhase); + org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = factory.createQuery(null, null, constraint, new ArrayList<>()); + + @SuppressWarnings("unchecked") + LuceneQueryBuilder builder = (LuceneQueryBuilder) queryModelQuery; + + QueryBuilderContext luceneContext = getLuceneQueryBuilderContext(searchParameters, req, CMISStrictDictionaryService.DEFAULT, rerankPhase); + + Set selectorGroup = null; + if (queryModelQuery.getSource() != null) + { + List> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext); + + if (selectorGroups.size() == 0) + { + throw new UnsupportedOperationException("No selectors"); + } + + if (selectorGroups.size() > 1) + { + throw new UnsupportedOperationException("Advanced join is not supported"); + } + + selectorGroup = selectorGroups.get(0); + } + Query luceneQuery = builder.buildQuery(selectorGroup, luceneContext, functionContext); + // query needs some search parameters fro correct caching .... + + return new ContextAwareQuery(luceneQuery, Boolean.TRUE.equals(isFilter) ? null : searchParameters); + } + + private PropertyDefinition getPropertyDefinition(String identifier) + { + return QueryParserUtils.matchPropertyDefinition(NamespaceService.CONTENT_MODEL_1_0_URI, + getNamespaceDAO(), + getDictionaryService(CMISStrictDictionaryService.DEFAULT), + identifier); + } + + public String mapProperty(String potentialProperty, FieldUse fieldUse, SolrQueryRequest req) + { + return mapProperty(potentialProperty, fieldUse, req, 0); + } + + /** + * + * return the stored field associated to potentialProperty parameter + */ + public String mapStoredProperty(String potentialProperty, SolrQueryRequest req) + { + if(potentialProperty.equals("asc") || potentialProperty.equals("desc") || potentialProperty.equals("_docid_")) + { + return potentialProperty; + } + + if(potentialProperty.equalsIgnoreCase("score") || potentialProperty.equalsIgnoreCase("SEARCH_SCORE")) + { + return "score"; + } + + AlfrescoFunctionEvaluationContext functionContext = + new AlfrescoSolr4FunctionEvaluationContext( + getNamespaceDAO(), + getDictionaryService(CMISStrictDictionaryService.DEFAULT), + NamespaceService.CONTENT_MODEL_1_0_URI, + req.getSchema()); + + + Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(potentialProperty); + String luceneField = functionContext.getLuceneFieldName(fieldNameAndEnding.getFirst()); + + PropertyDefinition propertyDef = getPropertyDefinition(fieldNameAndEnding.getFirst()); + //Retry scan using luceneField. + if(propertyDef == null) + { + if(luceneField.contains("@")) + { + int index = luceneField.lastIndexOf("@"); + propertyDef = getPropertyDefinition(luceneField.substring(index +1)); + } + } + + if (propertyDef == null || propertyDef.getName() == null) + { + return mapNonPropertyFields(luceneField); + } + + if (isDateOrDatetime(propertyDef.getDataType()) && isDerivedDateField(fieldNameAndEnding.getSecond())) + { + return getDateDerivedField(propertyDef.getName(), fieldNameAndEnding.getSecond()); + } + else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) + { + return getStoredTextField(propertyDef.getName(), fieldNameAndEnding.getSecond()); + } + else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) + { + return getStoredMLTextField(propertyDef.getName(), fieldNameAndEnding.getSecond()); + } + else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) + { + return getStoredContentField(propertyDef.getName(), fieldNameAndEnding.getSecond()); + } + else + { + return mapAlfrescoField(FieldUse.FTS, 0, fieldNameAndEnding, luceneField, propertyDef) + + fieldNameAndEnding.getSecond(); + } + } + + public boolean isDerivedDateField(String suffix) + { + return DATE_PART_SUFFIXES.contains(suffix); + } + + public String mapProperty(String potentialProperty, FieldUse fieldUse, SolrQueryRequest req, int position) + { + if(potentialProperty.equals("asc") || potentialProperty.equals("desc") || potentialProperty.equals("_docid_")) + { + return potentialProperty; + } + + if(potentialProperty.equalsIgnoreCase("score") || potentialProperty.equalsIgnoreCase("SEARCH_SCORE")) + { + return "score"; + } + + if(req.getSchema().getFieldOrNull(potentialProperty) != null) + { + return mapNonPropertyFields(potentialProperty); + } + + AlfrescoFunctionEvaluationContext functionContext = + new AlfrescoSolr4FunctionEvaluationContext( + getNamespaceDAO(), + getDictionaryService(CMISStrictDictionaryService.DEFAULT), + NamespaceService.CONTENT_MODEL_1_0_URI, + req.getSchema()); + + Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(potentialProperty); + String luceneField = functionContext.getLuceneFieldName(fieldNameAndEnding.getFirst()); + + PropertyDefinition propertyDef = getPropertyDefinition(fieldNameAndEnding.getFirst()); + //Retry scan using luceneField. + if(propertyDef == null) + { + if(luceneField.contains("@")) + { + int index = luceneField.lastIndexOf("@"); + propertyDef = getPropertyDefinition(luceneField.substring(index +1)); + } + } + String solrSortField; + solrSortField = mapAlfrescoField(fieldUse, position, fieldNameAndEnding, luceneField, propertyDef); + return solrSortField; + } + + public String mapNonPropertyFields(String queryField) + { + switch(queryField) + { + case "ID": + return "LID"; + case "EXACTTYPE": + return "TYPE"; + default: + return queryField; + + } + } + + /** + * @param ending String + * @return SpecializedFieldType + */ + public SpecializedFieldType getTextField(String ending) + { + switch(ending) + { + case FIELD_MIMETYPE_SUFFIX: + return SpecializedFieldType.CONTENT_MIMETYPE; + case FIELD_SIZE_SUFFIX: + return SpecializedFieldType.CONTENT_SIZE; + case FIELD_LOCALE_SUFFIX: + return SpecializedFieldType.CONTENT_LOCALE; + case FIELD_ENCODING_SUFFIX: + return SpecializedFieldType.CONTENT_ENCODING; + case UNIT_OF_TIME_SECOND_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_SECOND; + case UNIT_OF_TIME_MINUTE_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_MINUTE; + case UNIT_OF_TIME_HOUR_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_HOUR; + case UNIT_OF_TIME_DAY_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_DAY; + case UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_DAY_OF_WEEK; + case UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_DAY_OF_YEAR; + case UNIT_OF_TIME_MONTH_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_MONTH; + case UNIT_OF_TIME_QUARTER_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_QUARTER; + case UNIT_OF_TIME_YEAR_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_YEAR; + case FIELD_TRANSFORMATION_STATUS_SUFFIX: + case FIELD_TRANSFORMATION_TIME_SUFFIX: + case FIELD_TRANSFORMATION_EXCEPTION_SUFFIX: + default: + return null; + } + } + + public void setCMDefaultUri() + { + if(getNamespaceDAO().getURIs().contains(NamespaceService.CONTENT_MODEL_1_0_URI)) + { + getNamespaceDAO().addPrefix("", NamespaceService.CONTENT_MODEL_1_0_URI); + } + } + + /** + * Returns the prefix used for denoting a field which is meant to represent a constituent part of a + * date or datetime (e.g. year, month, second, minute). + * + * @param sourceFieldName the date/datetime source field name. + * @return the prefix that can be used for denoting a date or datetime part + */ + public String destructuredDateTimePartFieldNamePrefix(String sourceFieldName) { + // source field name example: datetime@sd@{http://www.alfresco.org/model/content/1.0}created + // prefix: datetime + String prefix = sourceFieldName.substring(0, sourceFieldName.indexOf("@")); + + // prefix, docValues disabled option: datetime@s_@ + String sourceFieldNamePrefixWithoutDocValues = prefix + SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER; + + // prefix, docValues enabled option: datetime@sd@ + String sourceFieldNamePrefixWithDocValues = prefix + SINGLE_VALUE_WITH_DOC_VALUES_MARKER; + + return sourceFieldName.replace(sourceFieldNamePrefixWithoutDocValues, PART_FIELDNAME_PREFIX) + .replace(sourceFieldNamePrefixWithDocValues, PART_FIELDNAME_PREFIX); + } + + private boolean isDateOrDatetime(DataTypeDefinition dataType) + { + return dataType.getName().equals(DataTypeDefinition.DATE) || + dataType.getName().equals(DataTypeDefinition.DATETIME); + } + + private boolean isPrimitive(DataTypeDefinition dataType) + { + QName name = dataType.getName(); + return name.equals(DataTypeDefinition.INT) + || name.equals(DataTypeDefinition.LONG) + || name.equals(DataTypeDefinition.FLOAT) + || name.equals(DataTypeDefinition.DOUBLE) + || isDateOrDatetime(dataType) + || name.equals(DataTypeDefinition.BOOLEAN) + || name.equals(DataTypeDefinition.CATEGORY) + || name.equals(DataTypeDefinition.NODE_REF); + } + + private String mapAlfrescoField(FieldUse fieldUse, int position, Pair fieldNameAndEnding, String luceneField, PropertyDefinition propertyDef) + { + String solrSortField; + if(propertyDef != null) + { + + IndexedField fields = AlfrescoSolrDataModel.getInstance().getQueryableFields(propertyDef.getName(), getTextField(fieldNameAndEnding.getSecond()), fieldUse); + if(fields.getFields().size() > 0) + { + if(fields.getFields().size() > position) + { + solrSortField = fields.getFields().get(position).getField(); + } + else + { + solrSortField = fields.getFields().get(0).getField(); + } + } + else + { + solrSortField = mapNonPropertyFields(luceneField); + } + } + else + { + solrSortField = mapNonPropertyFields(luceneField); + } + return solrSortField; + } + + } diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/Cloud.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/Cloud.java index 37882dd0b..2f2e8a73d 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/Cloud.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/Cloud.java @@ -1,168 +1,168 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import java.util.Collection; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.solr.common.SolrDocumentList; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.request.SolrRequestHandler; -import org.apache.solr.response.ResultContext; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.search.DocList; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Cloud consists of support methods for running solr queries in the cloud. - * - * @author Ahmed Owian - */ -public class Cloud -{ - public static final String RESPONSE_SOLR_DOCUMENT_LIST = "responseSolrDocumentList"; - public static final String PARAM_GET_SOLR_DOCUMENT_LIST = "alfresco.getSolrDocumentList"; - protected final static Logger log = LoggerFactory.getLogger(Cloud.class); - - /** - * Creates a query to select docs with any of the specified field values - * @param fieldName the name of the field in the condition - * @param operator currently only OR makes sense. To add AND, fieldName must become a list of fields. - * @param valueLists a bunch of possible values for the field - * @return the resulting query string - */ - @SuppressWarnings("rawtypes") - String getQuery(String fieldName, String operator, Collection... valueLists) - { - StringBuilder query = new StringBuilder(); - for (Collection values : valueLists) - { - for (Object value : values) - { - query.append(fieldName).append(":").append("\"").append(value.toString()).append("\"").append(operator); - } - } - - if (query.length() >= operator.length()) - { - // Removes the last appended operator from the query string - query.delete(query.length() - operator.length(), query.length()); - } - - return query.toString(); - } - - /** - * Returns whether or not a doc exists that satisfies the specified query - * @param requestHandler the handler that handles the request - * @param request the request object to put the query on - * @param query the string that specifies the doc - * @return true if the specified query returns a doc - */ - boolean exists(SolrRequestHandler requestHandler, SolrQueryRequest request, String query) - { - ModifiableSolrParams params = new ModifiableSolrParams(request.getParams()); - // Sets 1 because this is effectively a boolean query to see if there exists a single match - params.set("q", query).set("fl", "id").set("rows", "1"); - ResultContext rc = this.getResultContext(requestHandler, request, params); - - if (rc != null) - { -// TODO Should we use rc.docs.matches() instead? - if (rc.getDocList() != null) { return rc.getDocList().iterator().hasNext(); } - } - - return false; - } - - /** - * Returns the doc list resulting from running the query - * @param requestHandler the handler that handles the request - * @param request the request object to put the query on - * @param query the string that specifies the docs - * @return the docs that come back from the query - */ - DocList getDocList(SolrRequestHandler requestHandler, SolrQueryRequest request, String query) - { - // Getting the doc list is shard-specific, and not really cloud-friendly - - ModifiableSolrParams params = new ModifiableSolrParams(request.getParams()); - // Sets MAX_VALUE to get all the rows - params.set("q", query).set("fl", QueryConstants.FIELD_SOLR4_ID).set("rows", Integer.MAX_VALUE); - ResultContext rc = this.getResultContext(requestHandler, request, params); - return rc != null ? rc.getDocList() : null; - } - - /** - * @param requestHandler the handler that handles the request - * @param request the request object to put the params on - * @param params Solr parameters - * @return the result context from the handled request - */ - ResultContext getResultContext(SolrRequestHandler requestHandler, SolrQueryRequest request, SolrParams params) - { - SolrQueryResponse solrRsp = getResponse(requestHandler, request, params); - ResultContext rc = (ResultContext) solrRsp.getValues().get("response"); - return rc; - } - - /** - * Returns the SolrDocumentList from the response, assuming that the specified request handler uses the - * AlfrescoSearchHandler which puts it on the response values. - * @param requestHandler the handler that handles the request - * @param request the request object to put the params on - * @param params Solr parameters - * @return the result context from the handled request - */ - SolrDocumentList getSolrDocumentList(SolrRequestHandler requestHandler, SolrQueryRequest request, - ModifiableSolrParams params) - { - // Tells the AlfrescoSearchHandler to get the SolrDocumentList from the default results and add to response - params.set(PARAM_GET_SOLR_DOCUMENT_LIST, true); - SolrQueryResponse solrRsp = getResponse(requestHandler, request, params); - SolrDocumentList docs = (SolrDocumentList) solrRsp.getValues().get(RESPONSE_SOLR_DOCUMENT_LIST); - return docs; - } - - /** - * @param requestHandler the handler that handles the request - * @param request the request object to put the params on - * @param params Solr parameters - * @return the response - */ - SolrQueryResponse getResponse(SolrRequestHandler requestHandler, SolrQueryRequest request, SolrParams params) - { - request.setParams(params); - log.info("Running query " + params.get("q")); - SolrQueryResponse solrRsp = new SolrQueryResponse(); - requestHandler.handleRequest(request, solrRsp); - return solrRsp; - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import java.util.Collection; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestHandler; +import org.apache.solr.response.ResultContext; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.search.DocList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Cloud consists of support methods for running solr queries in the cloud. + * + * @author Ahmed Owian + */ +public class Cloud +{ + public static final String RESPONSE_SOLR_DOCUMENT_LIST = "responseSolrDocumentList"; + public static final String PARAM_GET_SOLR_DOCUMENT_LIST = "alfresco.getSolrDocumentList"; + protected final static Logger log = LoggerFactory.getLogger(Cloud.class); + + /** + * Creates a query to select docs with any of the specified field values + * @param fieldName the name of the field in the condition + * @param operator currently only OR makes sense. To add AND, fieldName must become a list of fields. + * @param valueLists a bunch of possible values for the field + * @return the resulting query string + */ + @SuppressWarnings("rawtypes") + String getQuery(String fieldName, String operator, Collection... valueLists) + { + StringBuilder query = new StringBuilder(); + for (Collection values : valueLists) + { + for (Object value : values) + { + query.append(fieldName).append(":").append("\"").append(value.toString()).append("\"").append(operator); + } + } + + if (query.length() >= operator.length()) + { + // Removes the last appended operator from the query string + query.delete(query.length() - operator.length(), query.length()); + } + + return query.toString(); + } + + /** + * Returns whether or not a doc exists that satisfies the specified query + * @param requestHandler the handler that handles the request + * @param request the request object to put the query on + * @param query the string that specifies the doc + * @return true if the specified query returns a doc + */ + boolean exists(SolrRequestHandler requestHandler, SolrQueryRequest request, String query) + { + ModifiableSolrParams params = new ModifiableSolrParams(request.getParams()); + // Sets 1 because this is effectively a boolean query to see if there exists a single match + params.set("q", query).set("fl", "id").set("rows", "1"); + ResultContext rc = this.getResultContext(requestHandler, request, params); + + if (rc != null) + { +// TODO Should we use rc.docs.matches() instead? + if (rc.getDocList() != null) { return rc.getDocList().iterator().hasNext(); } + } + + return false; + } + + /** + * Returns the doc list resulting from running the query + * @param requestHandler the handler that handles the request + * @param request the request object to put the query on + * @param query the string that specifies the docs + * @return the docs that come back from the query + */ + DocList getDocList(SolrRequestHandler requestHandler, SolrQueryRequest request, String query) + { + // Getting the doc list is shard-specific, and not really cloud-friendly + + ModifiableSolrParams params = new ModifiableSolrParams(request.getParams()); + // Sets MAX_VALUE to get all the rows + params.set("q", query).set("fl", QueryConstants.FIELD_SOLR4_ID).set("rows", Integer.MAX_VALUE); + ResultContext rc = this.getResultContext(requestHandler, request, params); + return rc != null ? rc.getDocList() : null; + } + + /** + * @param requestHandler the handler that handles the request + * @param request the request object to put the params on + * @param params Solr parameters + * @return the result context from the handled request + */ + ResultContext getResultContext(SolrRequestHandler requestHandler, SolrQueryRequest request, SolrParams params) + { + SolrQueryResponse solrRsp = getResponse(requestHandler, request, params); + ResultContext rc = (ResultContext) solrRsp.getValues().get("response"); + return rc; + } + + /** + * Returns the SolrDocumentList from the response, assuming that the specified request handler uses the + * AlfrescoSearchHandler which puts it on the response values. + * @param requestHandler the handler that handles the request + * @param request the request object to put the params on + * @param params Solr parameters + * @return the result context from the handled request + */ + SolrDocumentList getSolrDocumentList(SolrRequestHandler requestHandler, SolrQueryRequest request, + ModifiableSolrParams params) + { + // Tells the AlfrescoSearchHandler to get the SolrDocumentList from the default results and add to response + params.set(PARAM_GET_SOLR_DOCUMENT_LIST, true); + SolrQueryResponse solrRsp = getResponse(requestHandler, request, params); + SolrDocumentList docs = (SolrDocumentList) solrRsp.getValues().get(RESPONSE_SOLR_DOCUMENT_LIST); + return docs; + } + + /** + * @param requestHandler the handler that handles the request + * @param request the request object to put the params on + * @param params Solr parameters + * @return the response + */ + SolrQueryResponse getResponse(SolrRequestHandler requestHandler, SolrQueryRequest request, SolrParams params) + { + request.setParams(params); + log.info("Running query " + params.get("q")); + SolrQueryResponse solrRsp = new SolrQueryResponse(); + requestHandler.handleRequest(request, solrRsp); + return solrRsp; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/ContextAwareQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/ContextAwareQuery.java index acc700063..cad8eb4c2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/ContextAwareQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/ContextAwareQuery.java @@ -1,120 +1,120 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import java.io.IOException; - -import org.alfresco.service.cmr.search.SearchParameters; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - * - */ -public class ContextAwareQuery extends Query -{ - protected final static Logger log = LoggerFactory.getLogger(ContextAwareQuery.class); - - private final Query luceneQuery; - private final SearchParameters searchParameters; - - /** - * @param luceneQuery Query - * @param searchParameters SearchParameters - */ - public ContextAwareQuery(Query luceneQuery, SearchParameters searchParameters) - { - this.luceneQuery = luceneQuery; - this.searchParameters = searchParameters; - } - - /** - * @param field String - * @return String - * @see org.apache.lucene.search.Query#toString(java.lang.String) - */ - public String toString(String field) - { - return luceneQuery.toString(field); - } - - /** - * @param searcher IndexSearcher - * @return Weight - * @throws IOException - * @see org.apache.lucene.search.Query#createWeight(IndexSearcher, boolean) - */ - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - return luceneQuery.createWeight(searcher, needsScore); - } - - - - /** - * @param reader IndexReader - * @return Query - * @throws IOException - * @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) - */ - public Query rewrite(IndexReader reader) throws IOException - { - return luceneQuery.rewrite(reader); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ContextAwareQuery)) return false; - - ContextAwareQuery that = (ContextAwareQuery) o; - - if (luceneQuery != null ? !luceneQuery.equals(that.luceneQuery) : that.luceneQuery != null) return false; - return searchParameters != null ? searchParameters.equals(that.searchParameters) : that.searchParameters == null; - - } - - @Override - public int hashCode() { - int result = luceneQuery != null ? luceneQuery.hashCode() : 0; - result = 31 * result + (searchParameters != null ? searchParameters.hashCode() : 0); - return result; - } - - public Query getLuceneQuery() - { - return luceneQuery; - } - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import java.io.IOException; + +import org.alfresco.service.cmr.search.SearchParameters; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + * + */ +public class ContextAwareQuery extends Query +{ + protected final static Logger log = LoggerFactory.getLogger(ContextAwareQuery.class); + + private final Query luceneQuery; + private final SearchParameters searchParameters; + + /** + * @param luceneQuery Query + * @param searchParameters SearchParameters + */ + public ContextAwareQuery(Query luceneQuery, SearchParameters searchParameters) + { + this.luceneQuery = luceneQuery; + this.searchParameters = searchParameters; + } + + /** + * @param field String + * @return String + * @see org.apache.lucene.search.Query#toString(java.lang.String) + */ + public String toString(String field) + { + return luceneQuery.toString(field); + } + + /** + * @param searcher IndexSearcher + * @return Weight + * @throws IOException + * @see org.apache.lucene.search.Query#createWeight(IndexSearcher, boolean) + */ + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + return luceneQuery.createWeight(searcher, needsScore); + } + + + + /** + * @param reader IndexReader + * @return Query + * @throws IOException + * @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) + */ + public Query rewrite(IndexReader reader) throws IOException + { + return luceneQuery.rewrite(reader); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ContextAwareQuery)) return false; + + ContextAwareQuery that = (ContextAwareQuery) o; + + if (luceneQuery != null ? !luceneQuery.equals(that.luceneQuery) : that.luceneQuery != null) return false; + return searchParameters != null ? searchParameters.equals(that.searchParameters) : that.searchParameters == null; + + } + + @Override + public int hashCode() { + int result = luceneQuery != null ? luceneQuery.hashCode() : 0; + result = 31 * result + (searchParameters != null ? searchParameters.hashCode() : 0); + return result; + } + + public Query getLuceneQuery() + { + return luceneQuery; + } + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/DualPivotQuickSort.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/DualPivotQuickSort.java index bdf10ddfe..065892cb9 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/DualPivotQuickSort.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/DualPivotQuickSort.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerOfResources.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerOfResources.java index 005c1d4c6..39f9113b3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerOfResources.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerOfResources.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerReportHelper.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerReportHelper.java index e30f8b8ce..7f1767315 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerReportHelper.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerReportHelper.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -562,4 +562,4 @@ class HandlerReportHelper report.add(cname, coreSummary); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/InformationServer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/InformationServer.java index c23d6ca2e..42b207abc 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/InformationServer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/InformationServer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrInformationServer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrInformationServer.java index cdfb2faeb..b4bb57cd3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrInformationServer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrInformationServer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -4102,4 +4102,4 @@ public class SolrInformationServer implements InformationServer return empty(); } } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrKeyResourceLoader.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrKeyResourceLoader.java index 47efdec19..dd4c0d2ae 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrKeyResourceLoader.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrKeyResourceLoader.java @@ -1,76 +1,76 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -import org.alfresco.encryption.KeyResourceLoader; -import org.apache.solr.core.SolrResourceLoader; - -/** - * Loads encryption key resources from a Solr core installation using a SolrResourceLoader. - * - * @since 4.0 - */ -public class SolrKeyResourceLoader implements KeyResourceLoader -{ - private SolrResourceLoader loader; - - public SolrKeyResourceLoader(SolrResourceLoader loader) - { - this.loader = loader; - } - - @Override - public InputStream getKeyStore(String location) - throws FileNotFoundException - { - try - { - return loader.openResource(location); - } - catch (IOException e) - { - // TODO: SOLR API changes mean that IOException must be handled. - // This may need revisiting. - throw new FileNotFoundException("Caused by " + e.getMessage()); - } - } - - @Override - public Properties loadKeyMetaData(String location) throws IOException - { - Properties p = new Properties(); - InputStream stream = loader.openResource(location); - p.load(stream); - stream.close(); - return p; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import org.alfresco.encryption.KeyResourceLoader; +import org.apache.solr.core.SolrResourceLoader; + +/** + * Loads encryption key resources from a Solr core installation using a SolrResourceLoader. + * + * @since 4.0 + */ +public class SolrKeyResourceLoader implements KeyResourceLoader +{ + private SolrResourceLoader loader; + + public SolrKeyResourceLoader(SolrResourceLoader loader) + { + this.loader = loader; + } + + @Override + public InputStream getKeyStore(String location) + throws FileNotFoundException + { + try + { + return loader.openResource(location); + } + catch (IOException e) + { + // TODO: SOLR API changes mean that IOException must be handled. + // This may need revisiting. + throw new FileNotFoundException("Caused by " + e.getMessage()); + } + } + + @Override + public Properties loadKeyMetaData(String location) throws IOException + { + Properties p = new Properties(); + InputStream stream = loader.openResource(location); + p.load(stream); + stream.close(); + return p; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/StripLocaleStrField.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/StripLocaleStrField.java index 81c16652c..9cb51e668 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/StripLocaleStrField.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/StripLocaleStrField.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -48,4 +48,4 @@ public class StripLocaleStrField extends StrField .orElse(null); return super.createFields(field, newValue, boost); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/TrackerStateException.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/TrackerStateException.java index 1c9c39299..28945d1ea 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/TrackerStateException.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/TrackerStateException.java @@ -1,79 +1,79 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import org.alfresco.error.AlfrescoRuntimeException; - -/** - * @author Andy - * - */ -public class TrackerStateException extends AlfrescoRuntimeException -{ - - /** - * - */ - private static final long serialVersionUID = -3030261887767267309L; - - /** - * @param msgId - */ - public TrackerStateException(String msgId) - { - super(msgId); - } - - /** - * @param msgId - * @param msgParams - */ - public TrackerStateException(String msgId, Object[] msgParams) - { - super(msgId, msgParams); - } - - /** - * @param msgId - * @param cause - */ - public TrackerStateException(String msgId, Throwable cause) - { - super(msgId, cause); - } - - /** - * @param msgId - * @param msgParams - * @param cause - */ - public TrackerStateException(String msgId, Object[] msgParams, Throwable cause) - { - super(msgId, msgParams, cause); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * @author Andy + * + */ +public class TrackerStateException extends AlfrescoRuntimeException +{ + + /** + * + */ + private static final long serialVersionUID = -3030261887767267309L; + + /** + * @param msgId + */ + public TrackerStateException(String msgId) + { + super(msgId); + } + + /** + * @param msgId + * @param msgParams + */ + public TrackerStateException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + + /** + * @param msgId + * @param cause + */ + public TrackerStateException(String msgId, Throwable cause) + { + super(msgId, cause); + } + + /** + * @param msgId + * @param msgParams + * @param cause + */ + public TrackerStateException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrOpenBitSetAdapter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrOpenBitSetAdapter.java index 05bf759ac..6b6fafdd5 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrOpenBitSetAdapter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrOpenBitSetAdapter.java @@ -1,77 +1,77 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.adapters; - -import org.apache.lucene.util.LongBitSet; - -/** - * The reason we have this class is so that lucene-free dependent classes can be dependent on IOpenBitSet instead of the - * lucene-version-specific OpenBitSet. - * @author Ahmed Owian - */ -public class SolrOpenBitSetAdapter implements IOpenBitSet -{ - LongBitSet delegate; - - public SolrOpenBitSetAdapter() - { - delegate = new LongBitSet(64); - } - - @Override - public void set(long index) - { - delegate = LongBitSet.ensureCapacity(delegate, index); - delegate.set(index); - } - - @Override - public void or(IOpenBitSet other) - { - delegate.or( ((SolrOpenBitSetAdapter)other).delegate); - } - - @Override - public long nextSetBit(long index) - { - return delegate.nextSetBit(index); - } - - @Override - public long cardinality() - { - return delegate.cardinality(); - } - - @Override - public boolean get(long index) - { - return delegate.get(index); - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.adapters; + +import org.apache.lucene.util.LongBitSet; + +/** + * The reason we have this class is so that lucene-free dependent classes can be dependent on IOpenBitSet instead of the + * lucene-version-specific OpenBitSet. + * @author Ahmed Owian + */ +public class SolrOpenBitSetAdapter implements IOpenBitSet +{ + LongBitSet delegate; + + public SolrOpenBitSetAdapter() + { + delegate = new LongBitSet(64); + } + + @Override + public void set(long index) + { + delegate = LongBitSet.ensureCapacity(delegate, index); + delegate.set(index); + } + + @Override + public void or(IOpenBitSet other) + { + delegate.or( ((SolrOpenBitSetAdapter)other).delegate); + } + + @Override + public long nextSetBit(long index) + { + return delegate.nextSetBit(index); + } + + @Override + public long cardinality() + { + return delegate.cardinality(); + } + + @Override + public boolean get(long index) + { + return delegate.get(index); + } + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrSimpleOrderedMap.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrSimpleOrderedMap.java index c1901afb2..b38b9d8fb 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrSimpleOrderedMap.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrSimpleOrderedMap.java @@ -1,39 +1,39 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.adapters; - -import org.apache.solr.common.util.SimpleOrderedMap; - -public class SolrSimpleOrderedMap extends SimpleOrderedMap implements ISimpleOrderedMap -{ - - /** - * - */ - private static final long serialVersionUID = 2799724619330515239L; - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.adapters; + +import org.apache.solr.common.util.SimpleOrderedMap; + +public class SolrSimpleOrderedMap extends SimpleOrderedMap implements ISimpleOrderedMap +{ + + /** + * + */ + private static final long serialVersionUID = 2799724619330515239L; + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/AuthorityCacheRegenerator.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/AuthorityCacheRegenerator.java index 624a64d08..1e3e44b1a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/AuthorityCacheRegenerator.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/AuthorityCacheRegenerator.java @@ -1,58 +1,58 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.cache; - -import java.io.IOException; - -import org.apache.lucene.search.Query; -import org.apache.solr.search.CacheRegenerator; -import org.apache.solr.search.SolrCache; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Cache regeneration for AUTHORITY and AUTHSET queries. - * - * @author Matt Ward - */ -public class AuthorityCacheRegenerator implements CacheRegenerator -{ - @SuppressWarnings({ "rawtypes" }) - @Override - public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache, - SolrCache oldCache, Object oldKey, Object oldVal) throws IOException - { - if (oldKey instanceof Query) - { - // The authority cache contains results keyed by SolrAuthorityQuery - // and SolrAuthoritySetQuery. - Query authQuery = (Query) oldKey; - // Execute the query on the new searcher - resulting in cache population as a side-effect. - newSearcher.getDocSet(authQuery); - } - return true; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.cache; + +import java.io.IOException; + +import org.apache.lucene.search.Query; +import org.apache.solr.search.CacheRegenerator; +import org.apache.solr.search.SolrCache; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Cache regeneration for AUTHORITY and AUTHSET queries. + * + * @author Matt Ward + */ +public class AuthorityCacheRegenerator implements CacheRegenerator +{ + @SuppressWarnings({ "rawtypes" }) + @Override + public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache, + SolrCache oldCache, Object oldKey, Object oldVal) throws IOException + { + if (oldKey instanceof Query) + { + // The authority cache contains results keyed by SolrAuthorityQuery + // and SolrAuthoritySetQuery. + Query authQuery = (Query) oldKey; + // Execute the query on the new searcher - resulting in cache population as a side-effect. + newSearcher.getDocSet(authQuery); + } + return true; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/CacheConstants.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/CacheConstants.java index fcc882cbe..14d0ca056 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/CacheConstants.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/CacheConstants.java @@ -1,48 +1,48 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.cache; - -/** - * Constants for per-searcher cache names and keys. - * - * @author Matt Ward - */ -public class CacheConstants -{ - // Cache names -// public static String ALFRESCO_CACHE = "alfrescoCache"; -// public static String ALFRESCO_ARRAYLIST_CACHE = "alfrescoArrayListCache"; -// public static String ALFRESCO_PATH_CACHE = "alfrescoPathCache"; -// public static String ALFRESCO_READER_TO_ACL_IDS_CACHE = "alfrescoReaderToAclIdsCache"; -// public static String ALFRESCO_DENY_TO_ACL_IDS_CACHE = "alfrescoDenyToAclIdsCache"; - - public final static String ALFRESCO_AUTHORITY_CACHE = "alfrescoAuthorityCache"; - public final static String ALFRESCO_OWNERLOOKUP_CACHE = "alfrescoOwnerCache"; - public final static String ALFRESCO_READER_CACHE = "alfrescoReaderCache"; - public final static String ALFRESCO_DENIED_CACHE = "alfrescoDeniedCache"; - public final static String ALFRESCO_PATH_CACHE = "alfrescoPathCache"; -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.cache; + +/** + * Constants for per-searcher cache names and keys. + * + * @author Matt Ward + */ +public class CacheConstants +{ + // Cache names +// public static String ALFRESCO_CACHE = "alfrescoCache"; +// public static String ALFRESCO_ARRAYLIST_CACHE = "alfrescoArrayListCache"; +// public static String ALFRESCO_PATH_CACHE = "alfrescoPathCache"; +// public static String ALFRESCO_READER_TO_ACL_IDS_CACHE = "alfrescoReaderToAclIdsCache"; +// public static String ALFRESCO_DENY_TO_ACL_IDS_CACHE = "alfrescoDenyToAclIdsCache"; + + public final static String ALFRESCO_AUTHORITY_CACHE = "alfrescoAuthorityCache"; + public final static String ALFRESCO_OWNERLOOKUP_CACHE = "alfrescoOwnerCache"; + public final static String ALFRESCO_READER_CACHE = "alfrescoReaderCache"; + public final static String ALFRESCO_DENIED_CACHE = "alfrescoDeniedCache"; + public final static String ALFRESCO_PATH_CACHE = "alfrescoPathCache"; +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/PathCacheRegenerator.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/PathCacheRegenerator.java index 795f00b1c..a9d3690b2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/PathCacheRegenerator.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/PathCacheRegenerator.java @@ -1,59 +1,59 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.cache; - -import java.io.IOException; - -import org.alfresco.solr.query.SolrCachingPathQuery; -import org.alfresco.solr.query.SolrPathQuery; -import org.apache.solr.search.CacheRegenerator; -import org.apache.solr.search.SolrCache; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * {@link CacheRegenerator} for alfrescoPathCache - * - * @author Matt Ward - */ -public class PathCacheRegenerator implements CacheRegenerator -{ - @SuppressWarnings({ "rawtypes" }) - @Override - public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache, - SolrCache oldCache, Object oldKey, Object oldVal) throws IOException - { - if (oldKey instanceof SolrPathQuery) - { - SolrPathQuery pathQuery = (SolrPathQuery) oldKey; - // Re-execute the path query in a cache-aware context - causing new results to be cached. - SolrCachingPathQuery cachingPathQuery = new SolrCachingPathQuery(pathQuery); - newSearcher.getDocSet(cachingPathQuery); - } - return true; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.cache; + +import java.io.IOException; + +import org.alfresco.solr.query.SolrCachingPathQuery; +import org.alfresco.solr.query.SolrPathQuery; +import org.apache.solr.search.CacheRegenerator; +import org.apache.solr.search.SolrCache; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * {@link CacheRegenerator} for alfrescoPathCache + * + * @author Matt Ward + */ +public class PathCacheRegenerator implements CacheRegenerator +{ + @SuppressWarnings({ "rawtypes" }) + @Override + public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache, + SolrCache oldCache, Object oldKey, Object oldVal) throws IOException + { + if (oldKey instanceof SolrPathQuery) + { + SolrPathQuery pathQuery = (SolrPathQuery) oldKey; + // Re-execute the path query in a cache-aware context - causing new results to be cached. + SolrCachingPathQuery cachingPathQuery = new SolrCachingPathQuery(pathQuery); + newSearcher.getDocSet(cachingPathQuery); + } + return true; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ClearLocaleComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ClearLocaleComponent.java index 90d0cef67..8710e9969 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ClearLocaleComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ClearLocaleComponent.java @@ -1,78 +1,78 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component; - -import java.io.IOException; - -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * @author Andy - * - */ -public class ClearLocaleComponent extends SearchComponent -{ - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - // Nothing to do - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void process(ResponseBuilder rb) throws IOException - { - I18NUtil.setLocale(null); - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getDescription() - */ - @Override - public String getDescription() - { - return "clearLocale"; - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getSource() - */ - @Override - public String getSource() - { - return ""; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component; + +import java.io.IOException; + +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + * + */ +public class ClearLocaleComponent extends SearchComponent +{ + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + // Nothing to do + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void process(ResponseBuilder rb) throws IOException + { + I18NUtil.setLocale(null); + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getDescription() + */ + @Override + public String getDescription() + { + return "clearLocale"; + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getSource() + */ + @Override + public String getSource() + { + return ""; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ConsistencyComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ConsistencyComponent.java index 575e8c90f..8aea5489c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ConsistencyComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ConsistencyComponent.java @@ -1,105 +1,105 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component; - -import org.alfresco.solr.AlfrescoCoreAdminHandler; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.TrackerState; -import org.alfresco.solr.tracker.MetadataTracker; -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.apache.solr.request.SolrQueryRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.util.Date; - -/** - * Adds consitency information to the search results - */ -public class ConsistencyComponent extends SearchComponent -{ - private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - // No preparation required. - } - - @Override - public void finishStage(ResponseBuilder rb) - { - super.finishStage(rb); - if (rb.stage != ResponseBuilder.STAGE_GET_FIELDS) - return; - try { - process(rb); - } catch (IOException e) { - LOG.error(e.getMessage()); - } - } - - @Override - public void process(ResponseBuilder rb) throws IOException - { - SolrQueryRequest req = rb.req; - AlfrescoCoreAdminHandler adminHandler = (AlfrescoCoreAdminHandler) - req.getCore(). - getCoreContainer(). - getMultiCoreHandler(); - - boolean isShard = rb.req.getParams().getBool(ShardParams.IS_SHARD, false); - MetadataTracker metaTrkr = adminHandler.getTrackerRegistry().getTrackerForCore(req.getCore().getName(), MetadataTracker.class); - if(metaTrkr != null && !isShard) - { - TrackerState metadataTrkrState = metaTrkr.getTrackerState(); - - long lastIndexedTx = metadataTrkrState.getLastIndexedTxId(); - long lastIndexTxCommitTime = metadataTrkrState.getLastIndexedTxCommitTime(); - long lastTxIdOnServer = metadataTrkrState.getLastTxIdOnServer(); - long transactionsToDo = lastTxIdOnServer - lastIndexedTx; - if (transactionsToDo < 0) - { - transactionsToDo = 0; - } - rb.rsp.add("lastIndexedTx", lastIndexedTx); - rb.rsp.add("lastIndexedTxTime", lastIndexTxCommitTime); - rb.rsp.add("txRemaining", transactionsToDo); - } - } - - @Override - public String getDescription() - { - return "Adds consitency information to the search results."; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component; + +import org.alfresco.solr.AlfrescoCoreAdminHandler; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.TrackerState; +import org.alfresco.solr.tracker.MetadataTracker; +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.request.SolrQueryRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.Date; + +/** + * Adds consitency information to the search results + */ +public class ConsistencyComponent extends SearchComponent +{ + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + // No preparation required. + } + + @Override + public void finishStage(ResponseBuilder rb) + { + super.finishStage(rb); + if (rb.stage != ResponseBuilder.STAGE_GET_FIELDS) + return; + try { + process(rb); + } catch (IOException e) { + LOG.error(e.getMessage()); + } + } + + @Override + public void process(ResponseBuilder rb) throws IOException + { + SolrQueryRequest req = rb.req; + AlfrescoCoreAdminHandler adminHandler = (AlfrescoCoreAdminHandler) + req.getCore(). + getCoreContainer(). + getMultiCoreHandler(); + + boolean isShard = rb.req.getParams().getBool(ShardParams.IS_SHARD, false); + MetadataTracker metaTrkr = adminHandler.getTrackerRegistry().getTrackerForCore(req.getCore().getName(), MetadataTracker.class); + if(metaTrkr != null && !isShard) + { + TrackerState metadataTrkrState = metaTrkr.getTrackerState(); + + long lastIndexedTx = metadataTrkrState.getLastIndexedTxId(); + long lastIndexTxCommitTime = metadataTrkrState.getLastIndexedTxCommitTime(); + long lastTxIdOnServer = metadataTrkrState.getLastTxIdOnServer(); + long transactionsToDo = lastTxIdOnServer - lastIndexedTx; + if (transactionsToDo < 0) + { + transactionsToDo = 0; + } + rb.rsp.add("lastIndexedTx", lastIndexedTx); + rb.rsp.add("lastIndexedTxTime", lastIndexTxCommitTime); + rb.rsp.add("txRemaining", transactionsToDo); + } + } + + @Override + public String getDescription() + { + return "Adds consitency information to the search results."; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/FingerPrintComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/FingerPrintComponent.java index e96f7ad16..28075d370 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/FingerPrintComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/FingerPrintComponent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -135,4 +135,4 @@ public class FingerPrintComponent extends SearchComponent { return null; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/QueryLoggingComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/QueryLoggingComponent.java index c6c049d3f..7d905bf7d 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/QueryLoggingComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/QueryLoggingComponent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetCountsComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetCountsComponent.java index 15f0365ab..bd6f4c7c4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetCountsComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetCountsComponent.java @@ -1,279 +1,279 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component; - -import java.io.IOException; -import java.util.Collection; -import java.util.Map; - -import com.google.common.collect.BiMap; - -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; - -/** - * @author Andy - * - */ -public class RewriteFacetCountsComponent extends SearchComponent -{ - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#finishStage(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void finishStage(ResponseBuilder rb) - { - /// wait until after distributed faceting is done - if (!rb.doFacets || rb.stage != ResponseBuilder.STAGE_GET_FIELDS) - { - return; - } - else - { - process(rb); - } - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - // Nothing to do - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void process(ResponseBuilder rb) - { - // rewrite - - rewrite(rb, "_field_mappings_", "facet_counts", "facet_fields"); - rewrite(rb, "_date_mappings_", "facet_counts", "facet_dates"); - rewrite(rb, "_range_mappings_", "facet_counts", "facet_ranges"); - rewrite(rb, "_interval_mappings_", "facet_counts", "facet_intervals"); - - rewrite(rb, "_pivot_mappings_", "facet_counts", "facet_pivot"); - rewritePivotFields(rb, "facet_counts", "facet_pivot"); - - - rewrite(rb, "_stats_field_mappings_", "stats", "stats_fields"); - - copyAnalytics(rb, "facet_counts", "facet_fields"); - - Map mappings = (Map) rb.rsp.getValues().get("_stats_field_mappings_"); - if(mappings != null) - { - for(String key : mappings.keySet()) - { - rewrite(rb, "_stats_facet_mappings_", "stats", "stats_fields", key, "facets"); - } - } - } - - - /** - * @param rb - */ - private void copyAnalytics(ResponseBuilder rb, String ... sections) - { - NamedList found = (NamedList) rb.rsp.getValues(); - for(String section : sections) - { - found = (NamedList)found.get(section); - if(found == null) - { - return; - } - } - - NamedList analytics = (NamedList) rb.rsp.getValues(); - analytics = (NamedList)analytics.get("analytics"); - if(analytics != null) - { - for(int i = 0; i < analytics.size(); i++) - { - String name = analytics.getName(i); - Object value = analytics.getVal(i); - found.add(name, value); - } - } - - - - - - } - - - /** - * Update pivot fields in the response to reference ACS properties rather than Solr fields. - * - * @param rb The response builder. - * @param sections The names of the sections in the response to update. - */ - private void rewritePivotFields(ResponseBuilder rb, String ... sections) - { - Map mappings = (Map) rb.rsp.getValues().get("_pivot_mappings_"); - if(mappings != null) - { - NamedList found = (NamedList) rb.rsp.getValues(); - for(String section : sections) - { - found = (NamedList)found.get(section); - if(found == null) - { - return; - } - } - for(int i = 0; i < found.size(); i++) - { - String pivotName = found.getName(i); - String[] fromParts = pivotName.split(","); - String mapping = mappings.get(pivotName); - String[] toParts = mapping != null ? mapping.split(",") : fromParts; - Collection> current = (Collection>)found.getVal(i); - processPivot(rb, fromParts, toParts, current, 0); - } - } - - } - - private void processPivot(ResponseBuilder rb, String[] fromParts, String[] toParts, Collection> current, int level) - { - for(NamedList entry : current) - { - for(int i = 0; i < entry.size(); i++) - { - String name = entry.getName(i); - if(name.equals("field")) - { - entry.setVal(i, fromParts[level].trim()); - } - else if(name.equals("pivot")) - { - Collection> pivot = (Collection>)entry.getVal(i); - processPivot(rb, fromParts, toParts, pivot, level+1); - } - else if(name.equals("ranges")) - { - SimpleOrderedMap ranges = (SimpleOrderedMap)entry.getVal(i); - processRanges(rb, ranges); - } - else - { - // leave alone - } - } - } - } - - /** - * Update the returned ranges to reference ACS properties rather than Solr fields. - * - * @param rb The response builder. - * @param ranges The ranges to update. - */ - private void processRanges(ResponseBuilder rb, SimpleOrderedMap ranges) { - - BiMap mappings = (BiMap) rb.rsp.getValues().get("_range_mappings_"); - if(mappings != null) - { - updateToACSNaming(mappings, ranges); - } - } - - /** - * Update the returned facet sections to reference ACS properties rather than Solr fields. - * - * @param rb the response builder. - * @param mappingName The name of the mapping definition created in {@link RewriteFacetParametersComponent}. - * @param sections The names of the sections to be updated. - */ - private void rewrite(ResponseBuilder rb, String mappingName, String ... sections) - { - BiMap mappings = (BiMap) rb.rsp.getValues().get(mappingName); - if(mappings != null) - { - NamedList found = (NamedList) rb.rsp.getValues(); - for (String section : sections) - { - found = (NamedList) found.get(section); - if (found == null) - { - return; - } - } - // This found already contains the private buckets - updateToACSNaming(mappings, found); - } - } - - /** - * Update entries in a list to reference ACS properties rather than Solr fields. - * - * @param mappings The mapping from ACS properties to Solr fields. - * @param propertyList The list to update. - */ - private void updateToACSNaming(BiMap mappings, NamedList propertyList) - { - for (int i = 0; i < propertyList.size(); i++) - { - String name = propertyList.getName(i); - String newName = mappings.inverse().get(name); - if (newName != null) - { - propertyList.setName(i, newName); - } - } - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getDescription() - */ - @Override - public String getDescription() - { - return "RewriteFacetCounts"; - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getSource() - */ - @Override - public String getSource() - { - return ""; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +import com.google.common.collect.BiMap; + +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; + +/** + * @author Andy + * + */ +public class RewriteFacetCountsComponent extends SearchComponent +{ + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#finishStage(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void finishStage(ResponseBuilder rb) + { + /// wait until after distributed faceting is done + if (!rb.doFacets || rb.stage != ResponseBuilder.STAGE_GET_FIELDS) + { + return; + } + else + { + process(rb); + } + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + // Nothing to do + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void process(ResponseBuilder rb) + { + // rewrite + + rewrite(rb, "_field_mappings_", "facet_counts", "facet_fields"); + rewrite(rb, "_date_mappings_", "facet_counts", "facet_dates"); + rewrite(rb, "_range_mappings_", "facet_counts", "facet_ranges"); + rewrite(rb, "_interval_mappings_", "facet_counts", "facet_intervals"); + + rewrite(rb, "_pivot_mappings_", "facet_counts", "facet_pivot"); + rewritePivotFields(rb, "facet_counts", "facet_pivot"); + + + rewrite(rb, "_stats_field_mappings_", "stats", "stats_fields"); + + copyAnalytics(rb, "facet_counts", "facet_fields"); + + Map mappings = (Map) rb.rsp.getValues().get("_stats_field_mappings_"); + if(mappings != null) + { + for(String key : mappings.keySet()) + { + rewrite(rb, "_stats_facet_mappings_", "stats", "stats_fields", key, "facets"); + } + } + } + + + /** + * @param rb + */ + private void copyAnalytics(ResponseBuilder rb, String ... sections) + { + NamedList found = (NamedList) rb.rsp.getValues(); + for(String section : sections) + { + found = (NamedList)found.get(section); + if(found == null) + { + return; + } + } + + NamedList analytics = (NamedList) rb.rsp.getValues(); + analytics = (NamedList)analytics.get("analytics"); + if(analytics != null) + { + for(int i = 0; i < analytics.size(); i++) + { + String name = analytics.getName(i); + Object value = analytics.getVal(i); + found.add(name, value); + } + } + + + + + + } + + + /** + * Update pivot fields in the response to reference ACS properties rather than Solr fields. + * + * @param rb The response builder. + * @param sections The names of the sections in the response to update. + */ + private void rewritePivotFields(ResponseBuilder rb, String ... sections) + { + Map mappings = (Map) rb.rsp.getValues().get("_pivot_mappings_"); + if(mappings != null) + { + NamedList found = (NamedList) rb.rsp.getValues(); + for(String section : sections) + { + found = (NamedList)found.get(section); + if(found == null) + { + return; + } + } + for(int i = 0; i < found.size(); i++) + { + String pivotName = found.getName(i); + String[] fromParts = pivotName.split(","); + String mapping = mappings.get(pivotName); + String[] toParts = mapping != null ? mapping.split(",") : fromParts; + Collection> current = (Collection>)found.getVal(i); + processPivot(rb, fromParts, toParts, current, 0); + } + } + + } + + private void processPivot(ResponseBuilder rb, String[] fromParts, String[] toParts, Collection> current, int level) + { + for(NamedList entry : current) + { + for(int i = 0; i < entry.size(); i++) + { + String name = entry.getName(i); + if(name.equals("field")) + { + entry.setVal(i, fromParts[level].trim()); + } + else if(name.equals("pivot")) + { + Collection> pivot = (Collection>)entry.getVal(i); + processPivot(rb, fromParts, toParts, pivot, level+1); + } + else if(name.equals("ranges")) + { + SimpleOrderedMap ranges = (SimpleOrderedMap)entry.getVal(i); + processRanges(rb, ranges); + } + else + { + // leave alone + } + } + } + } + + /** + * Update the returned ranges to reference ACS properties rather than Solr fields. + * + * @param rb The response builder. + * @param ranges The ranges to update. + */ + private void processRanges(ResponseBuilder rb, SimpleOrderedMap ranges) { + + BiMap mappings = (BiMap) rb.rsp.getValues().get("_range_mappings_"); + if(mappings != null) + { + updateToACSNaming(mappings, ranges); + } + } + + /** + * Update the returned facet sections to reference ACS properties rather than Solr fields. + * + * @param rb the response builder. + * @param mappingName The name of the mapping definition created in {@link RewriteFacetParametersComponent}. + * @param sections The names of the sections to be updated. + */ + private void rewrite(ResponseBuilder rb, String mappingName, String ... sections) + { + BiMap mappings = (BiMap) rb.rsp.getValues().get(mappingName); + if(mappings != null) + { + NamedList found = (NamedList) rb.rsp.getValues(); + for (String section : sections) + { + found = (NamedList) found.get(section); + if (found == null) + { + return; + } + } + // This found already contains the private buckets + updateToACSNaming(mappings, found); + } + } + + /** + * Update entries in a list to reference ACS properties rather than Solr fields. + * + * @param mappings The mapping from ACS properties to Solr fields. + * @param propertyList The list to update. + */ + private void updateToACSNaming(BiMap mappings, NamedList propertyList) + { + for (int i = 0; i < propertyList.size(); i++) + { + String name = propertyList.getName(i); + String newName = mappings.inverse().get(name); + if (newName != null) + { + propertyList.setName(i, newName); + } + } + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getDescription() + */ + @Override + public String getDescription() + { + return "RewriteFacetCounts"; + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getSource() + */ + @Override + public String getSource() + { + return ""; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetParametersComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetParametersComponent.java index 7e556b461..1731f6a33 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetParametersComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetParametersComponent.java @@ -1,568 +1,568 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.google.common.collect.HashBiMap; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.alfresco.solr.query.MimetypeGroupingQParserPlugin; -import org.apache.commons.lang.StringUtils; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.apache.solr.handler.component.ShardRequest; -import org.apache.solr.request.SolrQueryRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Update facet parameters to reference Solr fields rather than ACS properties. - * - * @author Andy - */ -public class RewriteFacetParametersComponent extends SearchComponent -{ - private static final Logger log = LoggerFactory.getLogger(RewriteFacetParametersComponent.class); - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - SolrQueryRequest req = rb.req; - SolrParams params = req.getParams(); - - ModifiableSolrParams fixed = new ModifiableSolrParams(); - ModifiableSolrParams allParamsWithFix = new ModifiableSolrParams(params); - fixFilterQueries(fixed, params, rb); - fixFacetParams(fixed, params, rb); - fixRows(fixed, params, rb); - - Set fixedParameterNames = fixed.getParameterNames(); - for (String fixedParam : fixedParameterNames) - { - allParamsWithFix.set(fixedParam, fixed.getParams(fixedParam)); - } - - if (allParamsWithFix.get(CommonParams.SORT) != null) - { - allParamsWithFix.remove(CommonParams.RQ); - } - - req.setParams(allParamsWithFix); - } - - /** - * Prevents users from requesting a large number of rows by - * replacing an enormous row value with a maximum value that will - * not cause a run time exception. - * @param fixed - * @param params - * @param rb - */ - private void fixRows(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) - { - String rows = params.get("rows"); - if(rows != null && !rows.isEmpty()) - { - Integer row = Integer.valueOf(rows); - // Avoid +1 in SOLR code which produces null:java.lang.NegativeArraySizeException at at org.apache.lucene.util.PriorityQueue.(PriorityQueue.java:56) - if(row > 1000000) - { - fixed.remove("rows"); - fixed.add("rows", "1000000"); - } - } - } - private void fixFilterQueries(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) - { - for(Iterator it = params.getParameterNamesIterator(); it.hasNext(); /**/) - { - String name = it.next(); - - if(name.equals("fq")) - { - String[] values = params.getParams(name); - if(values != null) - { - String[] fixedValues = new String[values.length]; - for(int i = 0; i < values.length; i++) - { - String value = values[i]; - if(value.startsWith("{!")) - { - fixedValues[i] = value; - } - else - { - if(value.startsWith("contentSize():")) - { - value = "cm:content.size:" + removeQuotes(value.substring("contentSize():".length())); - } - else if(value.startsWith("mimetype():")) - { - value = removeQuotes(value.substring("mimetype():".length())); - ArrayList expand = MimetypeGroupingQParserPlugin.getReverseMappings().get(value); - if(expand == null) - { - value = "cm:content.mimetype:\""+value+"\""; - } - else - { - StringBuilder builder = new StringBuilder(); - builder.append("cm:content.mimetype:("); - for(int j = 0; j < expand.size(); j++) - { - if(j > 0) - { - builder.append(" OR "); - } - builder.append('"'); - builder.append(expand.get(j)); - builder.append('"'); - } - builder.append(')'); - value = builder.toString(); - - } - } - fixedValues[i] = "{!afts}"+value; - } - } - fixed.add(name, fixedValues); - } - - } - } - } - - private String removeQuotes(String quoted) - { - if(quoted.startsWith("\"") && quoted.endsWith("\"")) - { - return quoted.substring(1, quoted.length()-1); - } - else - { - return quoted; - } - } - - - /** - * @param fixed ModifiableSolrParams - * @param params SolrParams - * @param rb ResponseBuilder - * @return SolrParams - */ - private SolrParams fixFacetParams(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) - { - // Use BiMaps so that these can be quickly inverted in RewriteFacetCountsComponent. - Map fieldMappings = HashBiMap.create(); - Map dateMappings = HashBiMap.create(); - Map rangeMappings = HashBiMap.create(); - Map pivotMappings = HashBiMap.create(); - Map intervalMappings = HashBiMap.create(); - - Map statsFieldMappings = HashBiMap.create(); - Map statsFacetMappings = HashBiMap.create(); - - Map functionMappings = HashBiMap.create(); - - rewriteFacetFieldList(fixed, params, "facet.field", fieldMappings, rb.req); - rewriteFacetFieldList(fixed, params, "facet.date", dateMappings, rb.req); - rewriteFacetFieldList(fixed, params, "facet.range", rangeMappings, rb.req); - rewriteFacetFieldList(fixed, params, "facet.pivot", pivotMappings, rb.req); - rewriteFacetFieldList(fixed, params, "facet.interval", intervalMappings, rb.req); - - rewriteFacetFieldList(fixed, params, "stats.field", statsFieldMappings, rb.req); - rewriteFacetFieldList(fixed, params, "stats.facet", statsFacetMappings, rb.req); - - mapFacetFunctions(fixed, params, "facet.field", functionMappings); - - rewriteFacetFieldOptions(fixed, params, "facet.prefix", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.contains", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.contains.ignoreCase", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.sort", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.limit", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.offset", fieldMappings); - rewriteMincountFacetFieldOption(fixed, params, "facet.mincount", fieldMappings,rb.req); - rewriteFacetFieldOptions(fixed, params, "facet.missing", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.method", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.enum.cache.minDF", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.enum.cache.minDF", fieldMappings); - - rewriteFacetFieldOptions(fixed, params, "facet.range.start", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.end", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.gap", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.hardend", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.include", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.other", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.method", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.limit", rangeMappings); - - rewriteMincountFacetFieldOption(fixed, params, "facet.pivot.mincount", pivotMappings, rb.req); - rewriteFacetFieldOptions(fixed, params, "facet.sort", pivotMappings); - rewriteFacetFieldOptions(fixed, params, "facet.limit", pivotMappings); - rewriteFacetFieldOptions(fixed, params, "facet.offset", pivotMappings); - - rewriteFacetFieldOptions(fixed, params, "facet.interval.set", intervalMappings); - - - - // TODO: - // f..stats.facet= - // would require a more complex rewrite - - - - rb.rsp.add("_original_parameters_", params); - rb.rsp.add("_field_mappings_", fieldMappings); - rb.rsp.add("_date_mappings_", dateMappings); - rb.rsp.add("_range_mappings_", rangeMappings); - rb.rsp.add("_pivot_mappings_", pivotMappings); - rb.rsp.add("_interval_mappings_", intervalMappings); - rb.rsp.add("_stats_field_mappings_", statsFieldMappings); - rb.rsp.add("_stats_facet_mappings_", statsFacetMappings); - rb.rsp.add("_facet_function_mappings_", functionMappings); - - return fixed; - } - - - /** - * Populate a map with the mappings from functions in the query to functions including the Solr filter handler. - * - * @param fixed The updated params object. - * @param params The original params object. - * @param paramName The name of the parameter (e.g. "facet.field"). - * @param facetFunctionMappings The map to update. - */ - private void mapFacetFunctions(ModifiableSolrParams fixed, SolrParams params, String paramName, Map facetFunctionMappings) - { - String[] facetFieldsOrig = params.getParams(paramName); - if(facetFieldsOrig != null) - { - for(String facetFields : facetFieldsOrig) - { - String[] fields = facetFields.split(","); - - for(String field : fields) - { - field = field.trim(); - if(field.endsWith("()")) - { - if(isMimetypeAndHasFQ(params, field)) - { - String function = "{!" + field.substring(0, field.length()-2)+ " group=false }"; - fixed.add("fq", function); - facetFunctionMappings.put(field, function); - } - else - { - String function = "{!" + field.substring(0, field.length()-2)+ " group=true}"; - fixed.add("fq", function); - facetFunctionMappings.put(field, function); - } - } - } - } - } - } - - - /** - * @param params - * @param field - * @return - */ - private boolean isMimetypeAndHasFQ(SolrParams params, String field) - { - if(!field.equals("mimetype()")) - { - return false; - } - else - { - String[] filterQueries = params.getParams("fq"); - if(filterQueries != null) - { - for(String fq : filterQueries) - { - if(fq.startsWith("mimetype():")) - { - return true; - } - } - } - } - return false; - } - - /** - * Ensure the mincount for all given facets is at least 1 to prevent exposing sensitive buckets to users without permission. - * - * @param fixed The updated params object. - * @param params The original params object. - * @param paramName The name of the mincount parameter to rewrite (e.g. "facet.mincount" or "facet.pivot.mincount"). - * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. - * @param req The Solr request - */ - protected void rewriteMincountFacetFieldOption(ModifiableSolrParams fixed, SolrParams params, String paramName, Map fieldMappings, SolrQueryRequest req) - { - rewriteFacetFieldOptions(fixed, params, paramName, fieldMappings); - - String shardPurpose = req.getParams().get(ShardParams.SHARDS_PURPOSE); - boolean isInitialDistributedRequest = (shardPurpose==null); - /* - * After the initial Search request, in case of Sharding the Solr node will build additional http requests to the other shards. - * These requests may have specific parameters rewritten by Solr internally. - * It's not recommended to rewrite them, because some side effect may happen. - * Only the initial request parameters may be rewritten. - * */ - if(isInitialDistributedRequest) - { - // Update any existing mincount entries for facets. - Map updatedValues = new HashMap<>(); - for (Iterator renamedParameters = fixed.getParameterNamesIterator(); renamedParameters.hasNext(); ) - { - String solrParameter = renamedParameters.next(); - if (solrParameter.endsWith("." + paramName)) - { - // If mincount is set then renamedParameters must be an integer. - int value = Integer.valueOf(fixed.get(solrParameter)); - // Don't directly edit the params while we're iterating through the entries. - updatedValues.put(solrParameter, Math.max(value, 1)); - } - } - // Now we've finished iterating, update the fixed parameters. - for (Map.Entry updatedParameterValues : updatedValues.entrySet()) - { - fixed.set(updatedParameterValues.getKey(), updatedParameterValues.getValue()); - } - - String paramValue = params.get(paramName); - int value = 0; - if (paramValue != null) - { - value = Integer.valueOf(paramValue); - } - fixed.set(paramName, Math.max(value, 1)); - } - } - - /** - * Update any existing facet parameters to use the Solr field names. - * - * @param fixed The updated params object. - * @param params The original params object. - * @param paramName The name of the parameter to rewrite (e.g. "facet.limit" or "f.fieldName.facet.limit"). - * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. - */ - private void rewriteFacetFieldOptions(ModifiableSolrParams fixed, SolrParams params, String paramName, Map fieldMappings) - { - for(Iterator originalParamsIterator = params.getParameterNamesIterator(); originalParamsIterator.hasNext(); /**/) - { - String originalSolrParam = originalParamsIterator.next(); //it contains the alfresco field name - if(originalSolrParam.startsWith("f.")) - { - if(originalSolrParam.endsWith("."+paramName)) - { - String originalAlfrescoFieldName = originalSolrParam.substring(2, originalSolrParam.length() - paramName.length() - 1); - if(fieldMappings.containsKey(originalAlfrescoFieldName)) - { - String solrFieldName = fieldMappings.get(originalAlfrescoFieldName); - fixed.set("f."+ solrFieldName +"."+paramName, params.getParams(originalSolrParam)); - } - else - { - fixed.set(originalSolrParam, params.getParams(originalSolrParam)); - } - } - } - } - } - - /** - * Tokenizes a string based on comma's except for the ones in single or double - * qoutes. - * @param line - * @return - */ - public static String[] parseFacetField(String line) - { - if(StringUtils.isEmpty(line)) - { - throw new RuntimeException("String input is requried"); - } - String[] tokens = line.split(",(?=(?:[^'|\"]*\"[^'|\"]*\")*[^'|\"]*$)", -1); - return tokens; - - } - /** - * Update the facet fields to use Solr field names rather than ACS property names. - * - * @param fixed The updated params object. - * @param params The original params object. - * @param paramName The name of the mincount parameter to rewrite (e.g. "facet.mincount" or "facet.pivot.mincount"). - * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. - * @param req The original request. - * @return An array of the facet field names. - */ - private List rewriteFacetFieldList(ModifiableSolrParams fixed, SolrParams params, String paramName, - Map fieldMappings, SolrQueryRequest req) - { - String shardPurpose = req.getParams().get(ShardParams.SHARDS_PURPOSE); - boolean isRefinementRequest = false; - //Fix for https://issues.alfresco.com/jira/browse/MNT-21015 - if (shardPurpose != null) { - int shardPurposeCode = Integer.parseInt(shardPurpose); - log.debug("ShardPurpose: " + shardPurpose); - isRefinementRequest = ((shardPurposeCode & ShardRequest.PURPOSE_REFINE_FACETS) != 0) || ((shardPurposeCode & ShardRequest.PURPOSE_REFINE_PIVOT_FACETS) != 0); - } - String[] facetFieldsOrig = params.getParams(paramName); - List facetFieldList = new ArrayList<>(); - if(facetFieldsOrig != null) - { - ArrayList newFacetFields = new ArrayList(); - for(String facetFields : facetFieldsOrig) - { - StringBuilder commaSeparated = new StringBuilder(); - StringBuilder mapping = new StringBuilder(); - StringBuilder unmapped = new StringBuilder(); - - String[] fields = parseFacetField(facetFields); - - for(String field : fields) - { - String prefix = ""; - field = field.trim(); - - if(field.endsWith("()")) - { - // skip facet functions - continue; - } - - if(field.startsWith("{!") &&!(isRefinementRequest)) - { - int index = field.indexOf("}"); - if((index > 0) && (index < (field.length() - 1))) - { - prefix = field.substring(0, index+1); - field = field.substring(index+1); - } - } - - boolean noMappingIsRequired = req.getSchema().getFieldOrNull(field) != null|| isRefinementRequest; - if(noMappingIsRequired) - { - if(commaSeparated.length() > 0) - { - commaSeparated.append(","); - mapping.append(","); - unmapped.append(","); - } - commaSeparated.append(prefix).append(field); - mapping.append(field); - unmapped.append(field); - facetFieldList.add(field); - } - else - { - String mappedField = AlfrescoSolrDataModel.getInstance().mapProperty(field, FieldUse.FACET, req); - - if(commaSeparated.length() > 0) - { - commaSeparated.append(","); - mapping.append(","); - unmapped.append(","); - } - commaSeparated.append(prefix).append(mappedField); - mapping.append(mappedField); - unmapped.append(field); - facetFieldList.add(mappedField); - } - } - if(!facetFields.equals(commaSeparated.toString())) - { - fieldMappings.put(unmapped.toString(), mapping.toString()); - } - if(commaSeparated.length() > 0) - { - newFacetFields.add(commaSeparated.toString()); - } - } - fixed.set(paramName, newFacetFields.toArray(new String[newFacetFields.size()])); - } - - return facetFieldList; - } - - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void process(ResponseBuilder rb) throws IOException - { - - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getDescription() - */ - @Override - public String getDescription() - { - return "RewriteFacetParameters"; - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getSource() - */ - @Override - public String getSource() - { - return ""; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.HashBiMap; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.alfresco.solr.query.MimetypeGroupingQParserPlugin; +import org.apache.commons.lang.StringUtils; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.handler.component.ShardRequest; +import org.apache.solr.request.SolrQueryRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Update facet parameters to reference Solr fields rather than ACS properties. + * + * @author Andy + */ +public class RewriteFacetParametersComponent extends SearchComponent +{ + private static final Logger log = LoggerFactory.getLogger(RewriteFacetParametersComponent.class); + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + SolrQueryRequest req = rb.req; + SolrParams params = req.getParams(); + + ModifiableSolrParams fixed = new ModifiableSolrParams(); + ModifiableSolrParams allParamsWithFix = new ModifiableSolrParams(params); + fixFilterQueries(fixed, params, rb); + fixFacetParams(fixed, params, rb); + fixRows(fixed, params, rb); + + Set fixedParameterNames = fixed.getParameterNames(); + for (String fixedParam : fixedParameterNames) + { + allParamsWithFix.set(fixedParam, fixed.getParams(fixedParam)); + } + + if (allParamsWithFix.get(CommonParams.SORT) != null) + { + allParamsWithFix.remove(CommonParams.RQ); + } + + req.setParams(allParamsWithFix); + } + + /** + * Prevents users from requesting a large number of rows by + * replacing an enormous row value with a maximum value that will + * not cause a run time exception. + * @param fixed + * @param params + * @param rb + */ + private void fixRows(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) + { + String rows = params.get("rows"); + if(rows != null && !rows.isEmpty()) + { + Integer row = Integer.valueOf(rows); + // Avoid +1 in SOLR code which produces null:java.lang.NegativeArraySizeException at at org.apache.lucene.util.PriorityQueue.(PriorityQueue.java:56) + if(row > 1000000) + { + fixed.remove("rows"); + fixed.add("rows", "1000000"); + } + } + } + private void fixFilterQueries(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) + { + for(Iterator it = params.getParameterNamesIterator(); it.hasNext(); /**/) + { + String name = it.next(); + + if(name.equals("fq")) + { + String[] values = params.getParams(name); + if(values != null) + { + String[] fixedValues = new String[values.length]; + for(int i = 0; i < values.length; i++) + { + String value = values[i]; + if(value.startsWith("{!")) + { + fixedValues[i] = value; + } + else + { + if(value.startsWith("contentSize():")) + { + value = "cm:content.size:" + removeQuotes(value.substring("contentSize():".length())); + } + else if(value.startsWith("mimetype():")) + { + value = removeQuotes(value.substring("mimetype():".length())); + ArrayList expand = MimetypeGroupingQParserPlugin.getReverseMappings().get(value); + if(expand == null) + { + value = "cm:content.mimetype:\""+value+"\""; + } + else + { + StringBuilder builder = new StringBuilder(); + builder.append("cm:content.mimetype:("); + for(int j = 0; j < expand.size(); j++) + { + if(j > 0) + { + builder.append(" OR "); + } + builder.append('"'); + builder.append(expand.get(j)); + builder.append('"'); + } + builder.append(')'); + value = builder.toString(); + + } + } + fixedValues[i] = "{!afts}"+value; + } + } + fixed.add(name, fixedValues); + } + + } + } + } + + private String removeQuotes(String quoted) + { + if(quoted.startsWith("\"") && quoted.endsWith("\"")) + { + return quoted.substring(1, quoted.length()-1); + } + else + { + return quoted; + } + } + + + /** + * @param fixed ModifiableSolrParams + * @param params SolrParams + * @param rb ResponseBuilder + * @return SolrParams + */ + private SolrParams fixFacetParams(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) + { + // Use BiMaps so that these can be quickly inverted in RewriteFacetCountsComponent. + Map fieldMappings = HashBiMap.create(); + Map dateMappings = HashBiMap.create(); + Map rangeMappings = HashBiMap.create(); + Map pivotMappings = HashBiMap.create(); + Map intervalMappings = HashBiMap.create(); + + Map statsFieldMappings = HashBiMap.create(); + Map statsFacetMappings = HashBiMap.create(); + + Map functionMappings = HashBiMap.create(); + + rewriteFacetFieldList(fixed, params, "facet.field", fieldMappings, rb.req); + rewriteFacetFieldList(fixed, params, "facet.date", dateMappings, rb.req); + rewriteFacetFieldList(fixed, params, "facet.range", rangeMappings, rb.req); + rewriteFacetFieldList(fixed, params, "facet.pivot", pivotMappings, rb.req); + rewriteFacetFieldList(fixed, params, "facet.interval", intervalMappings, rb.req); + + rewriteFacetFieldList(fixed, params, "stats.field", statsFieldMappings, rb.req); + rewriteFacetFieldList(fixed, params, "stats.facet", statsFacetMappings, rb.req); + + mapFacetFunctions(fixed, params, "facet.field", functionMappings); + + rewriteFacetFieldOptions(fixed, params, "facet.prefix", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.contains", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.contains.ignoreCase", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.sort", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.limit", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.offset", fieldMappings); + rewriteMincountFacetFieldOption(fixed, params, "facet.mincount", fieldMappings,rb.req); + rewriteFacetFieldOptions(fixed, params, "facet.missing", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.method", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.enum.cache.minDF", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.enum.cache.minDF", fieldMappings); + + rewriteFacetFieldOptions(fixed, params, "facet.range.start", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.end", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.gap", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.hardend", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.include", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.other", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.method", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.limit", rangeMappings); + + rewriteMincountFacetFieldOption(fixed, params, "facet.pivot.mincount", pivotMappings, rb.req); + rewriteFacetFieldOptions(fixed, params, "facet.sort", pivotMappings); + rewriteFacetFieldOptions(fixed, params, "facet.limit", pivotMappings); + rewriteFacetFieldOptions(fixed, params, "facet.offset", pivotMappings); + + rewriteFacetFieldOptions(fixed, params, "facet.interval.set", intervalMappings); + + + + // TODO: + // f..stats.facet= + // would require a more complex rewrite + + + + rb.rsp.add("_original_parameters_", params); + rb.rsp.add("_field_mappings_", fieldMappings); + rb.rsp.add("_date_mappings_", dateMappings); + rb.rsp.add("_range_mappings_", rangeMappings); + rb.rsp.add("_pivot_mappings_", pivotMappings); + rb.rsp.add("_interval_mappings_", intervalMappings); + rb.rsp.add("_stats_field_mappings_", statsFieldMappings); + rb.rsp.add("_stats_facet_mappings_", statsFacetMappings); + rb.rsp.add("_facet_function_mappings_", functionMappings); + + return fixed; + } + + + /** + * Populate a map with the mappings from functions in the query to functions including the Solr filter handler. + * + * @param fixed The updated params object. + * @param params The original params object. + * @param paramName The name of the parameter (e.g. "facet.field"). + * @param facetFunctionMappings The map to update. + */ + private void mapFacetFunctions(ModifiableSolrParams fixed, SolrParams params, String paramName, Map facetFunctionMappings) + { + String[] facetFieldsOrig = params.getParams(paramName); + if(facetFieldsOrig != null) + { + for(String facetFields : facetFieldsOrig) + { + String[] fields = facetFields.split(","); + + for(String field : fields) + { + field = field.trim(); + if(field.endsWith("()")) + { + if(isMimetypeAndHasFQ(params, field)) + { + String function = "{!" + field.substring(0, field.length()-2)+ " group=false }"; + fixed.add("fq", function); + facetFunctionMappings.put(field, function); + } + else + { + String function = "{!" + field.substring(0, field.length()-2)+ " group=true}"; + fixed.add("fq", function); + facetFunctionMappings.put(field, function); + } + } + } + } + } + } + + + /** + * @param params + * @param field + * @return + */ + private boolean isMimetypeAndHasFQ(SolrParams params, String field) + { + if(!field.equals("mimetype()")) + { + return false; + } + else + { + String[] filterQueries = params.getParams("fq"); + if(filterQueries != null) + { + for(String fq : filterQueries) + { + if(fq.startsWith("mimetype():")) + { + return true; + } + } + } + } + return false; + } + + /** + * Ensure the mincount for all given facets is at least 1 to prevent exposing sensitive buckets to users without permission. + * + * @param fixed The updated params object. + * @param params The original params object. + * @param paramName The name of the mincount parameter to rewrite (e.g. "facet.mincount" or "facet.pivot.mincount"). + * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. + * @param req The Solr request + */ + protected void rewriteMincountFacetFieldOption(ModifiableSolrParams fixed, SolrParams params, String paramName, Map fieldMappings, SolrQueryRequest req) + { + rewriteFacetFieldOptions(fixed, params, paramName, fieldMappings); + + String shardPurpose = req.getParams().get(ShardParams.SHARDS_PURPOSE); + boolean isInitialDistributedRequest = (shardPurpose==null); + /* + * After the initial Search request, in case of Sharding the Solr node will build additional http requests to the other shards. + * These requests may have specific parameters rewritten by Solr internally. + * It's not recommended to rewrite them, because some side effect may happen. + * Only the initial request parameters may be rewritten. + * */ + if(isInitialDistributedRequest) + { + // Update any existing mincount entries for facets. + Map updatedValues = new HashMap<>(); + for (Iterator renamedParameters = fixed.getParameterNamesIterator(); renamedParameters.hasNext(); ) + { + String solrParameter = renamedParameters.next(); + if (solrParameter.endsWith("." + paramName)) + { + // If mincount is set then renamedParameters must be an integer. + int value = Integer.valueOf(fixed.get(solrParameter)); + // Don't directly edit the params while we're iterating through the entries. + updatedValues.put(solrParameter, Math.max(value, 1)); + } + } + // Now we've finished iterating, update the fixed parameters. + for (Map.Entry updatedParameterValues : updatedValues.entrySet()) + { + fixed.set(updatedParameterValues.getKey(), updatedParameterValues.getValue()); + } + + String paramValue = params.get(paramName); + int value = 0; + if (paramValue != null) + { + value = Integer.valueOf(paramValue); + } + fixed.set(paramName, Math.max(value, 1)); + } + } + + /** + * Update any existing facet parameters to use the Solr field names. + * + * @param fixed The updated params object. + * @param params The original params object. + * @param paramName The name of the parameter to rewrite (e.g. "facet.limit" or "f.fieldName.facet.limit"). + * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. + */ + private void rewriteFacetFieldOptions(ModifiableSolrParams fixed, SolrParams params, String paramName, Map fieldMappings) + { + for(Iterator originalParamsIterator = params.getParameterNamesIterator(); originalParamsIterator.hasNext(); /**/) + { + String originalSolrParam = originalParamsIterator.next(); //it contains the alfresco field name + if(originalSolrParam.startsWith("f.")) + { + if(originalSolrParam.endsWith("."+paramName)) + { + String originalAlfrescoFieldName = originalSolrParam.substring(2, originalSolrParam.length() - paramName.length() - 1); + if(fieldMappings.containsKey(originalAlfrescoFieldName)) + { + String solrFieldName = fieldMappings.get(originalAlfrescoFieldName); + fixed.set("f."+ solrFieldName +"."+paramName, params.getParams(originalSolrParam)); + } + else + { + fixed.set(originalSolrParam, params.getParams(originalSolrParam)); + } + } + } + } + } + + /** + * Tokenizes a string based on comma's except for the ones in single or double + * qoutes. + * @param line + * @return + */ + public static String[] parseFacetField(String line) + { + if(StringUtils.isEmpty(line)) + { + throw new RuntimeException("String input is requried"); + } + String[] tokens = line.split(",(?=(?:[^'|\"]*\"[^'|\"]*\")*[^'|\"]*$)", -1); + return tokens; + + } + /** + * Update the facet fields to use Solr field names rather than ACS property names. + * + * @param fixed The updated params object. + * @param params The original params object. + * @param paramName The name of the mincount parameter to rewrite (e.g. "facet.mincount" or "facet.pivot.mincount"). + * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. + * @param req The original request. + * @return An array of the facet field names. + */ + private List rewriteFacetFieldList(ModifiableSolrParams fixed, SolrParams params, String paramName, + Map fieldMappings, SolrQueryRequest req) + { + String shardPurpose = req.getParams().get(ShardParams.SHARDS_PURPOSE); + boolean isRefinementRequest = false; + //Fix for https://issues.alfresco.com/jira/browse/MNT-21015 + if (shardPurpose != null) { + int shardPurposeCode = Integer.parseInt(shardPurpose); + log.debug("ShardPurpose: " + shardPurpose); + isRefinementRequest = ((shardPurposeCode & ShardRequest.PURPOSE_REFINE_FACETS) != 0) || ((shardPurposeCode & ShardRequest.PURPOSE_REFINE_PIVOT_FACETS) != 0); + } + String[] facetFieldsOrig = params.getParams(paramName); + List facetFieldList = new ArrayList<>(); + if(facetFieldsOrig != null) + { + ArrayList newFacetFields = new ArrayList(); + for(String facetFields : facetFieldsOrig) + { + StringBuilder commaSeparated = new StringBuilder(); + StringBuilder mapping = new StringBuilder(); + StringBuilder unmapped = new StringBuilder(); + + String[] fields = parseFacetField(facetFields); + + for(String field : fields) + { + String prefix = ""; + field = field.trim(); + + if(field.endsWith("()")) + { + // skip facet functions + continue; + } + + if(field.startsWith("{!") &&!(isRefinementRequest)) + { + int index = field.indexOf("}"); + if((index > 0) && (index < (field.length() - 1))) + { + prefix = field.substring(0, index+1); + field = field.substring(index+1); + } + } + + boolean noMappingIsRequired = req.getSchema().getFieldOrNull(field) != null|| isRefinementRequest; + if(noMappingIsRequired) + { + if(commaSeparated.length() > 0) + { + commaSeparated.append(","); + mapping.append(","); + unmapped.append(","); + } + commaSeparated.append(prefix).append(field); + mapping.append(field); + unmapped.append(field); + facetFieldList.add(field); + } + else + { + String mappedField = AlfrescoSolrDataModel.getInstance().mapProperty(field, FieldUse.FACET, req); + + if(commaSeparated.length() > 0) + { + commaSeparated.append(","); + mapping.append(","); + unmapped.append(","); + } + commaSeparated.append(prefix).append(mappedField); + mapping.append(mappedField); + unmapped.append(field); + facetFieldList.add(mappedField); + } + } + if(!facetFields.equals(commaSeparated.toString())) + { + fieldMappings.put(unmapped.toString(), mapping.toString()); + } + if(commaSeparated.length() > 0) + { + newFacetFields.add(commaSeparated.toString()); + } + } + fixed.set(paramName, newFacetFields.toArray(new String[newFacetFields.size()])); + } + + return facetFieldList; + } + + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void process(ResponseBuilder rb) throws IOException + { + + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getDescription() + */ + @Override + public String getDescription() + { + return "RewriteFacetParameters"; + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getSource() + */ + @Override + public String getSource() + { + return ""; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java index 345494ec4..a876bf1c9 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetLocaleComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetLocaleComponent.java index 672186e40..d95301850 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetLocaleComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetLocaleComponent.java @@ -1,89 +1,89 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component; - -import java.io.IOException; -import java.util.Locale; - -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.apache.solr.request.SolrQueryRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * @author Andy - * - */ -public class SetLocaleComponent extends SearchComponent -{ - private static final Logger log = LoggerFactory.getLogger(SetLocaleComponent.class); - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - SolrQueryRequest req = rb.req; - SolrParams params = req.getParams(); - String localeStr = params.get("locale"); - Locale locale = I18NUtil.parseLocale(localeStr); - I18NUtil.setLocale(locale); - log.info("Set locale to " + localeStr); - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void process(ResponseBuilder rb) throws IOException - { - // Nothing to do - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getDescription() - */ - @Override - public String getDescription() - { - return "setLocale"; - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getSource() - */ - @Override - public String getSource() - { - return ""; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component; + +import java.io.IOException; +import java.util.Locale; + +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.request.SolrQueryRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + * + */ +public class SetLocaleComponent extends SearchComponent +{ + private static final Logger log = LoggerFactory.getLogger(SetLocaleComponent.class); + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + SolrQueryRequest req = rb.req; + SolrParams params = req.getParams(); + String localeStr = params.get("locale"); + Locale locale = I18NUtil.parseLocale(localeStr); + I18NUtil.setLocale(locale); + log.info("Set locale to " + localeStr); + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void process(ResponseBuilder rb) throws IOException + { + // Nothing to do + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getDescription() + */ + @Override + public String getDescription() + { + return "setLocale"; + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getSource() + */ + @Override + public String getSource() + { + return ""; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetProcessedDeniesComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetProcessedDeniesComponent.java index addba8b18..80df24bc0 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetProcessedDeniesComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetProcessedDeniesComponent.java @@ -1,87 +1,87 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component; - -import java.io.IOException; - -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; - -/** - * Sets a boolean flag ("processedDenies") in the JSON response indicating that - * the results (should) have been processed with respect to anyDenyDenies - * (i.e. {@link org.alfresco.solr.query.AbstractQParser} has added the correct clause to the search query). - * - * @author Matt Ward - */ -public class SetProcessedDeniesComponent extends SearchComponent -{ - public static final String PROCESSED_DENIES = "processedDenies"; - - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - // No preparation required - } - - @Override - public void process(ResponseBuilder rb) throws IOException - { - Boolean processedDenies = (Boolean) rb.req.getContext().get(PROCESSED_DENIES); - processedDenies = (processedDenies == null) ? false : processedDenies; - rb.rsp.add(PROCESSED_DENIES, processedDenies); - } - - @Override - public String getDescription() - { - return "Adds the processedDenies boolean flag to the search results."; - } - - @Override - public String getSource() - { - return "http://www.alfresco.com"; - } - - @Override - public String getVersion() - { - return "1.0"; - } - - @Override - public void finishStage(ResponseBuilder rb) { - if (rb.stage != ResponseBuilder.STAGE_GET_FIELDS) { - return; - } - - Boolean processedDenies = (Boolean) rb.req.getContext().get(PROCESSED_DENIES); - processedDenies = (processedDenies == null) ? false : processedDenies; - rb.rsp.add(PROCESSED_DENIES, processedDenies); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component; + +import java.io.IOException; + +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; + +/** + * Sets a boolean flag ("processedDenies") in the JSON response indicating that + * the results (should) have been processed with respect to anyDenyDenies + * (i.e. {@link org.alfresco.solr.query.AbstractQParser} has added the correct clause to the search query). + * + * @author Matt Ward + */ +public class SetProcessedDeniesComponent extends SearchComponent +{ + public static final String PROCESSED_DENIES = "processedDenies"; + + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + // No preparation required + } + + @Override + public void process(ResponseBuilder rb) throws IOException + { + Boolean processedDenies = (Boolean) rb.req.getContext().get(PROCESSED_DENIES); + processedDenies = (processedDenies == null) ? false : processedDenies; + rb.rsp.add(PROCESSED_DENIES, processedDenies); + } + + @Override + public String getDescription() + { + return "Adds the processedDenies boolean flag to the search results."; + } + + @Override + public String getSource() + { + return "http://www.alfresco.com"; + } + + @Override + public String getVersion() + { + return "1.0"; + } + + @Override + public void finishStage(ResponseBuilder rb) { + if (rb.stage != ResponseBuilder.STAGE_GET_FIELDS) { + return; + } + + Boolean processedDenies = (Boolean) rb.req.getContext().get(PROCESSED_DENIES); + processedDenies = (processedDenies == null) ? false : processedDenies; + rb.rsp.add(PROCESSED_DENIES, processedDenies); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/TempFileWarningLogger.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/TempFileWarningLogger.java index 21a4a8cfa..1c69ba762 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/TempFileWarningLogger.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/TempFileWarningLogger.java @@ -1,109 +1,109 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component; - -import java.io.IOException; - -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; - -import org.slf4j.Logger; -import org.springframework.util.StringUtils; - -import edu.umd.cs.findbugs.annotations.SuppressWarnings; - -/** - * Temp files may take up a lot of space, warn administrators - * of their existence, giving them the chance to manage them. - * - * @author Matt Ward - */ -public class TempFileWarningLogger -{ - private final Logger log; - private final Path dir; - private final String glob; - - public TempFileWarningLogger(Logger log, String prefix, String[] extensions, Path dir) - { - this.log = log; - this.dir = dir; - // Match files with glob, e.g. "SomePrefix*.{tmp,~bak}" - glob = prefix + ".{"+ StringUtils.arrayToCommaDelimitedString(extensions) + "}"; - } - - // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) - @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") - public boolean checkFiles() - { - if (log.isDebugEnabled()) - { - log.debug("Looking for temp files matching " + glob + " in directory " + dir); - } - - try(DirectoryStream stream = Files.newDirectoryStream(dir, glob)) - { - for (Path file : stream) - { - if (log.isDebugEnabled()) - { - log.debug("Solr suggester temp file found matching file pattern: " + glob + ", path: " + file); - log.debug("Removing suggester temp files."); - } - return true; - } - return false; - } - catch (IOException e) - { - throw new RuntimeException("Unable to create directory stream", e); - } - } - - // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) - @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") - public void removeFiles() - { - try(DirectoryStream stream = Files.newDirectoryStream(dir, glob)) - { - for (Path file : stream) - { - file.toFile().delete(); - } - } - catch (IOException e) - { - log.debug("Unable to delete temp file", e); - } - } - - protected String getGlob() - { - return glob; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component; + +import java.io.IOException; + +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.slf4j.Logger; +import org.springframework.util.StringUtils; + +import edu.umd.cs.findbugs.annotations.SuppressWarnings; + +/** + * Temp files may take up a lot of space, warn administrators + * of their existence, giving them the chance to manage them. + * + * @author Matt Ward + */ +public class TempFileWarningLogger +{ + private final Logger log; + private final Path dir; + private final String glob; + + public TempFileWarningLogger(Logger log, String prefix, String[] extensions, Path dir) + { + this.log = log; + this.dir = dir; + // Match files with glob, e.g. "SomePrefix*.{tmp,~bak}" + glob = prefix + ".{"+ StringUtils.arrayToCommaDelimitedString(extensions) + "}"; + } + + // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) + @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") + public boolean checkFiles() + { + if (log.isDebugEnabled()) + { + log.debug("Looking for temp files matching " + glob + " in directory " + dir); + } + + try(DirectoryStream stream = Files.newDirectoryStream(dir, glob)) + { + for (Path file : stream) + { + if (log.isDebugEnabled()) + { + log.debug("Solr suggester temp file found matching file pattern: " + glob + ", path: " + file); + log.debug("Removing suggester temp files."); + } + return true; + } + return false; + } + catch (IOException e) + { + throw new RuntimeException("Unable to create directory stream", e); + } + } + + // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) + @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") + public void removeFiles() + { + try(DirectoryStream stream = Files.newDirectoryStream(dir, glob)) + { + for (Path file : stream) + { + file.toFile().delete(); + } + } + catch (IOException e) + { + log.debug("Unable to delete temp file", e); + } + } + + protected String getGlob() + { + return glob; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckBackCompatComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckBackCompatComponent.java index ba7531da1..a60658681 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckBackCompatComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckBackCompatComponent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollation.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollation.java index eee5b97df..f17cc57a9 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollation.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollation.java @@ -1,55 +1,55 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component.spellcheck; - -import org.apache.solr.spelling.SpellCheckCollation; - -/** - * @author Jamal Kaabi-Mofrad - * @since 5.0 - */ -public class AlfrescoSpellCheckCollation extends SpellCheckCollation -{ - private String collationQueryString; - - /** - * @return the collationQueryString - */ - public String getCollationQueryString() - { - return this.collationQueryString; - } - - /** - * @param collationQueryString the collationQueryString to set - */ - public void setCollationQueryString(String collationQueryString) - { - this.collationQueryString = collationQueryString; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component.spellcheck; + +import org.apache.solr.spelling.SpellCheckCollation; + +/** + * @author Jamal Kaabi-Mofrad + * @since 5.0 + */ +public class AlfrescoSpellCheckCollation extends SpellCheckCollation +{ + private String collationQueryString; + + /** + * @return the collationQueryString + */ + public String getCollationQueryString() + { + return this.collationQueryString; + } + + /** + * @param collationQueryString the collationQueryString to set + */ + public void setCollationQueryString(String collationQueryString) + { + this.collationQueryString = collationQueryString; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollator.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollator.java index a6b277433..38bd8171a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollator.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollator.java @@ -1,369 +1,369 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component.spellcheck; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.alfresco.solr.query.AbstractQParser; -import org.apache.lucene.analysis.Token; -import org.apache.lucene.index.IndexReader; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.DisMaxParams; -import org.apache.solr.common.params.GroupParams; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.params.SpellingParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.handler.component.QueryComponent; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.search.EarlyTerminatingCollectorException; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.spelling.PossibilityIterator; -import org.apache.solr.spelling.QueryConverter; -import org.apache.solr.spelling.SpellCheckCorrection; -import org.apache.solr.spelling.SpellingResult; -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Disclaimer: The code copied from - * {@link org.apache.solr.spelling.SpellCheckCollator} but only modified the - * {@link org.apache.solr.spelling.SpellCheckCollator#collate(SpellingResult, String, ResponseBuilder)} - * method to pass the suggested term to the original alfresco JSON request rather - * than via 'q' parameter. - * - * @author Jamal Kaabi-Mofrad - * @since 5.0 - */ -public class AlfrescoSpellCheckCollator -{ - private final static Logger LOG = LoggerFactory.getLogger(AlfrescoSpellCheckCollator.class); - - private int maxCollations = 1; - private int maxCollationTries = 0; - private int maxCollationEvaluations = 10000; - private boolean suggestionsMayOverlap = false; - private int docCollectionLimit = 0; - - public List collate(SpellingResult result, String originalQuery, - ResponseBuilder ultimateResponse) - { - List collations = new ArrayList<>(); - - QueryComponent queryComponent = null; - if (ultimateResponse.components != null) - { - for (SearchComponent sc : ultimateResponse.components) - { - if (sc instanceof QueryComponent) - { - queryComponent = (QueryComponent) sc; - break; - } - } - } - - boolean verifyCandidateWithQuery = true; - int maxTries = maxCollationTries; - int maxNumberToIterate = maxTries; - if (maxTries < 1) - { - maxTries = 1; - maxNumberToIterate = maxCollations; - verifyCandidateWithQuery = false; - } - if (queryComponent == null && verifyCandidateWithQuery) - { - LOG.info("Could not find an instance of QueryComponent. Disabling collation verification against the index."); - maxTries = 1; - verifyCandidateWithQuery = false; - } - docCollectionLimit = docCollectionLimit > 0 ? docCollectionLimit : 0; - int maxDocId = -1; - if (verifyCandidateWithQuery && docCollectionLimit > 0) - { - IndexReader reader = ultimateResponse.req.getSearcher().getIndexReader(); - maxDocId = reader.maxDoc(); - } - - JSONObject alfrescoJSON = (JSONObject) ultimateResponse.req.getContext().get(AbstractQParser.ALFRESCO_JSON); - String originalAftsQuery = alfrescoJSON != null? alfrescoJSON.getString("query") : ultimateResponse.getQueryString(); - - - int tryNo = 0; - int collNo = 0; - PossibilityIterator possibilityIter = new PossibilityIterator(result.getSuggestions(), maxNumberToIterate, - maxCollationEvaluations, suggestionsMayOverlap); - while (tryNo < maxTries && collNo < maxCollations && possibilityIter.hasNext()) - { - PossibilityIterator.RankedSpellPossibility possibility = possibilityIter.next(); - String collationQueryStr = getCollation(originalQuery, possibility.corrections); - int hits = 0; - String aftsQuery = null; - - if (verifyCandidateWithQuery) - { - tryNo++; - SolrQueryRequest req = ultimateResponse.req; - SolrParams origParams = req.getParams(); - ModifiableSolrParams params = new ModifiableSolrParams(origParams); - Iterator origParamIterator = origParams.getParameterNamesIterator(); - int pl = SpellingParams.SPELLCHECK_COLLATE_PARAM_OVERRIDE.length(); - while (origParamIterator.hasNext()) - { - String origParamName = origParamIterator.next(); - if (origParamName.startsWith(SpellingParams.SPELLCHECK_COLLATE_PARAM_OVERRIDE) - && origParamName.length() > pl) - { - String[] val = origParams.getParams(origParamName); - if (val.length == 1 && val[0].length() == 0) - { - params.set(origParamName.substring(pl), (String[]) null); - } - else - { - params.set(origParamName.substring(pl), val); - } - } - } - // we don't set the 'q' param, as we'll pass the query via JSON. - // params.set(CommonParams.Q, collationQueryStr); - params.remove(CommonParams.START); - params.set(CommonParams.ROWS, "" + docCollectionLimit); - // we don't want any stored fields - params.set(CommonParams.FL, "id"); - // we'll sort by doc id to ensure no scoring is done. - params.set(CommonParams.SORT, "_docid_ asc"); - // If a dismax query, don't add unnecessary clauses for scoring - params.remove(DisMaxParams.TIE); - params.remove(DisMaxParams.PF); - params.remove(DisMaxParams.PF2); - params.remove(DisMaxParams.PF3); - params.remove(DisMaxParams.BQ); - params.remove(DisMaxParams.BF); - // Collate testing does not support Grouping (see SOLR-2577) - params.remove(GroupParams.GROUP); - - boolean useQStr = true; - - if (alfrescoJSON != null) - { - try - { - aftsQuery = originalAftsQuery.replaceAll(Pattern.quote(originalQuery), Matcher.quoteReplacement(collationQueryStr)); - alfrescoJSON.put("query", aftsQuery); - req.getContext().put(AbstractQParser.ALFRESCO_JSON, alfrescoJSON); - useQStr = false; - } - catch (JSONException e) - { - LOG.warn("Exception trying to get/set the query from/to ALFRESCO_JSON.]" + e); - } - } - else - { - aftsQuery = collationQueryStr; - } - req.setParams(params); - // creating a request here... make sure to close it! - ResponseBuilder checkResponse = new ResponseBuilder(req, new SolrQueryResponse(), - Arrays. asList(queryComponent)); - checkResponse.setQparser(ultimateResponse.getQparser()); - checkResponse.setFilters(ultimateResponse.getFilters()); - checkResponse.components = Arrays. asList(queryComponent); - if (useQStr) - { - checkResponse.setQueryString(collationQueryStr); - } - try - { - queryComponent.prepare(checkResponse); - if (docCollectionLimit > 0) - { - int f = checkResponse.getFieldFlags(); - checkResponse.setFieldFlags(f |= SolrIndexSearcher.TERMINATE_EARLY); - } - queryComponent.process(checkResponse); - hits = (Integer) checkResponse.rsp.getToLog().get("hits"); - } - catch (EarlyTerminatingCollectorException etce) - { - assert (docCollectionLimit > 0); - assert 0 < etce.getNumberScanned(); - assert 0 < etce.getNumberCollected(); - - if (etce.getNumberScanned() == maxDocId) - { - hits = etce.getNumberCollected(); - } - else - { - hits = (int) (((float) (maxDocId * etce.getNumberCollected())) / (float) etce - .getNumberScanned()); - } - } - catch (Exception e) - { - LOG.warn("Exception trying to re-query to check if a spell check possibility would return any hits." + e); - } - finally - { - checkResponse.req.close(); - } - } - if (hits > 0 || !verifyCandidateWithQuery) - { - collNo++; - AlfrescoSpellCheckCollation collation = new AlfrescoSpellCheckCollation(); - collation.setCollationQuery(aftsQuery); - collation.setCollationQueryString(collationQueryStr); - collation.setHits(hits); - collation.setInternalRank(suggestionsMayOverlap ? ((possibility.rank * 1000) + possibility.index) - : possibility.rank); - - NamedList misspellingsAndCorrections = new NamedList<>(); - for (SpellCheckCorrection corr : possibility.corrections) - { - misspellingsAndCorrections.add(corr.getOriginal().toString(), corr.getCorrection()); - } - collation.setMisspellingsAndCorrections(misspellingsAndCorrections); - collations.add(collation); - } - if (LOG.isDebugEnabled()) - { - LOG.debug("Collation: " + aftsQuery + (verifyCandidateWithQuery ? (" will return " + hits + " hits.") : "")); - } - } - return collations; - } - - @SuppressWarnings("deprecation") - private String getCollation(String origQuery, List corrections) - { - StringBuilder collation = new StringBuilder(origQuery); - int offset = 0; - String corr = ""; - for (int i = 0; i < corrections.size(); i++) - { - SpellCheckCorrection correction = corrections.get(i); - Token tok = correction.getOriginal(); - // we are replacing the query in order, but injected terms might - // cause illegal offsets due to previous replacements. - if (tok.getPositionIncrement() == 0) - continue; - corr = correction.getCorrection(); - boolean addParenthesis = false; - Character requiredOrProhibited = null; - int indexOfSpace = corr.indexOf(' '); - StringBuilder corrSb = new StringBuilder(corr); - int bump = 1; - - // If the correction contains whitespace (because it involved - // breaking a word in 2+ words), - // then be sure all of the new words have the same - // optional/required/prohibited status in the query. - while (indexOfSpace > -1 && indexOfSpace < corr.length() - 1) - { - addParenthesis = true; - char previousChar = tok.startOffset() > 0 ? origQuery.charAt(tok.startOffset() - 1) : ' '; - if (previousChar == '-' || previousChar == '+') - { - corrSb.insert(indexOfSpace + bump, previousChar); - if (requiredOrProhibited == null) - { - requiredOrProhibited = previousChar; - } - bump++; - } - else if ((tok.getFlags() & QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG) == QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG) - { - corrSb.insert(indexOfSpace + bump, "AND "); - bump += 4; - } - indexOfSpace = correction.getCorrection().indexOf(' ', indexOfSpace + bump); - } - - int oneForReqOrProhib = 0; - if (addParenthesis) - { - if (requiredOrProhibited != null) - { - corrSb.insert(0, requiredOrProhibited); - oneForReqOrProhib++; - } - corrSb.insert(0, '('); - corrSb.append(')'); - } - corr = corrSb.toString(); - int startIndex = tok.startOffset() + offset - oneForReqOrProhib; - int endIndex = tok.endOffset() + offset; - collation.replace(startIndex, endIndex, corr); - offset += corr.length() - oneForReqOrProhib - (tok.endOffset() - tok.startOffset()); - } - return collation.toString(); - } - - public AlfrescoSpellCheckCollator setMaxCollations(int maxCollations) - { - this.maxCollations = maxCollations; - return this; - } - - public AlfrescoSpellCheckCollator setMaxCollationTries(int maxCollationTries) - { - this.maxCollationTries = maxCollationTries; - return this; - } - - public AlfrescoSpellCheckCollator setMaxCollationEvaluations(int maxCollationEvaluations) - { - this.maxCollationEvaluations = maxCollationEvaluations; - return this; - } - - public AlfrescoSpellCheckCollator setSuggestionsMayOverlap(boolean suggestionsMayOverlap) - { - this.suggestionsMayOverlap = suggestionsMayOverlap; - return this; - } - - public AlfrescoSpellCheckCollator setDocCollectionLimit(int docCollectionLimit) - { - this.docCollectionLimit = docCollectionLimit; - return this; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component.spellcheck; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.alfresco.solr.query.AbstractQParser; +import org.apache.lucene.analysis.Token; +import org.apache.lucene.index.IndexReader; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.DisMaxParams; +import org.apache.solr.common.params.GroupParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.params.SpellingParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.component.QueryComponent; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.search.EarlyTerminatingCollectorException; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.spelling.PossibilityIterator; +import org.apache.solr.spelling.QueryConverter; +import org.apache.solr.spelling.SpellCheckCorrection; +import org.apache.solr.spelling.SpellingResult; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Disclaimer: The code copied from + * {@link org.apache.solr.spelling.SpellCheckCollator} but only modified the + * {@link org.apache.solr.spelling.SpellCheckCollator#collate(SpellingResult, String, ResponseBuilder)} + * method to pass the suggested term to the original alfresco JSON request rather + * than via 'q' parameter. + * + * @author Jamal Kaabi-Mofrad + * @since 5.0 + */ +public class AlfrescoSpellCheckCollator +{ + private final static Logger LOG = LoggerFactory.getLogger(AlfrescoSpellCheckCollator.class); + + private int maxCollations = 1; + private int maxCollationTries = 0; + private int maxCollationEvaluations = 10000; + private boolean suggestionsMayOverlap = false; + private int docCollectionLimit = 0; + + public List collate(SpellingResult result, String originalQuery, + ResponseBuilder ultimateResponse) + { + List collations = new ArrayList<>(); + + QueryComponent queryComponent = null; + if (ultimateResponse.components != null) + { + for (SearchComponent sc : ultimateResponse.components) + { + if (sc instanceof QueryComponent) + { + queryComponent = (QueryComponent) sc; + break; + } + } + } + + boolean verifyCandidateWithQuery = true; + int maxTries = maxCollationTries; + int maxNumberToIterate = maxTries; + if (maxTries < 1) + { + maxTries = 1; + maxNumberToIterate = maxCollations; + verifyCandidateWithQuery = false; + } + if (queryComponent == null && verifyCandidateWithQuery) + { + LOG.info("Could not find an instance of QueryComponent. Disabling collation verification against the index."); + maxTries = 1; + verifyCandidateWithQuery = false; + } + docCollectionLimit = docCollectionLimit > 0 ? docCollectionLimit : 0; + int maxDocId = -1; + if (verifyCandidateWithQuery && docCollectionLimit > 0) + { + IndexReader reader = ultimateResponse.req.getSearcher().getIndexReader(); + maxDocId = reader.maxDoc(); + } + + JSONObject alfrescoJSON = (JSONObject) ultimateResponse.req.getContext().get(AbstractQParser.ALFRESCO_JSON); + String originalAftsQuery = alfrescoJSON != null? alfrescoJSON.getString("query") : ultimateResponse.getQueryString(); + + + int tryNo = 0; + int collNo = 0; + PossibilityIterator possibilityIter = new PossibilityIterator(result.getSuggestions(), maxNumberToIterate, + maxCollationEvaluations, suggestionsMayOverlap); + while (tryNo < maxTries && collNo < maxCollations && possibilityIter.hasNext()) + { + PossibilityIterator.RankedSpellPossibility possibility = possibilityIter.next(); + String collationQueryStr = getCollation(originalQuery, possibility.corrections); + int hits = 0; + String aftsQuery = null; + + if (verifyCandidateWithQuery) + { + tryNo++; + SolrQueryRequest req = ultimateResponse.req; + SolrParams origParams = req.getParams(); + ModifiableSolrParams params = new ModifiableSolrParams(origParams); + Iterator origParamIterator = origParams.getParameterNamesIterator(); + int pl = SpellingParams.SPELLCHECK_COLLATE_PARAM_OVERRIDE.length(); + while (origParamIterator.hasNext()) + { + String origParamName = origParamIterator.next(); + if (origParamName.startsWith(SpellingParams.SPELLCHECK_COLLATE_PARAM_OVERRIDE) + && origParamName.length() > pl) + { + String[] val = origParams.getParams(origParamName); + if (val.length == 1 && val[0].length() == 0) + { + params.set(origParamName.substring(pl), (String[]) null); + } + else + { + params.set(origParamName.substring(pl), val); + } + } + } + // we don't set the 'q' param, as we'll pass the query via JSON. + // params.set(CommonParams.Q, collationQueryStr); + params.remove(CommonParams.START); + params.set(CommonParams.ROWS, "" + docCollectionLimit); + // we don't want any stored fields + params.set(CommonParams.FL, "id"); + // we'll sort by doc id to ensure no scoring is done. + params.set(CommonParams.SORT, "_docid_ asc"); + // If a dismax query, don't add unnecessary clauses for scoring + params.remove(DisMaxParams.TIE); + params.remove(DisMaxParams.PF); + params.remove(DisMaxParams.PF2); + params.remove(DisMaxParams.PF3); + params.remove(DisMaxParams.BQ); + params.remove(DisMaxParams.BF); + // Collate testing does not support Grouping (see SOLR-2577) + params.remove(GroupParams.GROUP); + + boolean useQStr = true; + + if (alfrescoJSON != null) + { + try + { + aftsQuery = originalAftsQuery.replaceAll(Pattern.quote(originalQuery), Matcher.quoteReplacement(collationQueryStr)); + alfrescoJSON.put("query", aftsQuery); + req.getContext().put(AbstractQParser.ALFRESCO_JSON, alfrescoJSON); + useQStr = false; + } + catch (JSONException e) + { + LOG.warn("Exception trying to get/set the query from/to ALFRESCO_JSON.]" + e); + } + } + else + { + aftsQuery = collationQueryStr; + } + req.setParams(params); + // creating a request here... make sure to close it! + ResponseBuilder checkResponse = new ResponseBuilder(req, new SolrQueryResponse(), + Arrays. asList(queryComponent)); + checkResponse.setQparser(ultimateResponse.getQparser()); + checkResponse.setFilters(ultimateResponse.getFilters()); + checkResponse.components = Arrays. asList(queryComponent); + if (useQStr) + { + checkResponse.setQueryString(collationQueryStr); + } + try + { + queryComponent.prepare(checkResponse); + if (docCollectionLimit > 0) + { + int f = checkResponse.getFieldFlags(); + checkResponse.setFieldFlags(f |= SolrIndexSearcher.TERMINATE_EARLY); + } + queryComponent.process(checkResponse); + hits = (Integer) checkResponse.rsp.getToLog().get("hits"); + } + catch (EarlyTerminatingCollectorException etce) + { + assert (docCollectionLimit > 0); + assert 0 < etce.getNumberScanned(); + assert 0 < etce.getNumberCollected(); + + if (etce.getNumberScanned() == maxDocId) + { + hits = etce.getNumberCollected(); + } + else + { + hits = (int) (((float) (maxDocId * etce.getNumberCollected())) / (float) etce + .getNumberScanned()); + } + } + catch (Exception e) + { + LOG.warn("Exception trying to re-query to check if a spell check possibility would return any hits." + e); + } + finally + { + checkResponse.req.close(); + } + } + if (hits > 0 || !verifyCandidateWithQuery) + { + collNo++; + AlfrescoSpellCheckCollation collation = new AlfrescoSpellCheckCollation(); + collation.setCollationQuery(aftsQuery); + collation.setCollationQueryString(collationQueryStr); + collation.setHits(hits); + collation.setInternalRank(suggestionsMayOverlap ? ((possibility.rank * 1000) + possibility.index) + : possibility.rank); + + NamedList misspellingsAndCorrections = new NamedList<>(); + for (SpellCheckCorrection corr : possibility.corrections) + { + misspellingsAndCorrections.add(corr.getOriginal().toString(), corr.getCorrection()); + } + collation.setMisspellingsAndCorrections(misspellingsAndCorrections); + collations.add(collation); + } + if (LOG.isDebugEnabled()) + { + LOG.debug("Collation: " + aftsQuery + (verifyCandidateWithQuery ? (" will return " + hits + " hits.") : "")); + } + } + return collations; + } + + @SuppressWarnings("deprecation") + private String getCollation(String origQuery, List corrections) + { + StringBuilder collation = new StringBuilder(origQuery); + int offset = 0; + String corr = ""; + for (int i = 0; i < corrections.size(); i++) + { + SpellCheckCorrection correction = corrections.get(i); + Token tok = correction.getOriginal(); + // we are replacing the query in order, but injected terms might + // cause illegal offsets due to previous replacements. + if (tok.getPositionIncrement() == 0) + continue; + corr = correction.getCorrection(); + boolean addParenthesis = false; + Character requiredOrProhibited = null; + int indexOfSpace = corr.indexOf(' '); + StringBuilder corrSb = new StringBuilder(corr); + int bump = 1; + + // If the correction contains whitespace (because it involved + // breaking a word in 2+ words), + // then be sure all of the new words have the same + // optional/required/prohibited status in the query. + while (indexOfSpace > -1 && indexOfSpace < corr.length() - 1) + { + addParenthesis = true; + char previousChar = tok.startOffset() > 0 ? origQuery.charAt(tok.startOffset() - 1) : ' '; + if (previousChar == '-' || previousChar == '+') + { + corrSb.insert(indexOfSpace + bump, previousChar); + if (requiredOrProhibited == null) + { + requiredOrProhibited = previousChar; + } + bump++; + } + else if ((tok.getFlags() & QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG) == QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG) + { + corrSb.insert(indexOfSpace + bump, "AND "); + bump += 4; + } + indexOfSpace = correction.getCorrection().indexOf(' ', indexOfSpace + bump); + } + + int oneForReqOrProhib = 0; + if (addParenthesis) + { + if (requiredOrProhibited != null) + { + corrSb.insert(0, requiredOrProhibited); + oneForReqOrProhib++; + } + corrSb.insert(0, '('); + corrSb.append(')'); + } + corr = corrSb.toString(); + int startIndex = tok.startOffset() + offset - oneForReqOrProhib; + int endIndex = tok.endOffset() + offset; + collation.replace(startIndex, endIndex, corr); + offset += corr.length() - oneForReqOrProhib - (tok.endOffset() - tok.startOffset()); + } + return collation.toString(); + } + + public AlfrescoSpellCheckCollator setMaxCollations(int maxCollations) + { + this.maxCollations = maxCollations; + return this; + } + + public AlfrescoSpellCheckCollator setMaxCollationTries(int maxCollationTries) + { + this.maxCollationTries = maxCollationTries; + return this; + } + + public AlfrescoSpellCheckCollator setMaxCollationEvaluations(int maxCollationEvaluations) + { + this.maxCollationEvaluations = maxCollationEvaluations; + return this; + } + + public AlfrescoSpellCheckCollator setSuggestionsMayOverlap(boolean suggestionsMayOverlap) + { + this.suggestionsMayOverlap = suggestionsMayOverlap; + return this; + } + + public AlfrescoSpellCheckCollator setDocCollectionLimit(int docCollectionLimit) + { + this.docCollectionLimit = docCollectionLimit; + return this; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckComponent.java index 2bfe76ed2..43d7f209e 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckComponent.java @@ -1,116 +1,116 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component.spellcheck; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SpellCheckComponent; -import org.apache.solr.spelling.SpellingResult; - -/** - * @author Jamal Kaabi-Mofrad - * @since 5.0 - */ -public class AlfrescoSpellCheckComponent extends SpellCheckComponent -{ - - /** - * Disclaimer: The code copied from the super class ( - * {@link org.apache.solr.handler.component.SpellCheckComponent}) but only - * replaced the collator with the AlfrescoSpellCheckCollator - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - protected void addCollationsToResponse(SolrParams params, SpellingResult spellingResult, ResponseBuilder rb, - String q, NamedList response, boolean suggestionsMayOverlap) - { - int maxCollations = params.getInt(SPELLCHECK_MAX_COLLATIONS, 1); - int maxCollationTries = params.getInt(SPELLCHECK_MAX_COLLATION_TRIES, 0); - int maxCollationEvaluations = params.getInt(SPELLCHECK_MAX_COLLATION_EVALUATIONS, 10000); - boolean collationExtendedResults = params.getBool(SPELLCHECK_COLLATE_EXTENDED_RESULTS, false); - int maxCollationCollectDocs = params.getInt(SPELLCHECK_COLLATE_MAX_COLLECT_DOCS, 0); - // If not reporting hits counts, don't bother collecting more than 1 document per try. - if (!collationExtendedResults) - { - maxCollationCollectDocs = 1; - } - boolean shard = params.getBool(ShardParams.IS_SHARD, false); - AlfrescoSpellCheckCollator collator = new AlfrescoSpellCheckCollator(); - collator.setMaxCollations(maxCollations); - collator.setMaxCollationTries(maxCollationTries); - collator.setMaxCollationEvaluations(maxCollationEvaluations); - collator.setSuggestionsMayOverlap(suggestionsMayOverlap); - collator.setDocCollectionLimit(maxCollationCollectDocs); - - List collations = collator.collate(spellingResult, q, rb); - // by sorting here we guarantee a non-distributed request returns all - // results in the same order as a distributed request would, - // even in cases when the internal rank is the same. - Collections.sort(collations); - - NamedList collationList = new NamedList(); - - for (AlfrescoSpellCheckCollation collation : collations) - { - if (collationExtendedResults) - { - NamedList extendedResult = new SimpleOrderedMap(); - extendedResult.add("collationQuery", collation.getCollationQuery()); - extendedResult.add("hits", collation.getHits()); - extendedResult.add("misspellingsAndCorrections", collation.getMisspellingsAndCorrections()); - if (maxCollationTries > 0 && shard) - { - extendedResult.add("collationInternalRank", collation.getInternalRank()); - } - extendedResult.add("collationQueryString", collation.getCollationQueryString()); - collationList.add("collation", extendedResult); - } - else - { - collationList.add("collation", collation.getCollationQuery()); - if (maxCollationTries > 0 && shard) - { - collationList.add("collationInternalRank", collation.getInternalRank()); - } - } - } - - //Support Solr 4 output format - NamedList suggestions = (NamedList)response.get("suggestions"); - suggestions.addAll(collationList); - - //Support Solr distributed merge format. - response.add("collations", collationList); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component.spellcheck; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SpellCheckComponent; +import org.apache.solr.spelling.SpellingResult; + +/** + * @author Jamal Kaabi-Mofrad + * @since 5.0 + */ +public class AlfrescoSpellCheckComponent extends SpellCheckComponent +{ + + /** + * Disclaimer: The code copied from the super class ( + * {@link org.apache.solr.handler.component.SpellCheckComponent}) but only + * replaced the collator with the AlfrescoSpellCheckCollator + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + protected void addCollationsToResponse(SolrParams params, SpellingResult spellingResult, ResponseBuilder rb, + String q, NamedList response, boolean suggestionsMayOverlap) + { + int maxCollations = params.getInt(SPELLCHECK_MAX_COLLATIONS, 1); + int maxCollationTries = params.getInt(SPELLCHECK_MAX_COLLATION_TRIES, 0); + int maxCollationEvaluations = params.getInt(SPELLCHECK_MAX_COLLATION_EVALUATIONS, 10000); + boolean collationExtendedResults = params.getBool(SPELLCHECK_COLLATE_EXTENDED_RESULTS, false); + int maxCollationCollectDocs = params.getInt(SPELLCHECK_COLLATE_MAX_COLLECT_DOCS, 0); + // If not reporting hits counts, don't bother collecting more than 1 document per try. + if (!collationExtendedResults) + { + maxCollationCollectDocs = 1; + } + boolean shard = params.getBool(ShardParams.IS_SHARD, false); + AlfrescoSpellCheckCollator collator = new AlfrescoSpellCheckCollator(); + collator.setMaxCollations(maxCollations); + collator.setMaxCollationTries(maxCollationTries); + collator.setMaxCollationEvaluations(maxCollationEvaluations); + collator.setSuggestionsMayOverlap(suggestionsMayOverlap); + collator.setDocCollectionLimit(maxCollationCollectDocs); + + List collations = collator.collate(spellingResult, q, rb); + // by sorting here we guarantee a non-distributed request returns all + // results in the same order as a distributed request would, + // even in cases when the internal rank is the same. + Collections.sort(collations); + + NamedList collationList = new NamedList(); + + for (AlfrescoSpellCheckCollation collation : collations) + { + if (collationExtendedResults) + { + NamedList extendedResult = new SimpleOrderedMap(); + extendedResult.add("collationQuery", collation.getCollationQuery()); + extendedResult.add("hits", collation.getHits()); + extendedResult.add("misspellingsAndCorrections", collation.getMisspellingsAndCorrections()); + if (maxCollationTries > 0 && shard) + { + extendedResult.add("collationInternalRank", collation.getInternalRank()); + } + extendedResult.add("collationQueryString", collation.getCollationQueryString()); + collationList.add("collation", extendedResult); + } + else + { + collationList.add("collation", collation.getCollationQuery()); + if (maxCollationTries > 0 && shard) + { + collationList.add("collationInternalRank", collation.getInternalRank()); + } + } + } + + //Support Solr 4 output format + NamedList suggestions = (NamedList)response.get("suggestions"); + suggestions.addAll(collationList); + + //Support Solr distributed merge format. + response.add("collations", collationList); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/config/ConfigUtil.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/config/ConfigUtil.java index 08e8183aa..f6a322889 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/config/ConfigUtil.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/config/ConfigUtil.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/data/GlobalReaders.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/data/GlobalReaders.java index bdf656a49..1dcb31e85 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/data/GlobalReaders.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/data/GlobalReaders.java @@ -1,54 +1,54 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.data; - -import java.util.HashSet; - -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.security.PermissionService; - -/** - * Statically configured set of authorities that always have read access. - * - * @author Matt Ward - */ -public class GlobalReaders -{ - private static HashSet readers = new HashSet(); - - static - { - readers.add(PermissionService.OWNER_AUTHORITY); - readers.add(PermissionService.ADMINISTRATOR_AUTHORITY); - readers.add(AuthenticationUtil.getSystemUserName()); - } - - public static HashSet getReaders() - { - return readers; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.data; + +import java.util.HashSet; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.security.PermissionService; + +/** + * Statically configured set of authorities that always have read access. + * + * @author Matt Ward + */ +public class GlobalReaders +{ + private static HashSet readers = new HashSet(); + + static + { + readers.add(PermissionService.OWNER_AUTHORITY); + readers.add(PermissionService.ADMINISTRATOR_AUTHORITY); + readers.add(AuthenticationUtil.getSystemUserName()); + } + + public static HashSet getReaders() + { + return readers; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java index fff070f6f..77916188f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -462,4 +462,4 @@ public class SolrCoreLoadListener extends AbstractSolrEventListener .map(isSlaveModeEnabled) .orElse(false); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/logging/Log.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/logging/Log.java index d074ffefe..c51952314 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/logging/Log.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/logging/Log.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbsoluteStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbsoluteStructuredFieldPosition.java index e2e54b808..aeba76d58 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbsoluteStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbsoluteStructuredFieldPosition.java @@ -1,142 +1,142 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -/** - * This class patches a term at a specified location. - * - * @author andyh - */ -public class AbsoluteStructuredFieldPosition extends AbstractStructuredFieldPosition -{ - - int requiredPosition; - - /** - * Search for a term at the specified position. - */ - - public AbsoluteStructuredFieldPosition(String termText, int position) - { - super(termText, true, true); - this.requiredPosition = position; - } - - /* - * (non-Javadoc) - * - * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, - * org.apache.lucene.index.TermPositions) - */ - public int matches(int start, int end, int offset) throws IOException - { - if (offset >= requiredPosition) - { - return -1; - } - - if (getCachingTermPositions() != null) - { - // Doing "termText" - getCachingTermPositions().reset(); - int count = getCachingTermPositions().freq(); - int realPosition = 0; - int adjustedPosition = 0; - for (int i = 0; i < count; i++) - { - realPosition = getCachingTermPositions().nextPosition(); - adjustedPosition = realPosition - start; - if ((end != -1) && (realPosition > end)) - { - return -1; - } - if (adjustedPosition > requiredPosition) - { - return -1; - } - if (adjustedPosition == requiredPosition) - { - return adjustedPosition; - } - - } - } - else - { - // Doing "*" - if ((offset + 1) == requiredPosition) - { - return offset + 1; - } - } - return -1; - - } - - /* - * (non-Javadoc) - * - * @see org.alfresco.lucene.extensions.StructuredFieldPosition#getPosition() - */ - public int getPosition() - { - return requiredPosition; - } - - public String getDescription() - { - return "Absolute Named child"; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + requiredPosition; - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - AbsoluteStructuredFieldPosition other = (AbsoluteStructuredFieldPosition) obj; - if (requiredPosition != other.requiredPosition) - return false; - return true; - } - - -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +/** + * This class patches a term at a specified location. + * + * @author andyh + */ +public class AbsoluteStructuredFieldPosition extends AbstractStructuredFieldPosition +{ + + int requiredPosition; + + /** + * Search for a term at the specified position. + */ + + public AbsoluteStructuredFieldPosition(String termText, int position) + { + super(termText, true, true); + this.requiredPosition = position; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, + * org.apache.lucene.index.TermPositions) + */ + public int matches(int start, int end, int offset) throws IOException + { + if (offset >= requiredPosition) + { + return -1; + } + + if (getCachingTermPositions() != null) + { + // Doing "termText" + getCachingTermPositions().reset(); + int count = getCachingTermPositions().freq(); + int realPosition = 0; + int adjustedPosition = 0; + for (int i = 0; i < count; i++) + { + realPosition = getCachingTermPositions().nextPosition(); + adjustedPosition = realPosition - start; + if ((end != -1) && (realPosition > end)) + { + return -1; + } + if (adjustedPosition > requiredPosition) + { + return -1; + } + if (adjustedPosition == requiredPosition) + { + return adjustedPosition; + } + + } + } + else + { + // Doing "*" + if ((offset + 1) == requiredPosition) + { + return offset + 1; + } + } + return -1; + + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.lucene.extensions.StructuredFieldPosition#getPosition() + */ + public int getPosition() + { + return requiredPosition; + } + + public String getDescription() + { + return "Absolute Named child"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + requiredPosition; + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + AbsoluteStructuredFieldPosition other = (AbsoluteStructuredFieldPosition) obj; + if (requiredPosition != other.requiredPosition) + return false; + return true; + } + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQuery.java index 3b845ee12..bef366ccf 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQuery.java @@ -1,76 +1,76 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; - -/** - * Base class for queries relating to an authority. - */ -public abstract class AbstractAuthorityQuery extends Query -{ - protected final String authority; - - /** - * Construct with authority. - * - * @param authority - */ - public AbstractAuthorityQuery(String authority) - { - if (authority == null) throw new IllegalStateException("authority cannot be null"); - this.authority = authority; - } - - @Override - public abstract Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException; - - public String toString(String field) - { - return toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof AbstractAuthorityQuery)) return false; - - AbstractAuthorityQuery that = (AbstractAuthorityQuery) o; - - return authority.equals(that.authority); - - } - - @Override - public int hashCode() { - return authority.hashCode(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; + +/** + * Base class for queries relating to an authority. + */ +public abstract class AbstractAuthorityQuery extends Query +{ + protected final String authority; + + /** + * Construct with authority. + * + * @param authority + */ + public AbstractAuthorityQuery(String authority) + { + if (authority == null) throw new IllegalStateException("authority cannot be null"); + this.authority = authority; + } + + @Override + public abstract Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException; + + public String toString(String field) + { + return toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AbstractAuthorityQuery)) return false; + + AbstractAuthorityQuery that = (AbstractAuthorityQuery) o; + + return authority.equals(that.authority); + + } + + @Override + public int hashCode() { + return authority.hashCode(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQueryWeight.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQueryWeight.java index 3b46846e3..465fbbc8f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQueryWeight.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQueryWeight.java @@ -1,85 +1,85 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.apache.lucene.index.IndexReaderContext; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermContext; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Abstract {@link Weight} implementation for authority related queries. - * - * @see AbstractAuthoritySetQuery - */ -public abstract class AbstractAuthorityQueryWeight extends Weight -{ - protected Query query; - protected SolrIndexSearcher searcher; - protected float value; - protected boolean needsScores; - - public AbstractAuthorityQueryWeight(SolrIndexSearcher searcher, boolean needsScores, Query query, String authTermName, String authTermText) throws IOException - { - super(query); - this.searcher = searcher; - searcher.collectionStatistics(authTermName); - final IndexReaderContext context = searcher.getTopReaderContext(); - final Term term = new Term(authTermName, authTermText); - final TermContext termContext = TermContext.build(context, term); - searcher.termStatistics(term, termContext); - this.needsScores = needsScores; - } - - @Override - public Explanation explain(LeafReaderContext context, int doc) - { - throw new UnsupportedOperationException(); - } - - @Override - public float getValueForNormalization() - { - return sumOfSquaredWeights(); - } - - @Override - public void normalize(float queryNorm, float topLevelBoost) - { - } - - protected float sumOfSquaredWeights() - { - return 0; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.apache.lucene.index.IndexReaderContext; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.index.TermContext; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Abstract {@link Weight} implementation for authority related queries. + * + * @see AbstractAuthoritySetQuery + */ +public abstract class AbstractAuthorityQueryWeight extends Weight +{ + protected Query query; + protected SolrIndexSearcher searcher; + protected float value; + protected boolean needsScores; + + public AbstractAuthorityQueryWeight(SolrIndexSearcher searcher, boolean needsScores, Query query, String authTermName, String authTermText) throws IOException + { + super(query); + this.searcher = searcher; + searcher.collectionStatistics(authTermName); + final IndexReaderContext context = searcher.getTopReaderContext(); + final Term term = new Term(authTermName, authTermText); + final TermContext termContext = TermContext.build(context, term); + searcher.termStatistics(term, termContext); + this.needsScores = needsScores; + } + + @Override + public Explanation explain(LeafReaderContext context, int doc) + { + throw new UnsupportedOperationException(); + } + + @Override + public float getValueForNormalization() + { + return sumOfSquaredWeights(); + } + + @Override + public void normalize(float queryNorm, float topLevelBoost) + { + } + + protected float sumOfSquaredWeights() + { + return 0; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthoritySetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthoritySetQuery.java index 65da0c144..ef14d2625 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthoritySetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthoritySetQuery.java @@ -1,192 +1,192 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Base class for queries relating to a set of authorities, e.g. reader set query. - */ -public abstract class AbstractAuthoritySetQuery extends Query -{ - protected final String authorities; - - /** - * Construct with authorities. - * - * @param authorities - */ - public AbstractAuthoritySetQuery(String authorities) - { - super(); - if (authorities == null) throw new IllegalStateException("authorities cannot be null"); - this.authorities = authorities; - } - - @Override - public abstract Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException; - - @Override - public String toString(String field) - { - return toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof AbstractAuthoritySetQuery)) return false; - - AbstractAuthoritySetQuery that = (AbstractAuthoritySetQuery) o; - - return authorities.equals(that.authorities); - - } - - @Override - public int hashCode() { - return authorities.hashCode(); - } - - - /* - * This method collects the bitset of documents that match the authorities. - */ - - protected HybridBitSet getACLSet(String[] auths, String field, SolrIndexSearcher searcher) throws IOException - { - /* - * Build a query that matches the authorities with a field in the ACL records in the index. - */ - - BooleanQuery.Builder queryBuilder = new BooleanQuery.Builder(); - for(String current : auths) - { - queryBuilder.add(new TermQuery(new Term(field, current)), BooleanClause.Occur.SHOULD); - } - - - /* - * Collect a docset containing the ACL records that match the query. - * This query will be in the filter cache. Ideally it would remain cached throughout the users session. - */ - - DocSet docSet = searcher.getDocSet(queryBuilder.build()); - - DocIterator iterator = docSet.iterator(); - if(!iterator.hasNext()) - { - return new EmptyHybridBitSet(); - } - - //TODO : makes this configurable. For some systems this is huge and for others not big enough. - HybridBitSet hybridBitSet = new HybridBitSet(60000000); - - /* - * Collect the ACLID's from the matching acl records. - * This is done in a separate step so the initial ACL query can be cached in the FilterCache - * The initial ACL query may be expensive if the number of authorities is very large. - */ - - List leaves = searcher.getTopReaderContext().leaves(); - LeafReaderContext context = leaves.get(0); - NumericDocValues aclValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); - LeafReader reader = context.reader(); - int ceil = reader.maxDoc(); - int base = 0; - int ord = 0; - while (iterator.hasNext()) { - int doc = iterator.nextDoc(); - if(doc >= ceil) - { - do - { - ++ord; - context = leaves.get(ord); - reader = context.reader(); - base = context.docBase; - ceil = base+reader.maxDoc(); - aclValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, reader); - } - while(doc >= ceil); - } - - if(aclValues != null) { - long aclId = aclValues.get(doc - base); - hybridBitSet.set(aclId); - } - } - - return hybridBitSet; - } - - protected BitsFilter getACLFilter(String[] auths, String field, SolrIndexSearcher searcher) throws IOException - { - HybridBitSet aclBits = getACLSet(auths, field, searcher); - List leaves = searcher.getTopReaderContext().leaves(); - List bitSets = new ArrayList(leaves.size()); - - for(LeafReaderContext readerContext : leaves) - { - LeafReader reader = readerContext.reader(); - int maxDoc = reader.maxDoc(); - FixedBitSet bits = new FixedBitSet(maxDoc); - bitSets.add(bits); - - NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, reader); - if (fieldValues != null) { - for (int i = 0; i < maxDoc; i++) { - long aclID = fieldValues.get(i); - if (aclBits.get(aclID)) { - bits.set(i); - } - } - } - } - - return new BitsFilter(bitSets); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Base class for queries relating to a set of authorities, e.g. reader set query. + */ +public abstract class AbstractAuthoritySetQuery extends Query +{ + protected final String authorities; + + /** + * Construct with authorities. + * + * @param authorities + */ + public AbstractAuthoritySetQuery(String authorities) + { + super(); + if (authorities == null) throw new IllegalStateException("authorities cannot be null"); + this.authorities = authorities; + } + + @Override + public abstract Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException; + + @Override + public String toString(String field) + { + return toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AbstractAuthoritySetQuery)) return false; + + AbstractAuthoritySetQuery that = (AbstractAuthoritySetQuery) o; + + return authorities.equals(that.authorities); + + } + + @Override + public int hashCode() { + return authorities.hashCode(); + } + + + /* + * This method collects the bitset of documents that match the authorities. + */ + + protected HybridBitSet getACLSet(String[] auths, String field, SolrIndexSearcher searcher) throws IOException + { + /* + * Build a query that matches the authorities with a field in the ACL records in the index. + */ + + BooleanQuery.Builder queryBuilder = new BooleanQuery.Builder(); + for(String current : auths) + { + queryBuilder.add(new TermQuery(new Term(field, current)), BooleanClause.Occur.SHOULD); + } + + + /* + * Collect a docset containing the ACL records that match the query. + * This query will be in the filter cache. Ideally it would remain cached throughout the users session. + */ + + DocSet docSet = searcher.getDocSet(queryBuilder.build()); + + DocIterator iterator = docSet.iterator(); + if(!iterator.hasNext()) + { + return new EmptyHybridBitSet(); + } + + //TODO : makes this configurable. For some systems this is huge and for others not big enough. + HybridBitSet hybridBitSet = new HybridBitSet(60000000); + + /* + * Collect the ACLID's from the matching acl records. + * This is done in a separate step so the initial ACL query can be cached in the FilterCache + * The initial ACL query may be expensive if the number of authorities is very large. + */ + + List leaves = searcher.getTopReaderContext().leaves(); + LeafReaderContext context = leaves.get(0); + NumericDocValues aclValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); + LeafReader reader = context.reader(); + int ceil = reader.maxDoc(); + int base = 0; + int ord = 0; + while (iterator.hasNext()) { + int doc = iterator.nextDoc(); + if(doc >= ceil) + { + do + { + ++ord; + context = leaves.get(ord); + reader = context.reader(); + base = context.docBase; + ceil = base+reader.maxDoc(); + aclValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, reader); + } + while(doc >= ceil); + } + + if(aclValues != null) { + long aclId = aclValues.get(doc - base); + hybridBitSet.set(aclId); + } + } + + return hybridBitSet; + } + + protected BitsFilter getACLFilter(String[] auths, String field, SolrIndexSearcher searcher) throws IOException + { + HybridBitSet aclBits = getACLSet(auths, field, searcher); + List leaves = searcher.getTopReaderContext().leaves(); + List bitSets = new ArrayList(leaves.size()); + + for(LeafReaderContext readerContext : leaves) + { + LeafReader reader = readerContext.reader(); + int maxDoc = reader.maxDoc(); + FixedBitSet bits = new FixedBitSet(maxDoc); + bitSets.add(bits); + + NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, reader); + if (fieldValues != null) { + for (int i = 0; i < maxDoc; i++) { + long aclID = fieldValues.get(i); + if (aclBits.get(aclID)) { + bits.set(i); + } + } + } + } + + return new BitsFilter(bitSets); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractQParser.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractQParser.java index 68e6cea4b..c3fd28537 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractQParser.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractQParser.java @@ -1,707 +1,707 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; -import org.alfresco.service.cmr.search.QueryConsistency; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchParameters.Operator; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.alfresco.util.Pair; -import org.apache.commons.io.IOUtils; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.ContentStream; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.SortSpec; -import org.apache.solr.search.SortSpecParsing; -import org.apache.solr.search.SyntaxError; -import org.apache.solr.update.processor.LangDetectLanguageIdentifierUpdateProcessor; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.extensions.surf.util.I18NUtil; - -import com.cybozu.labs.langdetect.Detector; -import com.cybozu.labs.langdetect.DetectorFactory; -import com.cybozu.labs.langdetect.LangDetectException; -import com.cybozu.labs.langdetect.Language; - -/** - * @author Andy - */ -public abstract class AbstractQParser extends QParser implements QueryConstants -{ - private static char[] SEPARATORS = new char[] { ':', ',', '-', '!', '+', '=', ';', '~', '/' }; - - protected final static Logger log = LoggerFactory.getLogger(AbstractQParser.class); - - protected boolean authset; - - public static final String ALFRESCO_JSON = "ALFRESCO_JSON"; - - private static final String AUTHORITY_FILTER_FROM_JSON = "AUTHORITY_FILTER_FROM_JSON"; - - private static final String TENANT_FILTER_FROM_JSON = "TENANT_FILTER_FROM_JSON"; - - private static final String RERANK_QUERY_FROM_CONTEXT = "RERANK_QUERY_FROM_CONTEXT"; - - static final String languages[] = { - "af", "ar", "bg", "bn", "cs", "da", "de", "el", "en", "es", "et", "fa", "fi", "fr", "gu", - "he", "hi", "hr", "hu", "id", "it", "ja", "kn", "ko", "lt", "lv", "mk", "ml", "mr", "ne", - "nl", "no", "pa", "pl", "pt", "ro", "ru", "sk", "sl", "so", "sq", "sv", "sw", "ta", "te", - "th", "tl", "tr", "uk", "ur", "vi", "zh-cn", "zh-tw" - }; - - static - { - try { - List profileData = new ArrayList<>(); - for (String language : languages) { - InputStream stream = LangDetectLanguageIdentifierUpdateProcessor.class.getResourceAsStream("langdetect-profiles/" + language); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); - profileData.add(new String(IOUtils.toCharArray(reader))); - reader.close(); - } - DetectorFactory.loadProfile(profileData); - DetectorFactory.setSeed(0); - } catch (Exception e) { - throw new RuntimeException("Couldn't load profile data, will return empty languages always!", e); - } - } - - private boolean autoDetectQueryLocale = false; - - private HashSet autoDetectQueryLocales = new HashSet(); - - private HashSet fixedQueryLocales = new HashSet(); - - /** - * @param qstr String - * @param localParams SolrParams - * @param params SolrParams - * @param req SolrQueryRequest - * @param args - */ - public AbstractQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) - { - super(qstr, localParams, params, req); - if(args != null) - { - Object arg = args.get("autoDetectQueryLocale"); - if(arg != null) - { - this.autoDetectQueryLocale = Boolean.parseBoolean(arg.toString()); - } - - arg = args.get("autoDetectQueryLocales"); - if(arg != null) - { - String[] locales = arg.toString().split(","); - for(String locale : locales) - { - String mappedLanguage = isKnownLocale(locale); - if(mappedLanguage != null) - { - autoDetectQueryLocales.add(mappedLanguage); - } - } - } - - - arg = args.get("fixedQueryLocales"); - if(arg != null) - { - String[] locales = arg.toString().split(","); - for(String locale : locales) - { - String mappedLanguage = isKnownLocale(locale); - if(mappedLanguage != null) - { - fixedQueryLocales.add(mappedLanguage); - } - } - } - } - } - - private String isKnownLocale(String locale) - { - for(String test : languages) - { - if(locale.equalsIgnoreCase(test)) - { - return test; - } - } - return null; - } - - - protected Pair getSearchParameters() - { - SearchParameters searchParameters = new SearchParameters(); - - Boolean isFilter = Boolean.FALSE; - - Iterable streams = req.getContentStreams(); - - JSONObject json = (JSONObject) req.getContext().get(ALFRESCO_JSON); - - if (json == null) - { - if (streams != null) - { - - try - { - Reader reader = null; - for (ContentStream stream : streams) - { - reader = new BufferedReader(new InputStreamReader(stream.getStream(), "UTF-8")); - } - - // TODO - replace with streaming-based solution e.g. SimpleJSON ContentHandler - if (reader != null) - { - json = new JSONObject(new JSONTokener(reader)); - req.getContext().put(ALFRESCO_JSON, json); - } - } - catch (JSONException e) - { - // This is expected when there is no json element to the request - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("IO Error parsing query parameters", e); - } - } - } - - if (json != null) - { - try - { - if (getString() != null) - { - if (getString().equals(AUTHORITY_FILTER_FROM_JSON)) - { - isFilter =Boolean.TRUE; - - ArrayList tenantList = new ArrayList(1); - JSONArray tenants = json.getJSONArray("tenants"); - for (int i = 0; i < tenants.length(); i++) - { - String tenantString = tenants.getString(i); - tenantList.add(tenantString); - } - - ArrayList authorityList = new ArrayList(1); - JSONArray authorities = json.getJSONArray("authorities"); - for (int i = 0; i < authorities.length(); i++) - { - String authorityString = authorities.getString(i); - authorityList.add(authorityString); - } - - char separator = getSeparator(authorityList); - - StringBuilder authQuery = new StringBuilder(); - StringBuilder denyQuery = new StringBuilder(); - - for (String tenant : tenantList) - { - for (String authority : authorityList) - { - if (separator == 0) - { - if (authQuery.length() > 0) - { - authQuery.append(" "); - denyQuery.append(" "); - } - switch (AuthorityType.getAuthorityType(authority)) - { - case USER: - authQuery.append("|AUTHORITY:\"").append(authority).append("\""); - denyQuery.append("|DENIED:\"").append(authority).append("\""); - break; - case GROUP: - case EVERYONE: - case GUEST: - if (tenant.length() == 0) - { - // Default tenant matches 4.0 - authQuery.append("|AUTHORITY:\"").append(authority).append("\""); - denyQuery.append("|DENIED:\"").append(authority).append("\""); - } - else - { - authQuery.append("|AUTHORITY:\"").append(authority).append("@").append(tenant).append("\""); - denyQuery.append("|DENIED:\"").append(authority).append("@").append(tenant).append("\""); - } - break; - default: - authQuery.append("|AUTHORITY:\"").append(authority).append("\""); - denyQuery.append("|DENIED:\"").append(authority).append("\""); - break; - } - } - else - { - if(authQuery.length() == 0) - { - authset = true; - authQuery.append("|AUTHSET:\""); - denyQuery.append("|DENYSET:\""); - } - - switch (AuthorityType.getAuthorityType(authority)) - { - case USER: - authQuery.append(separator).append(authority); - denyQuery.append(separator).append(authority); - break; - case GROUP: - case EVERYONE: - case GUEST: - if (tenant.length() == 0) - { - // Default tenant matches 4.0 - authQuery.append(separator).append(authority); - denyQuery.append(separator).append(authority); - } - else - { - authQuery.append(separator).append(authority).append("@").append(tenant); - denyQuery.append(separator).append(authority).append("@").append(tenant); - } - break; - default: - authQuery.append(separator).append(authority); - denyQuery.append(separator).append(authority); - break; - } - } - - } - } - if(separator != 0) - { - authQuery.append("\""); - denyQuery.append("\""); - } - - if (authQuery.length() > 0) - { - // Default to true for safety reasons. - final boolean anyDenyDenies = json.optBoolean("anyDenyDenies", true); - - if (anyDenyDenies) - { - authQuery.insert(0, "("). - append(") AND NOT ("). - append(denyQuery). - append(")"); - // Record that the clause has been added. - // We only ever set this to true for solr4+ - req.getContext().put("processedDenies", Boolean.TRUE); - } - searchParameters.setQuery(authQuery.toString()); - } - } - else if (getString().equals(TENANT_FILTER_FROM_JSON)) - { - isFilter =Boolean.TRUE; - - ArrayList tenantList = new ArrayList(1); - JSONArray tenants = json.getJSONArray("tenants"); - for (int i = 0; i < tenants.length(); i++) - { - String tenantString = tenants.getString(i); - tenantList.add(tenantString); - } - - StringBuilder tenantQuery = new StringBuilder(); - for (String tenant : tenantList) - { - if (tenantQuery.length() > 0) - { - tenantQuery.append(" "); - } - - if (tenant.length() > 0) - - { - tenantQuery.append("|TENANT:\"").append(tenant).append("\""); - } - else - { - // TODO: Need to check for the default tenant or no tenant (4.0) or we force a reindex - // requirement later ... - // Better to add default tenant to the 4.0 index - tenantQuery.append("|TENANT:\"").append("_DEFAULT_").append("\""); - // tenantQuery.append(" |(+ISNODE:T -TENANT:*)"); - } - - } - searchParameters.setQuery(tenantQuery.toString()); - } - else if (getString().equals(RERANK_QUERY_FROM_CONTEXT)) - { - String searchTerm = getParam("spellcheck.q"); - searchParameters.setQuery(searchTerm); - } - } - else - { - String query = json.getString("query"); - if (query != null) - { - searchParameters.setQuery(query); - } - } - - JSONArray locales = json.getJSONArray("locales"); - for (int i = 0; i < locales.length(); i++) - { - String localeString = locales.getString(i); - Locale locale = DefaultTypeConverter.INSTANCE.convert(Locale.class, localeString); - searchParameters.addLocale(locale); - } - - JSONArray templates = json.getJSONArray("templates"); - for (int i = 0; i < templates.length(); i++) - { - JSONObject template = templates.getJSONObject(i); - String name = template.getString("name"); - String queryTemplate = template.getString("template"); - searchParameters.addQueryTemplate(name, queryTemplate); - } - - JSONArray allAttributes = json.getJSONArray("allAttributes"); - for (int i = 0; i < allAttributes.length(); i++) - { - String allAttribute = allAttributes.getString(i); - searchParameters.addAllAttribute(allAttribute); - } - - searchParameters.setDefaultFTSOperator(Operator.valueOf(json.getString("defaultFTSOperator"))); - searchParameters.setDefaultFTSFieldConnective(Operator.valueOf(json.getString("defaultFTSFieldOperator"))); - if (json.has("mlAnalaysisMode")) - { - searchParameters.setMlAnalaysisMode(MLAnalysisMode.valueOf(json.getString("mlAnalaysisMode"))); - } - searchParameters.setNamespace(json.getString("defaultNamespace")); - - JSONArray textAttributes = json.getJSONArray("textAttributes"); - for (int i = 0; i < textAttributes.length(); i++) - { - String textAttribute = textAttributes.getString(i); - searchParameters.addTextAttribute(textAttribute); - } - - - searchParameters.setQueryConsistency(QueryConsistency.valueOf(json.getString("queryConsistency"))); - - } - catch (JSONException e) - { - // This is expected when there is no json element to the request - } - } - - if (json != null) - { - if (log.isDebugEnabled()) - { - log.debug(json.toString()); - } - } - - if (searchParameters.getQuery() == null) - { - searchParameters.setQuery(getString()); - } - - if (searchParameters.getLocales().size() == 0) - { - searchParameters.addLocale(I18NUtil.getLocale()); - } - - String defaultField = getParam(CommonParams.DF); - if (defaultField != null) - { - searchParameters.setDefaultFieldName(defaultField); - } - - if(autoDetectQueryLocale) - { - String searchTerm = getParam("spellcheck.q"); - if (searchTerm != null) - { - searchParameters.setSearchTerm(searchTerm); - List detetcted = detectLanguage(searchTerm); - if((detetcted != null) && (detetcted.size() > 0)) - { - Locale detectedLocale = Locale.forLanguageTag(detetcted.get(0).getLangCode()); - if(localeIsNotIncluded(searchParameters, detectedLocale)) - { - searchParameters.addLocale( Locale.forLanguageTag(detectedLocale.getLanguage())); - } - } - - } - } - - if(fixedQueryLocales.size() > 0) - { - for(String locale : fixedQueryLocales) - { - searchParameters.addLocale( Locale.forLanguageTag(locale)); - } - } - - // searchParameters.setMlAnalaysisMode(getMLAnalysisMode()); - searchParameters.setNamespace(NamespaceService.CONTENT_MODEL_1_0_URI); - - return new Pair(searchParameters, isFilter); - } - - /** - * @param searchParameters SearchParameters - * @param detectedLocale Locale - * @return boolean - */ - private boolean localeIsNotIncluded(SearchParameters searchParameters, Locale detectedLocale) - { - for(Locale locale : searchParameters.getLocales()) - { - if(locale.getLanguage().equals(detectedLocale.getLanguage())) - { - return false; - } - } - return true; - } - - - /** - * @param authorityList ArrayList - * @return char - */ - private char getSeparator(ArrayList authorityList) - { - StringBuilder builder = new StringBuilder(); - for(String auth : authorityList) - { - builder.append(auth); - } - String test = builder.toString(); - - for(int i = 0; i < SEPARATORS.length; i++) - { - if(test.indexOf(SEPARATORS[i]) == -1) - { - return SEPARATORS[i]; - } - } - return 0; - } - - /* (non-Javadoc) - * @see org.apache.solr.search.QParser#getSort(boolean) - */ - @Override - public SortSpec getSortSpec(boolean useGlobalParams) throws SyntaxError - { - - getQuery(); // ensure query is parsed first - - String sortStr = null; - String startS = null; - String rowsS = null; - - if (localParams != null) { - sortStr = localParams.get(CommonParams.SORT); - startS = localParams.get(CommonParams.START); - rowsS = localParams.get(CommonParams.ROWS); - - // if any of these parameters are present, don't go back to the global params - if (sortStr != null || startS != null || rowsS != null) { - useGlobalParams = false; - } - } - - if (useGlobalParams) { - if (sortStr ==null) { - sortStr = params.get(CommonParams.SORT); - } - if (startS==null) { - startS = params.get(CommonParams.START); - } - if (rowsS==null) { - rowsS = params.get(CommonParams.ROWS); - } - } - - int start = startS != null ? Integer.parseInt(startS) : 0; - int rows = rowsS != null ? Integer.parseInt(rowsS) : 10; - - // Fix sort fields here - if(sortStr != null) - { - StringBuilder builder = new StringBuilder(); - StringBuilder propertyBuilder = null; - char c; - for(int i = 0; i < sortStr.length(); i++) - { - c = sortStr.charAt(i); - if(propertyBuilder == null) - { - if(!Character.isWhitespace(c) && (c != ',')) - { - propertyBuilder = new StringBuilder(); - propertyBuilder.append(c); - } - else - { - builder.append(c); - } - } - else - { - if(Character.isWhitespace(c) || (c == ',')) - { - String toAppend = AlfrescoSolrDataModel.getInstance().mapProperty(propertyBuilder.toString(), FieldUse.SORT, getReq()); - builder.append(toAppend); - builder.append(c); - propertyBuilder = null; - } - else - { - propertyBuilder.append(c); - } - } - } - if(propertyBuilder != null) - { - String toAppend = AlfrescoSolrDataModel.getInstance().mapProperty(propertyBuilder.toString(), FieldUse.SORT, getReq()); - builder.append(toAppend); - } - sortStr = builder.toString(); - } - - if(sortStr != null) - { - sortStr = sortStr.replaceAll("^ID(\\s)", "id$1"); - sortStr = sortStr.replaceAll("(\\s)ID(\\s)", "$1id$2"); - } - SortSpec sort = SortSpecParsing.parseSortSpec(sortStr, req); - - sort.setOffset(start); - sort.setCount(rows); - return sort; - } - - - - private List detectLanguage(String content) { - if (content.trim().length() == 0) { // to be consistent with the tika impl? - log.debug("No input text to detect language from, returning empty list"); - return Collections.emptyList(); - } - - try { - Detector detector = DetectorFactory.create(); - detector.append(content); - ArrayList langlist = detector.getProbabilities(); - ArrayList solrLangList = new ArrayList<>(); - for (Language l: langlist) - { - if((autoDetectQueryLocales.size() == 0) || (autoDetectQueryLocales.contains(l.lang))) - { - solrLangList.add(new DetectedLanguage(l.lang, l.prob)); - } - } - return solrLangList; - } catch (LangDetectException e) { - log.debug("Could not determine language, returning empty list: ", e); - return Collections.emptyList(); - } - } - - public class DetectedLanguage { - private final String langCode; - private final Double certainty; - - DetectedLanguage(String lang, Double certainty) { - this.langCode = lang; - this.certainty = certainty; - } - - /** - * Returns the detected language code - * @return language code as a string - */ - public String getLangCode() { - return langCode; - } - - /** - * Returns the detected certainty for this language - * @return certainty as a value between 0.0 and 1.0 where 1.0 is 100% certain - */ - public Double getCertainty() { - return certainty; - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.search.QueryConsistency; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchParameters.Operator; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.alfresco.util.Pair; +import org.apache.commons.io.IOUtils; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.ContentStream; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.SortSpec; +import org.apache.solr.search.SortSpecParsing; +import org.apache.solr.search.SyntaxError; +import org.apache.solr.update.processor.LangDetectLanguageIdentifierUpdateProcessor; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +import com.cybozu.labs.langdetect.Detector; +import com.cybozu.labs.langdetect.DetectorFactory; +import com.cybozu.labs.langdetect.LangDetectException; +import com.cybozu.labs.langdetect.Language; + +/** + * @author Andy + */ +public abstract class AbstractQParser extends QParser implements QueryConstants +{ + private static char[] SEPARATORS = new char[] { ':', ',', '-', '!', '+', '=', ';', '~', '/' }; + + protected final static Logger log = LoggerFactory.getLogger(AbstractQParser.class); + + protected boolean authset; + + public static final String ALFRESCO_JSON = "ALFRESCO_JSON"; + + private static final String AUTHORITY_FILTER_FROM_JSON = "AUTHORITY_FILTER_FROM_JSON"; + + private static final String TENANT_FILTER_FROM_JSON = "TENANT_FILTER_FROM_JSON"; + + private static final String RERANK_QUERY_FROM_CONTEXT = "RERANK_QUERY_FROM_CONTEXT"; + + static final String languages[] = { + "af", "ar", "bg", "bn", "cs", "da", "de", "el", "en", "es", "et", "fa", "fi", "fr", "gu", + "he", "hi", "hr", "hu", "id", "it", "ja", "kn", "ko", "lt", "lv", "mk", "ml", "mr", "ne", + "nl", "no", "pa", "pl", "pt", "ro", "ru", "sk", "sl", "so", "sq", "sv", "sw", "ta", "te", + "th", "tl", "tr", "uk", "ur", "vi", "zh-cn", "zh-tw" + }; + + static + { + try { + List profileData = new ArrayList<>(); + for (String language : languages) { + InputStream stream = LangDetectLanguageIdentifierUpdateProcessor.class.getResourceAsStream("langdetect-profiles/" + language); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); + profileData.add(new String(IOUtils.toCharArray(reader))); + reader.close(); + } + DetectorFactory.loadProfile(profileData); + DetectorFactory.setSeed(0); + } catch (Exception e) { + throw new RuntimeException("Couldn't load profile data, will return empty languages always!", e); + } + } + + private boolean autoDetectQueryLocale = false; + + private HashSet autoDetectQueryLocales = new HashSet(); + + private HashSet fixedQueryLocales = new HashSet(); + + /** + * @param qstr String + * @param localParams SolrParams + * @param params SolrParams + * @param req SolrQueryRequest + * @param args + */ + public AbstractQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) + { + super(qstr, localParams, params, req); + if(args != null) + { + Object arg = args.get("autoDetectQueryLocale"); + if(arg != null) + { + this.autoDetectQueryLocale = Boolean.parseBoolean(arg.toString()); + } + + arg = args.get("autoDetectQueryLocales"); + if(arg != null) + { + String[] locales = arg.toString().split(","); + for(String locale : locales) + { + String mappedLanguage = isKnownLocale(locale); + if(mappedLanguage != null) + { + autoDetectQueryLocales.add(mappedLanguage); + } + } + } + + + arg = args.get("fixedQueryLocales"); + if(arg != null) + { + String[] locales = arg.toString().split(","); + for(String locale : locales) + { + String mappedLanguage = isKnownLocale(locale); + if(mappedLanguage != null) + { + fixedQueryLocales.add(mappedLanguage); + } + } + } + } + } + + private String isKnownLocale(String locale) + { + for(String test : languages) + { + if(locale.equalsIgnoreCase(test)) + { + return test; + } + } + return null; + } + + + protected Pair getSearchParameters() + { + SearchParameters searchParameters = new SearchParameters(); + + Boolean isFilter = Boolean.FALSE; + + Iterable streams = req.getContentStreams(); + + JSONObject json = (JSONObject) req.getContext().get(ALFRESCO_JSON); + + if (json == null) + { + if (streams != null) + { + + try + { + Reader reader = null; + for (ContentStream stream : streams) + { + reader = new BufferedReader(new InputStreamReader(stream.getStream(), "UTF-8")); + } + + // TODO - replace with streaming-based solution e.g. SimpleJSON ContentHandler + if (reader != null) + { + json = new JSONObject(new JSONTokener(reader)); + req.getContext().put(ALFRESCO_JSON, json); + } + } + catch (JSONException e) + { + // This is expected when there is no json element to the request + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("IO Error parsing query parameters", e); + } + } + } + + if (json != null) + { + try + { + if (getString() != null) + { + if (getString().equals(AUTHORITY_FILTER_FROM_JSON)) + { + isFilter =Boolean.TRUE; + + ArrayList tenantList = new ArrayList(1); + JSONArray tenants = json.getJSONArray("tenants"); + for (int i = 0; i < tenants.length(); i++) + { + String tenantString = tenants.getString(i); + tenantList.add(tenantString); + } + + ArrayList authorityList = new ArrayList(1); + JSONArray authorities = json.getJSONArray("authorities"); + for (int i = 0; i < authorities.length(); i++) + { + String authorityString = authorities.getString(i); + authorityList.add(authorityString); + } + + char separator = getSeparator(authorityList); + + StringBuilder authQuery = new StringBuilder(); + StringBuilder denyQuery = new StringBuilder(); + + for (String tenant : tenantList) + { + for (String authority : authorityList) + { + if (separator == 0) + { + if (authQuery.length() > 0) + { + authQuery.append(" "); + denyQuery.append(" "); + } + switch (AuthorityType.getAuthorityType(authority)) + { + case USER: + authQuery.append("|AUTHORITY:\"").append(authority).append("\""); + denyQuery.append("|DENIED:\"").append(authority).append("\""); + break; + case GROUP: + case EVERYONE: + case GUEST: + if (tenant.length() == 0) + { + // Default tenant matches 4.0 + authQuery.append("|AUTHORITY:\"").append(authority).append("\""); + denyQuery.append("|DENIED:\"").append(authority).append("\""); + } + else + { + authQuery.append("|AUTHORITY:\"").append(authority).append("@").append(tenant).append("\""); + denyQuery.append("|DENIED:\"").append(authority).append("@").append(tenant).append("\""); + } + break; + default: + authQuery.append("|AUTHORITY:\"").append(authority).append("\""); + denyQuery.append("|DENIED:\"").append(authority).append("\""); + break; + } + } + else + { + if(authQuery.length() == 0) + { + authset = true; + authQuery.append("|AUTHSET:\""); + denyQuery.append("|DENYSET:\""); + } + + switch (AuthorityType.getAuthorityType(authority)) + { + case USER: + authQuery.append(separator).append(authority); + denyQuery.append(separator).append(authority); + break; + case GROUP: + case EVERYONE: + case GUEST: + if (tenant.length() == 0) + { + // Default tenant matches 4.0 + authQuery.append(separator).append(authority); + denyQuery.append(separator).append(authority); + } + else + { + authQuery.append(separator).append(authority).append("@").append(tenant); + denyQuery.append(separator).append(authority).append("@").append(tenant); + } + break; + default: + authQuery.append(separator).append(authority); + denyQuery.append(separator).append(authority); + break; + } + } + + } + } + if(separator != 0) + { + authQuery.append("\""); + denyQuery.append("\""); + } + + if (authQuery.length() > 0) + { + // Default to true for safety reasons. + final boolean anyDenyDenies = json.optBoolean("anyDenyDenies", true); + + if (anyDenyDenies) + { + authQuery.insert(0, "("). + append(") AND NOT ("). + append(denyQuery). + append(")"); + // Record that the clause has been added. + // We only ever set this to true for solr4+ + req.getContext().put("processedDenies", Boolean.TRUE); + } + searchParameters.setQuery(authQuery.toString()); + } + } + else if (getString().equals(TENANT_FILTER_FROM_JSON)) + { + isFilter =Boolean.TRUE; + + ArrayList tenantList = new ArrayList(1); + JSONArray tenants = json.getJSONArray("tenants"); + for (int i = 0; i < tenants.length(); i++) + { + String tenantString = tenants.getString(i); + tenantList.add(tenantString); + } + + StringBuilder tenantQuery = new StringBuilder(); + for (String tenant : tenantList) + { + if (tenantQuery.length() > 0) + { + tenantQuery.append(" "); + } + + if (tenant.length() > 0) + + { + tenantQuery.append("|TENANT:\"").append(tenant).append("\""); + } + else + { + // TODO: Need to check for the default tenant or no tenant (4.0) or we force a reindex + // requirement later ... + // Better to add default tenant to the 4.0 index + tenantQuery.append("|TENANT:\"").append("_DEFAULT_").append("\""); + // tenantQuery.append(" |(+ISNODE:T -TENANT:*)"); + } + + } + searchParameters.setQuery(tenantQuery.toString()); + } + else if (getString().equals(RERANK_QUERY_FROM_CONTEXT)) + { + String searchTerm = getParam("spellcheck.q"); + searchParameters.setQuery(searchTerm); + } + } + else + { + String query = json.getString("query"); + if (query != null) + { + searchParameters.setQuery(query); + } + } + + JSONArray locales = json.getJSONArray("locales"); + for (int i = 0; i < locales.length(); i++) + { + String localeString = locales.getString(i); + Locale locale = DefaultTypeConverter.INSTANCE.convert(Locale.class, localeString); + searchParameters.addLocale(locale); + } + + JSONArray templates = json.getJSONArray("templates"); + for (int i = 0; i < templates.length(); i++) + { + JSONObject template = templates.getJSONObject(i); + String name = template.getString("name"); + String queryTemplate = template.getString("template"); + searchParameters.addQueryTemplate(name, queryTemplate); + } + + JSONArray allAttributes = json.getJSONArray("allAttributes"); + for (int i = 0; i < allAttributes.length(); i++) + { + String allAttribute = allAttributes.getString(i); + searchParameters.addAllAttribute(allAttribute); + } + + searchParameters.setDefaultFTSOperator(Operator.valueOf(json.getString("defaultFTSOperator"))); + searchParameters.setDefaultFTSFieldConnective(Operator.valueOf(json.getString("defaultFTSFieldOperator"))); + if (json.has("mlAnalaysisMode")) + { + searchParameters.setMlAnalaysisMode(MLAnalysisMode.valueOf(json.getString("mlAnalaysisMode"))); + } + searchParameters.setNamespace(json.getString("defaultNamespace")); + + JSONArray textAttributes = json.getJSONArray("textAttributes"); + for (int i = 0; i < textAttributes.length(); i++) + { + String textAttribute = textAttributes.getString(i); + searchParameters.addTextAttribute(textAttribute); + } + + + searchParameters.setQueryConsistency(QueryConsistency.valueOf(json.getString("queryConsistency"))); + + } + catch (JSONException e) + { + // This is expected when there is no json element to the request + } + } + + if (json != null) + { + if (log.isDebugEnabled()) + { + log.debug(json.toString()); + } + } + + if (searchParameters.getQuery() == null) + { + searchParameters.setQuery(getString()); + } + + if (searchParameters.getLocales().size() == 0) + { + searchParameters.addLocale(I18NUtil.getLocale()); + } + + String defaultField = getParam(CommonParams.DF); + if (defaultField != null) + { + searchParameters.setDefaultFieldName(defaultField); + } + + if(autoDetectQueryLocale) + { + String searchTerm = getParam("spellcheck.q"); + if (searchTerm != null) + { + searchParameters.setSearchTerm(searchTerm); + List detetcted = detectLanguage(searchTerm); + if((detetcted != null) && (detetcted.size() > 0)) + { + Locale detectedLocale = Locale.forLanguageTag(detetcted.get(0).getLangCode()); + if(localeIsNotIncluded(searchParameters, detectedLocale)) + { + searchParameters.addLocale( Locale.forLanguageTag(detectedLocale.getLanguage())); + } + } + + } + } + + if(fixedQueryLocales.size() > 0) + { + for(String locale : fixedQueryLocales) + { + searchParameters.addLocale( Locale.forLanguageTag(locale)); + } + } + + // searchParameters.setMlAnalaysisMode(getMLAnalysisMode()); + searchParameters.setNamespace(NamespaceService.CONTENT_MODEL_1_0_URI); + + return new Pair(searchParameters, isFilter); + } + + /** + * @param searchParameters SearchParameters + * @param detectedLocale Locale + * @return boolean + */ + private boolean localeIsNotIncluded(SearchParameters searchParameters, Locale detectedLocale) + { + for(Locale locale : searchParameters.getLocales()) + { + if(locale.getLanguage().equals(detectedLocale.getLanguage())) + { + return false; + } + } + return true; + } + + + /** + * @param authorityList ArrayList + * @return char + */ + private char getSeparator(ArrayList authorityList) + { + StringBuilder builder = new StringBuilder(); + for(String auth : authorityList) + { + builder.append(auth); + } + String test = builder.toString(); + + for(int i = 0; i < SEPARATORS.length; i++) + { + if(test.indexOf(SEPARATORS[i]) == -1) + { + return SEPARATORS[i]; + } + } + return 0; + } + + /* (non-Javadoc) + * @see org.apache.solr.search.QParser#getSort(boolean) + */ + @Override + public SortSpec getSortSpec(boolean useGlobalParams) throws SyntaxError + { + + getQuery(); // ensure query is parsed first + + String sortStr = null; + String startS = null; + String rowsS = null; + + if (localParams != null) { + sortStr = localParams.get(CommonParams.SORT); + startS = localParams.get(CommonParams.START); + rowsS = localParams.get(CommonParams.ROWS); + + // if any of these parameters are present, don't go back to the global params + if (sortStr != null || startS != null || rowsS != null) { + useGlobalParams = false; + } + } + + if (useGlobalParams) { + if (sortStr ==null) { + sortStr = params.get(CommonParams.SORT); + } + if (startS==null) { + startS = params.get(CommonParams.START); + } + if (rowsS==null) { + rowsS = params.get(CommonParams.ROWS); + } + } + + int start = startS != null ? Integer.parseInt(startS) : 0; + int rows = rowsS != null ? Integer.parseInt(rowsS) : 10; + + // Fix sort fields here + if(sortStr != null) + { + StringBuilder builder = new StringBuilder(); + StringBuilder propertyBuilder = null; + char c; + for(int i = 0; i < sortStr.length(); i++) + { + c = sortStr.charAt(i); + if(propertyBuilder == null) + { + if(!Character.isWhitespace(c) && (c != ',')) + { + propertyBuilder = new StringBuilder(); + propertyBuilder.append(c); + } + else + { + builder.append(c); + } + } + else + { + if(Character.isWhitespace(c) || (c == ',')) + { + String toAppend = AlfrescoSolrDataModel.getInstance().mapProperty(propertyBuilder.toString(), FieldUse.SORT, getReq()); + builder.append(toAppend); + builder.append(c); + propertyBuilder = null; + } + else + { + propertyBuilder.append(c); + } + } + } + if(propertyBuilder != null) + { + String toAppend = AlfrescoSolrDataModel.getInstance().mapProperty(propertyBuilder.toString(), FieldUse.SORT, getReq()); + builder.append(toAppend); + } + sortStr = builder.toString(); + } + + if(sortStr != null) + { + sortStr = sortStr.replaceAll("^ID(\\s)", "id$1"); + sortStr = sortStr.replaceAll("(\\s)ID(\\s)", "$1id$2"); + } + SortSpec sort = SortSpecParsing.parseSortSpec(sortStr, req); + + sort.setOffset(start); + sort.setCount(rows); + return sort; + } + + + + private List detectLanguage(String content) { + if (content.trim().length() == 0) { // to be consistent with the tika impl? + log.debug("No input text to detect language from, returning empty list"); + return Collections.emptyList(); + } + + try { + Detector detector = DetectorFactory.create(); + detector.append(content); + ArrayList langlist = detector.getProbabilities(); + ArrayList solrLangList = new ArrayList<>(); + for (Language l: langlist) + { + if((autoDetectQueryLocales.size() == 0) || (autoDetectQueryLocales.contains(l.lang))) + { + solrLangList.add(new DetectedLanguage(l.lang, l.prob)); + } + } + return solrLangList; + } catch (LangDetectException e) { + log.debug("Could not determine language, returning empty list: ", e); + return Collections.emptyList(); + } + } + + public class DetectedLanguage { + private final String langCode; + private final Double certainty; + + DetectedLanguage(String lang, Double certainty) { + this.langCode = lang; + this.certainty = certainty; + } + + /** + * Returns the detected language code + * @return language code as a string + */ + public String getLangCode() { + return langCode; + } + + /** + * Returns the detected certainty for this language + * @return certainty as a value between 0.0 and 1.0 where 1.0 is 100% certain + */ + public Double getCertainty() { + return certainty; + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractSolrCachingScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractSolrCachingScorer.java index fa3704536..24a4af4e3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractSolrCachingScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractSolrCachingScorer.java @@ -1,198 +1,198 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.stream.LongStream; - -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - - -/** - * @author Andy - * - */ -public abstract class AbstractSolrCachingScorer extends Scorer -{ - protected static class LongCache - { - private static int CACHE_SIZE = 1000000; - - private LongCache(){} - - static final long cache[] = LongStream.range(0, CACHE_SIZE).toArray(); - } - - protected static Long getLong(long l) { - if(l > LongCache.CACHE_SIZE) - { - return Long.valueOf(l); - } - else - { - return LongCache.cache[(int)l]; - } - } - - - - SolrCachingScorerDoIdSetIterator iterator; - - AbstractSolrCachingScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight); - iterator = new SolrCachingScorerDoIdSetIterator(in, context, searcher); - } - - @Override - public float score() throws IOException - { - return 1.0f; - } - - // TODO: implement - @Override - public int freq() throws IOException - { - return 1; - } - - @Override - public int docID() - { - return iterator.docID(); - } - - @Override - public DocIdSetIterator iterator() - { - return iterator; - } - - - - private static class SolrCachingScorerDoIdSetIterator extends DocIdSetIterator - { - BitDocSet matches; - - int doc = -1; - - FixedBitSet bitSet; - - LeafReaderContext context; - - SolrCachingScorerDoIdSetIterator(DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - this.context = context; - - if (in instanceof BitDocSet) - { - matches = (BitDocSet) in; - } - else - { - this.matches = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - for (DocIterator it = in.iterator(); it.hasNext(); /* */) - { - matches.addUnique(it.nextDoc()); - } - } - bitSet = matches.getBits(); - - doc = getBase() - 1; - } - - private boolean next() - { - if(doc+1 < bitSet.length()) - { - doc = bitSet.nextSetBit(doc+1); - return (doc != NO_MORE_DOCS) && (doc < (getBase() + context.reader().maxDoc())); - } - else - { - return false; - } - } - - private int getBase() - { - return context.docBase; - } - - @Override - public int nextDoc() throws IOException - { - while (next()) - { - return docID(); - } - return NO_MORE_DOCS; - } - - - @Override - public int docID() - { - // TODO: check this expression as for next() - if ((doc > -1) && (doc != NO_MORE_DOCS)) - { - return doc - getBase(); - } - return doc; - } - - @Override - public int advance(int target) throws IOException - { - while (next()) - { - final int current = docID(); - if (current >= target) - { - return current; - } - } - return NO_MORE_DOCS; - } - - // TODO: implement - @Override - public long cost() - { - return 1; - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.stream.LongStream; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + + +/** + * @author Andy + * + */ +public abstract class AbstractSolrCachingScorer extends Scorer +{ + protected static class LongCache + { + private static int CACHE_SIZE = 1000000; + + private LongCache(){} + + static final long cache[] = LongStream.range(0, CACHE_SIZE).toArray(); + } + + protected static Long getLong(long l) { + if(l > LongCache.CACHE_SIZE) + { + return Long.valueOf(l); + } + else + { + return LongCache.cache[(int)l]; + } + } + + + + SolrCachingScorerDoIdSetIterator iterator; + + AbstractSolrCachingScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight); + iterator = new SolrCachingScorerDoIdSetIterator(in, context, searcher); + } + + @Override + public float score() throws IOException + { + return 1.0f; + } + + // TODO: implement + @Override + public int freq() throws IOException + { + return 1; + } + + @Override + public int docID() + { + return iterator.docID(); + } + + @Override + public DocIdSetIterator iterator() + { + return iterator; + } + + + + private static class SolrCachingScorerDoIdSetIterator extends DocIdSetIterator + { + BitDocSet matches; + + int doc = -1; + + FixedBitSet bitSet; + + LeafReaderContext context; + + SolrCachingScorerDoIdSetIterator(DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + this.context = context; + + if (in instanceof BitDocSet) + { + matches = (BitDocSet) in; + } + else + { + this.matches = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + for (DocIterator it = in.iterator(); it.hasNext(); /* */) + { + matches.addUnique(it.nextDoc()); + } + } + bitSet = matches.getBits(); + + doc = getBase() - 1; + } + + private boolean next() + { + if(doc+1 < bitSet.length()) + { + doc = bitSet.nextSetBit(doc+1); + return (doc != NO_MORE_DOCS) && (doc < (getBase() + context.reader().maxDoc())); + } + else + { + return false; + } + } + + private int getBase() + { + return context.docBase; + } + + @Override + public int nextDoc() throws IOException + { + while (next()) + { + return docID(); + } + return NO_MORE_DOCS; + } + + + @Override + public int docID() + { + // TODO: check this expression as for next() + if ((doc > -1) && (doc != NO_MORE_DOCS)) + { + return doc - getBase(); + } + return doc; + } + + @Override + public int advance(int target) throws IOException + { + while (next()) + { + final int current = docID(); + if (current >= target) + { + return current; + } + } + return NO_MORE_DOCS; + } + + // TODO: implement + @Override + public long cost() + { + return 1; + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractStructuredFieldPosition.java index ce05a219a..6b266f3ef 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractStructuredFieldPosition.java @@ -1,169 +1,169 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -public abstract class AbstractStructuredFieldPosition implements StructuredFieldPosition -{ - private String termText; - - private boolean isTerminal; - - private boolean isAbsolute; - - private CachingTermPositions tps; - - public AbstractStructuredFieldPosition(String termText, boolean isTerminal, boolean isAbsolute) - { - super(); - this.termText = termText; - this.isTerminal = isTerminal; - this.isAbsolute = isAbsolute; - } - - public boolean isTerminal() - { - return isTerminal; - } - - protected void setTerminal(boolean isTerminal) - { - this.isTerminal = isTerminal; - } - - public boolean isAbsolute() - { - return isAbsolute; - } - - public boolean isRelative() - { - return !isAbsolute; - } - - public String getTermText() - { - return termText; - } - - public int getPosition() - { - return -1; - } - - public void setCachingTermPositions(CachingTermPositions tps) - { - this.tps = tps; - } - - public CachingTermPositions getCachingTermPositions() - { - return this.tps; - } - - - - public boolean allowsLinkingBySelf() - { - return false; - } - - public boolean allowslinkingByParent() - { - return true; - } - - public boolean linkParent() - { - return true; - } - - public boolean linkSelf() - { - return false; - } - - public String toString() - { - StringBuffer buffer = new StringBuffer(256); - buffer.append(getDescription()); - buffer.append("<"+getTermText()+"> at "+getPosition()); - buffer.append(" Terminal = "+isTerminal()); - buffer.append(" Absolute = "+isAbsolute()); - return buffer.toString(); - } - - public abstract String getDescription(); - - public boolean isDescendant() - { - return false; - } - - public boolean matchesAll() - { - return getCachingTermPositions() == null; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + (isAbsolute ? 1231 : 1237); - result = prime * result + (isTerminal ? 1231 : 1237); - result = prime * result + ((termText == null) ? 0 : termText.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - AbstractStructuredFieldPosition other = (AbstractStructuredFieldPosition) obj; - if (isAbsolute != other.isAbsolute) - return false; - if (isTerminal != other.isTerminal) - return false; - if (termText == null) - { - if (other.termText != null) - return false; - } - else if (!termText.equals(other.termText)) - return false; - return true; - } - - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +public abstract class AbstractStructuredFieldPosition implements StructuredFieldPosition +{ + private String termText; + + private boolean isTerminal; + + private boolean isAbsolute; + + private CachingTermPositions tps; + + public AbstractStructuredFieldPosition(String termText, boolean isTerminal, boolean isAbsolute) + { + super(); + this.termText = termText; + this.isTerminal = isTerminal; + this.isAbsolute = isAbsolute; + } + + public boolean isTerminal() + { + return isTerminal; + } + + protected void setTerminal(boolean isTerminal) + { + this.isTerminal = isTerminal; + } + + public boolean isAbsolute() + { + return isAbsolute; + } + + public boolean isRelative() + { + return !isAbsolute; + } + + public String getTermText() + { + return termText; + } + + public int getPosition() + { + return -1; + } + + public void setCachingTermPositions(CachingTermPositions tps) + { + this.tps = tps; + } + + public CachingTermPositions getCachingTermPositions() + { + return this.tps; + } + + + + public boolean allowsLinkingBySelf() + { + return false; + } + + public boolean allowslinkingByParent() + { + return true; + } + + public boolean linkParent() + { + return true; + } + + public boolean linkSelf() + { + return false; + } + + public String toString() + { + StringBuffer buffer = new StringBuffer(256); + buffer.append(getDescription()); + buffer.append("<"+getTermText()+"> at "+getPosition()); + buffer.append(" Terminal = "+isTerminal()); + buffer.append(" Absolute = "+isAbsolute()); + return buffer.toString(); + } + + public abstract String getDescription(); + + public boolean isDescendant() + { + return false; + } + + public boolean matchesAll() + { + return getCachingTermPositions() == null; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (isAbsolute ? 1231 : 1237); + result = prime * result + (isTerminal ? 1231 : 1237); + result = prime * result + ((termText == null) ? 0 : termText.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AbstractStructuredFieldPosition other = (AbstractStructuredFieldPosition) obj; + if (isAbsolute != other.isAbsolute) + return false; + if (isTerminal != other.isTerminal) + return false; + if (termText == null) + { + if (other.termText != null) + return false; + } + else if (!termText.equals(other.termText)) + return false; + return true; + } + + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoDefaultTextFields.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoDefaultTextFields.java index 914cedd86..805271cf8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoDefaultTextFields.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoDefaultTextFields.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoFTSQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoFTSQParserPlugin.java index b8355fde2..979f6e134 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoFTSQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoFTSQParserPlugin.java @@ -1,153 +1,153 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import org.alfresco.repo.search.impl.parsers.FTSQueryParser.RerankPhase; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.util.Pair; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class AlfrescoFTSQParserPlugin extends QParserPlugin -{ - protected final static Logger log = LoggerFactory.getLogger(AlfrescoFTSQParserPlugin.class); - - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new AlfrescoFTSQParser(qstr, localParams, params, req, args); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - @Override - public void init(NamedList args) - { - this.args = args; - } - - public static class AlfrescoFTSQParser extends AbstractQParser - { - private Log logger = LogFactory.getLog(AlfrescoFTSQParser.class); - private RerankPhase rerankPhase = RerankPhase.SINGLE_PASS_WITH_AUTO_PHRASE; - private boolean postfilter; - - public AlfrescoFTSQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) - { - super(qstr, localParams, params, req, args); - Object arg = args.get("rerankPhase"); - if(arg != null) - { - rerankPhase = RerankPhase.valueOf(arg.toString()); - } - - /* - * This turns on the postFilter - */ - - postfilter = Boolean.parseBoolean(System.getProperty("alfresco.postfilter", - req.getCore().getCoreDescriptor().getCoreProperty("alfresco.postfilter", - "true"))); - logger.debug("Post filter value: " + postfilter); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - try - { - Pair searchParametersAndFilter = getSearchParameters(); - - Query query = AlfrescoSolrDataModel.getInstance().getFTSQuery(searchParametersAndFilter, req, rerankPhase); - if(log.isDebugEnabled()) - { - log.debug("AFTS QP query as lucene:\t "+query); - } - - if(authset && postfilter) - { - /* - * The cost of 200 turns on the postfilter inside Solr - * The postfilter query pulls out all the post filters in the - * query and applies them. - */ - return new PostFilterQuery(200, query); - } - - /* - * This assertion is designed to ensure that if the System property alfresco.postfilter - * is set to true then the PostFilter was actually used. - * - * If authset is false the assertion will short circuit before it tests the - * alfresco.postfilter property because the PostFilter is only applied for authset queries. - * - * If authset is true, the assertion checks to make sure the alfresco.postfilter property is either - * false or unset. If the alfresco.postfilter property is true it should not have gotten to this point - * of the code. - */ - - assert (authset == false || - Boolean.parseBoolean(System.getProperty("alfresco.postfilter","false")) == false); - - return query; - } - catch(ParseException e) - { - throw new SyntaxError(e); - } - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import org.alfresco.repo.search.impl.parsers.FTSQueryParser.RerankPhase; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class AlfrescoFTSQParserPlugin extends QParserPlugin +{ + protected final static Logger log = LoggerFactory.getLogger(AlfrescoFTSQParserPlugin.class); + + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new AlfrescoFTSQParser(qstr, localParams, params, req, args); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + @Override + public void init(NamedList args) + { + this.args = args; + } + + public static class AlfrescoFTSQParser extends AbstractQParser + { + private Log logger = LogFactory.getLog(AlfrescoFTSQParser.class); + private RerankPhase rerankPhase = RerankPhase.SINGLE_PASS_WITH_AUTO_PHRASE; + private boolean postfilter; + + public AlfrescoFTSQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) + { + super(qstr, localParams, params, req, args); + Object arg = args.get("rerankPhase"); + if(arg != null) + { + rerankPhase = RerankPhase.valueOf(arg.toString()); + } + + /* + * This turns on the postFilter + */ + + postfilter = Boolean.parseBoolean(System.getProperty("alfresco.postfilter", + req.getCore().getCoreDescriptor().getCoreProperty("alfresco.postfilter", + "true"))); + logger.debug("Post filter value: " + postfilter); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + try + { + Pair searchParametersAndFilter = getSearchParameters(); + + Query query = AlfrescoSolrDataModel.getInstance().getFTSQuery(searchParametersAndFilter, req, rerankPhase); + if(log.isDebugEnabled()) + { + log.debug("AFTS QP query as lucene:\t "+query); + } + + if(authset && postfilter) + { + /* + * The cost of 200 turns on the postfilter inside Solr + * The postfilter query pulls out all the post filters in the + * query and applies them. + */ + return new PostFilterQuery(200, query); + } + + /* + * This assertion is designed to ensure that if the System property alfresco.postfilter + * is set to true then the PostFilter was actually used. + * + * If authset is false the assertion will short circuit before it tests the + * alfresco.postfilter property because the PostFilter is only applied for authset queries. + * + * If authset is true, the assertion checks to make sure the alfresco.postfilter property is either + * false or unset. If the alfresco.postfilter property is true it should not have gotten to this point + * of the code. + */ + + assert (authset == false || + Boolean.parseBoolean(System.getProperty("alfresco.postfilter","false")) == false); + + return query; + } + catch(ParseException e) + { + throw new SyntaxError(e); + } + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoLuceneQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoLuceneQParserPlugin.java index 513466758..b7756ea76 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoLuceneQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoLuceneQParserPlugin.java @@ -1,149 +1,149 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import org.alfresco.repo.search.impl.parsers.FTSQueryParser; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.ContextAwareQuery; -import org.alfresco.util.Pair; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class AlfrescoLuceneQParserPlugin extends QParserPlugin -{ - protected final static Logger log = LoggerFactory.getLogger(AlfrescoLuceneQParserPlugin.class); - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new AlfrescoLuceneQParser(qstr, localParams, params, req, args); - - } - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - public void init(NamedList args) - { - this.args = args; - } - - public static class AlfrescoLuceneQParser extends AbstractQParser - { - public AlfrescoLuceneQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) - { - super(qstr, localParams, params, req, args); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - Pair searchParametersAndFilter = getSearchParameters(); - SearchParameters searchParameters = searchParametersAndFilter.getFirst(); - Boolean isFilter = searchParametersAndFilter.getSecond(); - - Solr4QueryParser qp = AlfrescoSolrDataModel.getInstance().getLuceneQueryParser(searchParameters, req, FTSQueryParser.RerankPhase.SINGLE_PASS_WITH_AUTO_PHRASE); - Query query; - try - { - // escape / not in a string and not already escaped - String escapedQ = escape(searchParameters.getQuery()); - query = qp.parse(escapedQ); - } - catch (ParseException pe) - { - throw new SyntaxError(pe); - } - ContextAwareQuery contextAwareQuery = new ContextAwareQuery(query, Boolean.TRUE.equals(isFilter) ? null : searchParameters); - if(log.isDebugEnabled()) - { - log.debug("Lucene QP query as lucene:\t "+contextAwareQuery); - } - return contextAwareQuery; - } - - protected String escape(String in) - { - if (in.indexOf('/') == -1) - { - // If we have no forward slashes, there is nothing to do. - return in; - } - - StringBuilder sb = new StringBuilder(in); - boolean quoted = false; - boolean escaped = false; - for (int i = 0; i < sb.length(); i++) - { - char c = sb.charAt(i); - if (c == '"') - { - quoted = !quoted; - continue; - } - else if (c == '\\') - { - escaped = true; - continue; - } - if (c == '/' && !quoted && !escaped) - { - sb.insert(i, "\\"); - i++; // jump over modification, avoid infinite loop. - } - escaped = false; - } - String out = sb.toString(); - return out; - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.ContextAwareQuery; +import org.alfresco.util.Pair; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class AlfrescoLuceneQParserPlugin extends QParserPlugin +{ + protected final static Logger log = LoggerFactory.getLogger(AlfrescoLuceneQParserPlugin.class); + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new AlfrescoLuceneQParser(qstr, localParams, params, req, args); + + } + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + public void init(NamedList args) + { + this.args = args; + } + + public static class AlfrescoLuceneQParser extends AbstractQParser + { + public AlfrescoLuceneQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) + { + super(qstr, localParams, params, req, args); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + Pair searchParametersAndFilter = getSearchParameters(); + SearchParameters searchParameters = searchParametersAndFilter.getFirst(); + Boolean isFilter = searchParametersAndFilter.getSecond(); + + Solr4QueryParser qp = AlfrescoSolrDataModel.getInstance().getLuceneQueryParser(searchParameters, req, FTSQueryParser.RerankPhase.SINGLE_PASS_WITH_AUTO_PHRASE); + Query query; + try + { + // escape / not in a string and not already escaped + String escapedQ = escape(searchParameters.getQuery()); + query = qp.parse(escapedQ); + } + catch (ParseException pe) + { + throw new SyntaxError(pe); + } + ContextAwareQuery contextAwareQuery = new ContextAwareQuery(query, Boolean.TRUE.equals(isFilter) ? null : searchParameters); + if(log.isDebugEnabled()) + { + log.debug("Lucene QP query as lucene:\t "+contextAwareQuery); + } + return contextAwareQuery; + } + + protected String escape(String in) + { + if (in.indexOf('/') == -1) + { + // If we have no forward slashes, there is nothing to do. + return in; + } + + StringBuilder sb = new StringBuilder(in); + boolean quoted = false; + boolean escaped = false; + for (int i = 0; i < sb.length(); i++) + { + char c = sb.charAt(i); + if (c == '"') + { + quoted = !quoted; + continue; + } + else if (c == '\\') + { + escaped = true; + continue; + } + if (c == '/' && !quoted && !escaped) + { + sb.insert(i, "\\"); + i++; // jump over modification, avoid infinite loop. + } + escaped = false; + } + String out = sb.toString(); + return out; + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoReRankQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoReRankQParserPlugin.java index aa45b708c..d15ba04b0 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoReRankQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoReRankQParserPlugin.java @@ -1,522 +1,522 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.LeafCollector; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.QueryRescorer; -import org.apache.lucene.search.ScoreDoc; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.search.TopDocsCollector; -import org.apache.lucene.search.TopFieldCollector; -import org.apache.lucene.search.TopScoreDocCollector; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.BytesRef; -import org.apache.solr.common.SolrException; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.handler.component.MergeStrategy; -import org.apache.solr.handler.component.QueryElevationComponent; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.request.SolrRequestInfo; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.RankQuery; -import org.apache.solr.search.QueryCommand; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.SyntaxError; - -import com.carrotsearch.hppc.IntFloatHashMap; -import com.carrotsearch.hppc.IntIntHashMap; - -/* -* -* Syntax: q=*:*&rq={!rerank reRankQuery=$rqq reRankDocs=300 reRankWeight=3} -* -*/ - -public class AlfrescoReRankQParserPlugin extends QParserPlugin { - - public static final String NAME = "rerank"; - private static Query defaultQuery = new MatchAllDocsQuery(); - - public void init(NamedList args) { - } - - public QParser createParser(String query, SolrParams localParams, SolrParams params, SolrQueryRequest req) { - return new ReRankQParser(query, localParams, params, req); - } - - private class ReRankQParser extends QParser { - - public ReRankQParser(String query, SolrParams localParams, SolrParams params, SolrQueryRequest req) { - super(query, localParams, params, req); - } - - public Query parse() throws SyntaxError { - String reRankQueryString = localParams.get("reRankQuery"); - boolean scale = localParams.getBool("scale", false); - QParser reRankParser = QParser.getParser(reRankQueryString, null, req); - Query reRankQuery = reRankParser.parse(); - - int reRankDocs = localParams.getInt("reRankDocs", 200); - reRankDocs = Math.max(1, reRankDocs); // - - double reRankWeight = localParams.getDouble("reRankWeight",2.0d); - - int start = params.getInt(CommonParams.START,0); - int rows = params.getInt(CommonParams.ROWS,10); - int length = start+rows; - return new ReRankQuery(reRankQuery, reRankDocs, reRankWeight, length, scale); - } - } - - private class ReRankQuery extends RankQuery { - private Query mainQuery = defaultQuery; - private Query reRankQuery; - private int reRankDocs; - private int length; - private double reRankWeight; - private boolean scale; - private Map boostedPriority; - - public int hashCode() { - return mainQuery.hashCode()+reRankQuery.hashCode()+(int)reRankWeight+reRankDocs+(scale ? 1 : 0); - } - - public boolean equals(Object o) { - if(o instanceof ReRankQuery) { - ReRankQuery rrq = (ReRankQuery)o; - return (mainQuery.equals(rrq.mainQuery) && - reRankQuery.equals(rrq.reRankQuery) && - reRankWeight == rrq.reRankWeight && - reRankDocs == rrq.reRankDocs && - scale == rrq.scale); - } - return false; - } - - public ReRankQuery(Query reRankQuery, int reRankDocs, double reRankWeight, int length, boolean scale) { - this.reRankQuery = reRankQuery; - this.reRankDocs = reRankDocs; - this.reRankWeight = reRankWeight; - this.length = length; - this.scale = scale; - } - - public RankQuery wrap(Query _mainQuery) { - if(_mainQuery != null){ - this.mainQuery = _mainQuery; - } - return this; - } - - public MergeStrategy getMergeStrategy() { - return null; - } - - public TopDocsCollector getTopDocsCollector(int len, QueryCommand cmd, IndexSearcher searcher) throws IOException { - - if(this.boostedPriority == null) { - SolrRequestInfo info = SolrRequestInfo.getRequestInfo(); - if(info != null) { - Map context = info.getReq().getContext(); - this.boostedPriority = (Map)context.get(QueryElevationComponent.BOOSTED_PRIORITY); - } - } - - return new ReRankCollector(reRankDocs, length, reRankQuery, reRankWeight, cmd, searcher, boostedPriority, scale); - } - - public String toString(String s) { - return "{!rerank mainQuery='"+mainQuery.toString()+ - "' reRankQuery='"+reRankQuery.toString()+ - "' reRankDocs="+reRankDocs+ - " reRankWeigh="+reRankWeight+"}"; - } - - - public Query rewrite(IndexReader reader) throws IOException { - Query q = mainQuery.rewrite(reader); - if(q == mainQuery) { - return this; - } else { - return clone().wrap(q); - } - } - - public ReRankQuery clone() { - ReRankQuery clonedQuery = new ReRankQuery(reRankQuery, reRankDocs, reRankWeight, length, scale); - return clonedQuery; - } - - - public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException{ - return new ReRankWeight(mainQuery, reRankQuery, reRankWeight, searcher); - } - } - - private class ReRankWeight extends Weight{ - private Query reRankQuery; - private IndexSearcher searcher; - private Weight mainWeight; - private double reRankWeight; - - public ReRankWeight(Query mainQuery, Query reRankQuery, double reRankWeight, IndexSearcher searcher) throws IOException { - super(reRankQuery); - this.reRankQuery = reRankQuery; - this.searcher = searcher; - this.reRankWeight = reRankWeight; - this.mainWeight = mainQuery.createWeight(searcher, true); - } - - @Override - public void extractTerms(Set terms) { - this.mainWeight.extractTerms(terms); - - } - - public float getValueForNormalization() throws IOException { - return mainWeight.getValueForNormalization(); - } - - public Scorer scorer(LeafReaderContext context) throws IOException { - return mainWeight.scorer(context); - } - - public void normalize(float norm, float topLevelBoost) { - mainWeight.normalize(norm, topLevelBoost); - } - - public Explanation explain(LeafReaderContext context, int doc) throws IOException { - Explanation mainExplain = mainWeight.explain(context, doc); - return new QueryRescorer(getQuery()) { - @Override - protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { - float score = firstPassScore; - if (secondPassMatches) { - score += reRankWeight * secondPassScore; - } - return score; - } - }.explain(searcher, mainExplain, context.docBase+doc); - } - - } - - private class ReRankCollector extends TopDocsCollector { - - private Query reRankQuery; - private TopDocsCollector mainCollector; - private IndexSearcher searcher; - private int reRankDocs; - private int length; - private double reRankWeight; - private Map boostedPriority; - private float minScore = Float.MAX_VALUE; - private float maxScore = -Float.MAX_VALUE; - private Scorer localScorer; - private boolean scale; - - public ReRankCollector(int reRankDocs, - int length, - Query reRankQuery, - double reRankWeight, - QueryCommand cmd, - IndexSearcher searcher, - Map boostedPriority, - boolean scale) throws IOException { - super(null); - this.reRankQuery = reRankQuery; - this.reRankDocs = reRankDocs; - this.length = length; - this.boostedPriority = boostedPriority; - this.scale = scale; - Sort sort = cmd.getSort(); - if(sort == null) { - this.mainCollector = TopScoreDocCollector.create(Math.max(this.reRankDocs, length), null); - } else { - sort = sort.rewrite(searcher); - this.mainCollector = TopFieldCollector.create(sort, Math.max(this.reRankDocs, length), null, false, true, true); - } - this.searcher = searcher; - this.reRankWeight = reRankWeight; - } - - public int getTotalHits() { - return mainCollector.getTotalHits(); - } - - public TopDocs topDocs(int start, int howMany) { - - try { - - TopDocs mainDocs = mainCollector.topDocs(0, Math.max(reRankDocs, length)); - - if(mainDocs.totalHits == 0 || mainDocs.scoreDocs.length == 0) { - return mainDocs; - } - - if(reRankDocs == 0) { - scaleScores(mainDocs, new HashMap()); - return mainDocs; - } - - if(boostedPriority != null) { - SolrRequestInfo info = SolrRequestInfo.getRequestInfo(); - Map requestContext = null; - if(info != null) { - requestContext = info.getReq().getContext(); - } - - IntIntHashMap boostedDocs = QueryElevationComponent.getBoostDocs((SolrIndexSearcher)searcher, boostedPriority, requestContext); - - ScoreDoc[] mainScoreDocs = mainDocs.scoreDocs; - ScoreDoc[] reRankScoreDocs = new ScoreDoc[Math.min(mainScoreDocs.length, reRankDocs)]; - System.arraycopy(mainScoreDocs,0,reRankScoreDocs,0,reRankScoreDocs.length); - - mainDocs.scoreDocs = reRankScoreDocs; - - Map scoreMap = getScoreMap(mainDocs.scoreDocs, mainDocs.scoreDocs.length); - - TopDocs rescoredDocs = new QueryRescorer(reRankQuery) { - @Override - protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { - float score = firstPassScore; - if (secondPassMatches) { - score += reRankWeight * secondPassScore; - } - return score; - } - }.rescore(searcher, mainDocs, mainDocs.scoreDocs.length); - - Arrays.sort(rescoredDocs.scoreDocs, new BoostedComp(boostedDocs, mainDocs.scoreDocs, rescoredDocs.getMaxScore())); - - //Lower howMany if we've collected fewer documents. - howMany = Math.min(howMany, mainScoreDocs.length); - - if(howMany == rescoredDocs.scoreDocs.length) { - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; // Just return the rescoredDocs - } else if(howMany > rescoredDocs.scoreDocs.length) { - //We need to return more then we've reRanked, so create the combined page. - ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; - System.arraycopy(mainScoreDocs, 0, scoreDocs, 0, scoreDocs.length); //lay down the initial docs - System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, rescoredDocs.scoreDocs.length);//overlay the re-ranked docs. - rescoredDocs.scoreDocs = scoreDocs; - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; - } else { - //We've rescored more then we need to return. - ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; - System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, howMany); - rescoredDocs.scoreDocs = scoreDocs; - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; - } - - } else { - - ScoreDoc[] mainScoreDocs = mainDocs.scoreDocs; - - /* - * Create the array for the reRankScoreDocs. - */ - ScoreDoc[] reRankScoreDocs = new ScoreDoc[Math.min(mainScoreDocs.length, reRankDocs)]; - - /* - * Copy the initial results into the reRankScoreDocs array. - */ - System.arraycopy(mainScoreDocs, 0, reRankScoreDocs, 0, reRankScoreDocs.length); - - mainDocs.scoreDocs = reRankScoreDocs; - - Map scoreMap = getScoreMap(mainDocs.scoreDocs, mainDocs.scoreDocs.length); - - TopDocs rescoredDocs = new QueryRescorer(reRankQuery) { - @Override - protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { - float score = firstPassScore; - if (secondPassMatches) { - score += reRankWeight * secondPassScore; - } - return score; - } - }.rescore(searcher, mainDocs, mainDocs.scoreDocs.length); - - //Lower howMany to return if we've collected fewer documents. - howMany = Math.min(howMany, mainScoreDocs.length); - - if(howMany == rescoredDocs.scoreDocs.length) { - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; // Just return the rescoredDocs - } else if(howMany > rescoredDocs.scoreDocs.length) { - - //We need to return more then we've reRanked, so create the combined page. - ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; - //lay down the initial docs - System.arraycopy(mainScoreDocs, 0, scoreDocs, 0, scoreDocs.length); - //overlay the rescoreds docs - System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, rescoredDocs.scoreDocs.length); - rescoredDocs.scoreDocs = scoreDocs; - if(scale) { - assert(scoreMap != null); - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; - } else { - //We've rescored more then we need to return. - ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; - System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, howMany); - rescoredDocs.scoreDocs = scoreDocs; - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; - } - } - } catch (Exception e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); - } - } - - @Override - public LeafCollector getLeafCollector(LeafReaderContext context) - throws IOException { - return mainCollector.getLeafCollector(context); - } - - @Override - public boolean needsScores() { - return true; - } - } - - private void scaleScores(TopDocs topDocs, Map scoreMap) - { - float maxScore = topDocs.getMaxScore(); - float newMax = -Float.MAX_VALUE; - - for(ScoreDoc scoreDoc : topDocs.scoreDocs) { - float score = scoreDoc.score; - - Float oldScore = scoreMap.get(scoreDoc.doc); - - // check if the score has been changed after rescoring - boolean rescored = oldScore != null && score != oldScore; - - // If maxScore is different from 0, the score is divided by maxscore - scoreDoc.score = score / (maxScore != 0? maxScore : 1); - - // If the document has been rescored, the score is increased by 1. - // This results in having all the rescored element scores in (1,2] range. - if (rescored) - { - scoreDoc.score += 1; - } - - if(scoreDoc.score > newMax) - { - newMax = scoreDoc.score; - } - } - - assert(newMax <= 2); - topDocs.setMaxScore(newMax); - } - - private Map getScoreMap(ScoreDoc[] scoreDocs, int num) { - Map scoreMap = new HashMap(); - for(int i=0; i score2) { - return -1; - } else if(score1 < score2) { - return 1; - } else { - return 0; - } - } - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.LeafCollector; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryRescorer; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.TopDocsCollector; +import org.apache.lucene.search.TopFieldCollector; +import org.apache.lucene.search.TopScoreDocCollector; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.component.MergeStrategy; +import org.apache.solr.handler.component.QueryElevationComponent; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestInfo; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.RankQuery; +import org.apache.solr.search.QueryCommand; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SyntaxError; + +import com.carrotsearch.hppc.IntFloatHashMap; +import com.carrotsearch.hppc.IntIntHashMap; + +/* +* +* Syntax: q=*:*&rq={!rerank reRankQuery=$rqq reRankDocs=300 reRankWeight=3} +* +*/ + +public class AlfrescoReRankQParserPlugin extends QParserPlugin { + + public static final String NAME = "rerank"; + private static Query defaultQuery = new MatchAllDocsQuery(); + + public void init(NamedList args) { + } + + public QParser createParser(String query, SolrParams localParams, SolrParams params, SolrQueryRequest req) { + return new ReRankQParser(query, localParams, params, req); + } + + private class ReRankQParser extends QParser { + + public ReRankQParser(String query, SolrParams localParams, SolrParams params, SolrQueryRequest req) { + super(query, localParams, params, req); + } + + public Query parse() throws SyntaxError { + String reRankQueryString = localParams.get("reRankQuery"); + boolean scale = localParams.getBool("scale", false); + QParser reRankParser = QParser.getParser(reRankQueryString, null, req); + Query reRankQuery = reRankParser.parse(); + + int reRankDocs = localParams.getInt("reRankDocs", 200); + reRankDocs = Math.max(1, reRankDocs); // + + double reRankWeight = localParams.getDouble("reRankWeight",2.0d); + + int start = params.getInt(CommonParams.START,0); + int rows = params.getInt(CommonParams.ROWS,10); + int length = start+rows; + return new ReRankQuery(reRankQuery, reRankDocs, reRankWeight, length, scale); + } + } + + private class ReRankQuery extends RankQuery { + private Query mainQuery = defaultQuery; + private Query reRankQuery; + private int reRankDocs; + private int length; + private double reRankWeight; + private boolean scale; + private Map boostedPriority; + + public int hashCode() { + return mainQuery.hashCode()+reRankQuery.hashCode()+(int)reRankWeight+reRankDocs+(scale ? 1 : 0); + } + + public boolean equals(Object o) { + if(o instanceof ReRankQuery) { + ReRankQuery rrq = (ReRankQuery)o; + return (mainQuery.equals(rrq.mainQuery) && + reRankQuery.equals(rrq.reRankQuery) && + reRankWeight == rrq.reRankWeight && + reRankDocs == rrq.reRankDocs && + scale == rrq.scale); + } + return false; + } + + public ReRankQuery(Query reRankQuery, int reRankDocs, double reRankWeight, int length, boolean scale) { + this.reRankQuery = reRankQuery; + this.reRankDocs = reRankDocs; + this.reRankWeight = reRankWeight; + this.length = length; + this.scale = scale; + } + + public RankQuery wrap(Query _mainQuery) { + if(_mainQuery != null){ + this.mainQuery = _mainQuery; + } + return this; + } + + public MergeStrategy getMergeStrategy() { + return null; + } + + public TopDocsCollector getTopDocsCollector(int len, QueryCommand cmd, IndexSearcher searcher) throws IOException { + + if(this.boostedPriority == null) { + SolrRequestInfo info = SolrRequestInfo.getRequestInfo(); + if(info != null) { + Map context = info.getReq().getContext(); + this.boostedPriority = (Map)context.get(QueryElevationComponent.BOOSTED_PRIORITY); + } + } + + return new ReRankCollector(reRankDocs, length, reRankQuery, reRankWeight, cmd, searcher, boostedPriority, scale); + } + + public String toString(String s) { + return "{!rerank mainQuery='"+mainQuery.toString()+ + "' reRankQuery='"+reRankQuery.toString()+ + "' reRankDocs="+reRankDocs+ + " reRankWeigh="+reRankWeight+"}"; + } + + + public Query rewrite(IndexReader reader) throws IOException { + Query q = mainQuery.rewrite(reader); + if(q == mainQuery) { + return this; + } else { + return clone().wrap(q); + } + } + + public ReRankQuery clone() { + ReRankQuery clonedQuery = new ReRankQuery(reRankQuery, reRankDocs, reRankWeight, length, scale); + return clonedQuery; + } + + + public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException{ + return new ReRankWeight(mainQuery, reRankQuery, reRankWeight, searcher); + } + } + + private class ReRankWeight extends Weight{ + private Query reRankQuery; + private IndexSearcher searcher; + private Weight mainWeight; + private double reRankWeight; + + public ReRankWeight(Query mainQuery, Query reRankQuery, double reRankWeight, IndexSearcher searcher) throws IOException { + super(reRankQuery); + this.reRankQuery = reRankQuery; + this.searcher = searcher; + this.reRankWeight = reRankWeight; + this.mainWeight = mainQuery.createWeight(searcher, true); + } + + @Override + public void extractTerms(Set terms) { + this.mainWeight.extractTerms(terms); + + } + + public float getValueForNormalization() throws IOException { + return mainWeight.getValueForNormalization(); + } + + public Scorer scorer(LeafReaderContext context) throws IOException { + return mainWeight.scorer(context); + } + + public void normalize(float norm, float topLevelBoost) { + mainWeight.normalize(norm, topLevelBoost); + } + + public Explanation explain(LeafReaderContext context, int doc) throws IOException { + Explanation mainExplain = mainWeight.explain(context, doc); + return new QueryRescorer(getQuery()) { + @Override + protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { + float score = firstPassScore; + if (secondPassMatches) { + score += reRankWeight * secondPassScore; + } + return score; + } + }.explain(searcher, mainExplain, context.docBase+doc); + } + + } + + private class ReRankCollector extends TopDocsCollector { + + private Query reRankQuery; + private TopDocsCollector mainCollector; + private IndexSearcher searcher; + private int reRankDocs; + private int length; + private double reRankWeight; + private Map boostedPriority; + private float minScore = Float.MAX_VALUE; + private float maxScore = -Float.MAX_VALUE; + private Scorer localScorer; + private boolean scale; + + public ReRankCollector(int reRankDocs, + int length, + Query reRankQuery, + double reRankWeight, + QueryCommand cmd, + IndexSearcher searcher, + Map boostedPriority, + boolean scale) throws IOException { + super(null); + this.reRankQuery = reRankQuery; + this.reRankDocs = reRankDocs; + this.length = length; + this.boostedPriority = boostedPriority; + this.scale = scale; + Sort sort = cmd.getSort(); + if(sort == null) { + this.mainCollector = TopScoreDocCollector.create(Math.max(this.reRankDocs, length), null); + } else { + sort = sort.rewrite(searcher); + this.mainCollector = TopFieldCollector.create(sort, Math.max(this.reRankDocs, length), null, false, true, true); + } + this.searcher = searcher; + this.reRankWeight = reRankWeight; + } + + public int getTotalHits() { + return mainCollector.getTotalHits(); + } + + public TopDocs topDocs(int start, int howMany) { + + try { + + TopDocs mainDocs = mainCollector.topDocs(0, Math.max(reRankDocs, length)); + + if(mainDocs.totalHits == 0 || mainDocs.scoreDocs.length == 0) { + return mainDocs; + } + + if(reRankDocs == 0) { + scaleScores(mainDocs, new HashMap()); + return mainDocs; + } + + if(boostedPriority != null) { + SolrRequestInfo info = SolrRequestInfo.getRequestInfo(); + Map requestContext = null; + if(info != null) { + requestContext = info.getReq().getContext(); + } + + IntIntHashMap boostedDocs = QueryElevationComponent.getBoostDocs((SolrIndexSearcher)searcher, boostedPriority, requestContext); + + ScoreDoc[] mainScoreDocs = mainDocs.scoreDocs; + ScoreDoc[] reRankScoreDocs = new ScoreDoc[Math.min(mainScoreDocs.length, reRankDocs)]; + System.arraycopy(mainScoreDocs,0,reRankScoreDocs,0,reRankScoreDocs.length); + + mainDocs.scoreDocs = reRankScoreDocs; + + Map scoreMap = getScoreMap(mainDocs.scoreDocs, mainDocs.scoreDocs.length); + + TopDocs rescoredDocs = new QueryRescorer(reRankQuery) { + @Override + protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { + float score = firstPassScore; + if (secondPassMatches) { + score += reRankWeight * secondPassScore; + } + return score; + } + }.rescore(searcher, mainDocs, mainDocs.scoreDocs.length); + + Arrays.sort(rescoredDocs.scoreDocs, new BoostedComp(boostedDocs, mainDocs.scoreDocs, rescoredDocs.getMaxScore())); + + //Lower howMany if we've collected fewer documents. + howMany = Math.min(howMany, mainScoreDocs.length); + + if(howMany == rescoredDocs.scoreDocs.length) { + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; // Just return the rescoredDocs + } else if(howMany > rescoredDocs.scoreDocs.length) { + //We need to return more then we've reRanked, so create the combined page. + ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; + System.arraycopy(mainScoreDocs, 0, scoreDocs, 0, scoreDocs.length); //lay down the initial docs + System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, rescoredDocs.scoreDocs.length);//overlay the re-ranked docs. + rescoredDocs.scoreDocs = scoreDocs; + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; + } else { + //We've rescored more then we need to return. + ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; + System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, howMany); + rescoredDocs.scoreDocs = scoreDocs; + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; + } + + } else { + + ScoreDoc[] mainScoreDocs = mainDocs.scoreDocs; + + /* + * Create the array for the reRankScoreDocs. + */ + ScoreDoc[] reRankScoreDocs = new ScoreDoc[Math.min(mainScoreDocs.length, reRankDocs)]; + + /* + * Copy the initial results into the reRankScoreDocs array. + */ + System.arraycopy(mainScoreDocs, 0, reRankScoreDocs, 0, reRankScoreDocs.length); + + mainDocs.scoreDocs = reRankScoreDocs; + + Map scoreMap = getScoreMap(mainDocs.scoreDocs, mainDocs.scoreDocs.length); + + TopDocs rescoredDocs = new QueryRescorer(reRankQuery) { + @Override + protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { + float score = firstPassScore; + if (secondPassMatches) { + score += reRankWeight * secondPassScore; + } + return score; + } + }.rescore(searcher, mainDocs, mainDocs.scoreDocs.length); + + //Lower howMany to return if we've collected fewer documents. + howMany = Math.min(howMany, mainScoreDocs.length); + + if(howMany == rescoredDocs.scoreDocs.length) { + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; // Just return the rescoredDocs + } else if(howMany > rescoredDocs.scoreDocs.length) { + + //We need to return more then we've reRanked, so create the combined page. + ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; + //lay down the initial docs + System.arraycopy(mainScoreDocs, 0, scoreDocs, 0, scoreDocs.length); + //overlay the rescoreds docs + System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, rescoredDocs.scoreDocs.length); + rescoredDocs.scoreDocs = scoreDocs; + if(scale) { + assert(scoreMap != null); + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; + } else { + //We've rescored more then we need to return. + ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; + System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, howMany); + rescoredDocs.scoreDocs = scoreDocs; + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; + } + } + } catch (Exception e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); + } + } + + @Override + public LeafCollector getLeafCollector(LeafReaderContext context) + throws IOException { + return mainCollector.getLeafCollector(context); + } + + @Override + public boolean needsScores() { + return true; + } + } + + private void scaleScores(TopDocs topDocs, Map scoreMap) + { + float maxScore = topDocs.getMaxScore(); + float newMax = -Float.MAX_VALUE; + + for(ScoreDoc scoreDoc : topDocs.scoreDocs) { + float score = scoreDoc.score; + + Float oldScore = scoreMap.get(scoreDoc.doc); + + // check if the score has been changed after rescoring + boolean rescored = oldScore != null && score != oldScore; + + // If maxScore is different from 0, the score is divided by maxscore + scoreDoc.score = score / (maxScore != 0? maxScore : 1); + + // If the document has been rescored, the score is increased by 1. + // This results in having all the rescored element scores in (1,2] range. + if (rescored) + { + scoreDoc.score += 1; + } + + if(scoreDoc.score > newMax) + { + newMax = scoreDoc.score; + } + } + + assert(newMax <= 2); + topDocs.setMaxScore(newMax); + } + + private Map getScoreMap(ScoreDoc[] scoreDocs, int num) { + Map scoreMap = new HashMap(); + for(int i=0; i score2) { + return -1; + } else if(score1 < score2) { + return 1; + } else { + return 0; + } + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AllAccessCollector.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AllAccessCollector.java index ecc986759..70a2eeb5a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AllAccessCollector.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AllAccessCollector.java @@ -1,37 +1,37 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import org.apache.solr.search.DelegatingCollector; - -public class AllAccessCollector extends DelegatingCollector -{ - public boolean acceptsDocsOutOfOrder() - { - return false; - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import org.apache.solr.search.DelegatingCollector; + +public class AllAccessCollector extends DelegatingCollector +{ + public boolean acceptsDocsOutOfOrder() + { + return false; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AnyStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AnyStructuredFieldPosition.java index 1f4841c2e..103af5af5 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AnyStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AnyStructuredFieldPosition.java @@ -1,103 +1,103 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -/** - * @author andyh - * - * TODO To change the template for this generated type comment go to Window - - * Preferences - Java - Code Style - Code Templates - */ -public class AnyStructuredFieldPosition extends AbstractStructuredFieldPosition -{ - - /** - * - */ - public AnyStructuredFieldPosition(String termText) - { - super(termText, true, false); - if (termText == null) - { - setTerminal(false); - } - } - - public AnyStructuredFieldPosition() - { - super(null, false, false); - } - - /* - * (non-Javadoc) - * - * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, - * int, org.apache.lucene.index.TermPositions) - */ - public int matches(int start, int end, int offset) throws IOException - { - // we are doing //name - if (getCachingTermPositions() != null) - { - setTerminal(true); - int realPosition = 0; - int adjustedPosition = 0; - getCachingTermPositions().reset(); - int count = getCachingTermPositions().freq(); - for (int i = 0; i < count; i++) - { - realPosition = getCachingTermPositions().nextPosition(); - adjustedPosition = realPosition - start; - if ((end != -1) && (realPosition > end)) - { - return -1; - } - if (adjustedPosition > offset) - { - return adjustedPosition; - } - } - } - else - { - // we are doing // - setTerminal(false); - return offset; - } - return -1; - } - - public String getDescription() - { - return "Any"; - } - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +/** + * @author andyh + * + * TODO To change the template for this generated type comment go to Window - + * Preferences - Java - Code Style - Code Templates + */ +public class AnyStructuredFieldPosition extends AbstractStructuredFieldPosition +{ + + /** + * + */ + public AnyStructuredFieldPosition(String termText) + { + super(termText, true, false); + if (termText == null) + { + setTerminal(false); + } + } + + public AnyStructuredFieldPosition() + { + super(null, false, false); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, + * int, org.apache.lucene.index.TermPositions) + */ + public int matches(int start, int end, int offset) throws IOException + { + // we are doing //name + if (getCachingTermPositions() != null) + { + setTerminal(true); + int realPosition = 0; + int adjustedPosition = 0; + getCachingTermPositions().reset(); + int count = getCachingTermPositions().freq(); + for (int i = 0; i < count; i++) + { + realPosition = getCachingTermPositions().nextPosition(); + adjustedPosition = realPosition - start; + if ((end != -1) && (realPosition > end)) + { + return -1; + } + if (adjustedPosition > offset) + { + return adjustedPosition; + } + } + } + else + { + // we are doing // + setTerminal(false); + return offset; + } + return -1; + } + + public String getDescription() + { + return "Any"; + } + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/BitsFilter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/BitsFilter.java index 86729c908..90d26d959 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/BitsFilter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/BitsFilter.java @@ -1,105 +1,105 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.util.List; - -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.DocIdSet; -import org.apache.solr.search.BitsFilteredDocIdSet; -import org.apache.solr.search.Filter; -import org.apache.lucene.util.BitDocIdSet; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.FixedBitSet; - - -/* -* A segment level Lucene Filter where each segment is backed by a FixedBitSet. -*/ - -public class BitsFilter extends Filter { - - private final List bitSets; - - public BitsFilter(List bitSets) - { - if (bitSets == null) throw new IllegalStateException("bitSets cannot be null"); - this.bitSets = bitSets; - } - - public void or(BitsFilter bitsFilter) - { - List andSets = bitsFilter.bitSets; - for(int i=0; i andSets = bitsFilter.bitSets; - for(int i=0; i getBitSets() - { - return this.bitSets; - } - - public String toString(String s) { - return s; - } - - public DocIdSet getDocIdSet(LeafReaderContext context, Bits bits) { - return BitsFilteredDocIdSet.wrap(new BitDocIdSet(bitSets.get(context.ord)), bits); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof BitsFilter)) return false; - - BitsFilter that = (BitsFilter) o; - - if (!bitSets.equals(that.bitSets)) return false; - - return true; - } - - @Override - public int hashCode() { - return bitSets.hashCode(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.util.List; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.DocIdSet; +import org.apache.solr.search.BitsFilteredDocIdSet; +import org.apache.solr.search.Filter; +import org.apache.lucene.util.BitDocIdSet; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.FixedBitSet; + + +/* +* A segment level Lucene Filter where each segment is backed by a FixedBitSet. +*/ + +public class BitsFilter extends Filter { + + private final List bitSets; + + public BitsFilter(List bitSets) + { + if (bitSets == null) throw new IllegalStateException("bitSets cannot be null"); + this.bitSets = bitSets; + } + + public void or(BitsFilter bitsFilter) + { + List andSets = bitsFilter.bitSets; + for(int i=0; i andSets = bitsFilter.bitSets; + for(int i=0; i getBitSets() + { + return this.bitSets; + } + + public String toString(String s) { + return s; + } + + public DocIdSet getDocIdSet(LeafReaderContext context, Bits bits) { + return BitsFilteredDocIdSet.wrap(new BitDocIdSet(bitSets.get(context.ord)), bits); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BitsFilter)) return false; + + BitsFilter that = (BitsFilter) o; + + if (!bitSets.equals(that.bitSets)) return false; + + return true; + } + + @Override + public int hashCode() { + return bitSets.hashCode(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CachingTermPositions.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CachingTermPositions.java index f17ca745a..06949911b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CachingTermPositions.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CachingTermPositions.java @@ -1,210 +1,210 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.util.AttributeSource; -import org.apache.lucene.util.BytesRef; - - -/** - * @author andyh - * - * TODO To change the template for this generated type comment go to Window - - * Preferences - Java - Code Style - Code Templates - */ -public class CachingTermPositions extends PostingsEnum -{ - int[] results; - - int position = -1; - - int last = -1; - - PostingsEnum delegate; - - public CachingTermPositions(PostingsEnum delegate) - { - this.delegate = delegate; - } - - /* - * (non-Javadoc) - * - * @see org.apache.lucene.index.TermPositions#nextPosition() - */ - public int nextPosition() throws IOException - { - if (results == null) - { - results = new int[freq()]; - } - position++; - if (last < position) - { - results[position] = delegate.nextPosition(); - last = position; - } - return results[position]; - - } - - public void reset() - { - position = -1; - } - - private void clear() - { - position = -1; - last = -1; - results = null; - } - - /** - * @return int - * @see java.lang.Object#hashCode() - */ - public int hashCode() - { - return delegate.hashCode(); - } - - /** - * @return int - * @throws IOException - * @see org.apache.lucene.index.DocsEnum#freq() - */ - public int freq() throws IOException - { - return delegate.freq(); - } - - /** - * @return int - * @throws IOException - * @see org.apache.lucene.index.DocsAndPositionsEnum#startOffset() - */ - public int startOffset() throws IOException - { - return delegate.startOffset(); - } - - /** - * @return int - * @throws IOException - * @see org.apache.lucene.index.DocsAndPositionsEnum#endOffset() - */ - public int endOffset() throws IOException - { - return delegate.endOffset(); - } - - /** - * @return int - * @see org.apache.lucene.search.DocIdSetIterator#docID() - */ - public int docID() - { - return delegate.docID(); - } - - /** - * @return BytesRef - * @throws IOException - * @see org.apache.lucene.index.DocsAndPositionsEnum#getPayload() - */ - public BytesRef getPayload() throws IOException - { - return delegate.getPayload(); - } - - /** - * @return AttributeSource - * @see org.apache.lucene.index.DocsEnum#attributes() - */ - public AttributeSource attributes() - { - return delegate.attributes(); - } - - /** - * @return int - * @throws IOException - * @see org.apache.lucene.search.DocIdSetIterator#nextDoc() - */ - public int nextDoc() throws IOException - { - int nextDoc = delegate.nextDoc(); - clear(); - return nextDoc; - } - - /** - * @param target int - * @return int - * @throws IOException - * @see org.apache.lucene.search.DocIdSetIterator#advance(int) - */ - public int advance(int target) throws IOException - { - int nextDoc = delegate.advance(target); - clear(); - return nextDoc; - } - - /** - * @param obj Object - * @return boolean - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object obj) - { - return delegate.equals(obj); - } - - /** - * @return long - * @see org.apache.lucene.search.DocIdSetIterator#cost() - */ - public long cost() - { - return delegate.cost(); - } - - /** - * @return String - * @see java.lang.Object#toString() - */ - public String toString() - { - return delegate.toString(); - } - -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.apache.lucene.index.PostingsEnum; +import org.apache.lucene.util.AttributeSource; +import org.apache.lucene.util.BytesRef; + + +/** + * @author andyh + * + * TODO To change the template for this generated type comment go to Window - + * Preferences - Java - Code Style - Code Templates + */ +public class CachingTermPositions extends PostingsEnum +{ + int[] results; + + int position = -1; + + int last = -1; + + PostingsEnum delegate; + + public CachingTermPositions(PostingsEnum delegate) + { + this.delegate = delegate; + } + + /* + * (non-Javadoc) + * + * @see org.apache.lucene.index.TermPositions#nextPosition() + */ + public int nextPosition() throws IOException + { + if (results == null) + { + results = new int[freq()]; + } + position++; + if (last < position) + { + results[position] = delegate.nextPosition(); + last = position; + } + return results[position]; + + } + + public void reset() + { + position = -1; + } + + private void clear() + { + position = -1; + last = -1; + results = null; + } + + /** + * @return int + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return delegate.hashCode(); + } + + /** + * @return int + * @throws IOException + * @see org.apache.lucene.index.DocsEnum#freq() + */ + public int freq() throws IOException + { + return delegate.freq(); + } + + /** + * @return int + * @throws IOException + * @see org.apache.lucene.index.DocsAndPositionsEnum#startOffset() + */ + public int startOffset() throws IOException + { + return delegate.startOffset(); + } + + /** + * @return int + * @throws IOException + * @see org.apache.lucene.index.DocsAndPositionsEnum#endOffset() + */ + public int endOffset() throws IOException + { + return delegate.endOffset(); + } + + /** + * @return int + * @see org.apache.lucene.search.DocIdSetIterator#docID() + */ + public int docID() + { + return delegate.docID(); + } + + /** + * @return BytesRef + * @throws IOException + * @see org.apache.lucene.index.DocsAndPositionsEnum#getPayload() + */ + public BytesRef getPayload() throws IOException + { + return delegate.getPayload(); + } + + /** + * @return AttributeSource + * @see org.apache.lucene.index.DocsEnum#attributes() + */ + public AttributeSource attributes() + { + return delegate.attributes(); + } + + /** + * @return int + * @throws IOException + * @see org.apache.lucene.search.DocIdSetIterator#nextDoc() + */ + public int nextDoc() throws IOException + { + int nextDoc = delegate.nextDoc(); + clear(); + return nextDoc; + } + + /** + * @param target int + * @return int + * @throws IOException + * @see org.apache.lucene.search.DocIdSetIterator#advance(int) + */ + public int advance(int target) throws IOException + { + int nextDoc = delegate.advance(target); + clear(); + return nextDoc; + } + + /** + * @param obj Object + * @return boolean + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) + { + return delegate.equals(obj); + } + + /** + * @return long + * @see org.apache.lucene.search.DocIdSetIterator#cost() + */ + public long cost() + { + return delegate.cost(); + } + + /** + * @return String + * @see java.lang.Object#toString() + */ + public String toString() + { + return delegate.toString(); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CmisQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CmisQParserPlugin.java index eb08bb0ba..898c703ba 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CmisQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CmisQParserPlugin.java @@ -1,207 +1,207 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; -import org.alfresco.repo.search.impl.querymodel.Order; -import org.alfresco.repo.search.impl.querymodel.Ordering; -import org.alfresco.repo.search.impl.querymodel.PropertyArgument; -import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; -import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.util.Pair; -import org.apache.chemistry.opencmis.commons.enums.CmisVersion; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class CmisQParserPlugin extends QParserPlugin -{ - protected final static Logger log = LoggerFactory.getLogger(CmisQParserPlugin.class); - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new CmisQParser(qstr, localParams, params, req, args); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - public void init(NamedList args) - { - this.args = args; - } - - public static class CmisQParser extends AbstractQParser - { - public CmisQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) - { - super(qstr, localParams, params, req, args); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - try - { - Pair searchParametersAndFilter = getSearchParameters(); - SearchParameters searchParameters = searchParametersAndFilter.getFirst(); - // these could either be checked & set here, or in the SolrQueryParser constructor - - String cmisVersionString = this.params.get("cmisVersion"); - CmisVersion cmisVersion = (cmisVersionString == null ? CmisVersion.CMIS_1_0 : CmisVersion.valueOf(cmisVersionString)); - - String altDic = this.params.get(SearchParameters.ALTERNATIVE_DICTIONARY); - org.alfresco.repo.search.impl.querymodel.Query queryModelQuery - = AlfrescoSolrDataModel.getInstance().parseCMISQueryToAlfrescoAbstractQuery(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS, searchParameters, req, altDic, cmisVersion); - - // build the sort param and update the params on the request if required ..... - - if ((queryModelQuery.getOrderings() != null) && (queryModelQuery.getOrderings().size() > 0)) - { - StringBuilder sortParameter = new StringBuilder(); - - for (Ordering ordering : queryModelQuery.getOrderings()) - { - if (ordering.getColumn().getFunction().getName().equals(PropertyAccessor.NAME)) - { - PropertyArgument property = (PropertyArgument) ordering.getColumn().getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY); - - if (property == null) - { - throw new IllegalStateException(); - } - - String propertyName = property.getPropertyName(); - - String luceneField = AlfrescoSolrDataModel.getInstance().getCMISFunctionEvaluationContext(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS,cmisVersion,altDic).getLuceneFieldName(propertyName); - -// Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(luceneField); -// PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), AlfrescoSolrDataModel.getInstance().getNamespaceDAO(), AlfrescoSolrDataModel.getInstance().getDictionaryService(altDic), fieldNameAndEnding.getFirst()); -// -// String solrSortField = null; -// if(propertyDef != null) -// { -// -// IndexedField fields = AlfrescoSolrDataModel.getInstance().getQueryableFields(propertyDef.getName(), AlfrescoSolrDataModel.getInstance().getTextField(fieldNameAndEnding.getSecond()), FieldUse.SORT); -// if(fields.getFields().size() > 0) -// { -// solrSortField = fields.getFields().get(0).getField(); -// } -// } -// else -// { -// solrSortField = AlfrescoSolrDataModel.getInstance().mapNonPropertyFields(luceneField); -// } - if(sortParameter.length() > 0) - { - sortParameter.append(", "); - } - sortParameter.append(luceneField).append(" "); - if(ordering.getOrder() == Order.DESCENDING) - { - sortParameter.append("desc"); - } - else - { - sortParameter.append("asc"); - } - - } - else if (ordering.getColumn().getFunction().getName().equals(Score.NAME)) - { - if(sortParameter.length() > 0) - { - sortParameter.append(", "); - } - sortParameter.append("SCORE "); - if(ordering.getOrder() == Order.DESCENDING) - { - sortParameter.append("desc"); - } - else - { - sortParameter.append("asc"); - } - } - else - { - throw new IllegalStateException(); - } - - } - - // update request params - - ModifiableSolrParams newParams = new ModifiableSolrParams(req.getParams()); - newParams.set("sort", sortParameter.toString()); - req.setParams(newParams); - this.params = newParams; - } - - Query query = AlfrescoSolrDataModel.getInstance().getCMISQuery(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS, searchParametersAndFilter, req, queryModelQuery, cmisVersion, altDic); - if(log.isDebugEnabled()) - { - log.debug("AFTS QP query as lucene:\t "+query); - } - return query; - } - catch(ParseException e) - { - throw new SyntaxError(e); - } - } - - - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; +import org.alfresco.repo.search.impl.querymodel.Order; +import org.alfresco.repo.search.impl.querymodel.Ordering; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class CmisQParserPlugin extends QParserPlugin +{ + protected final static Logger log = LoggerFactory.getLogger(CmisQParserPlugin.class); + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new CmisQParser(qstr, localParams, params, req, args); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + public void init(NamedList args) + { + this.args = args; + } + + public static class CmisQParser extends AbstractQParser + { + public CmisQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) + { + super(qstr, localParams, params, req, args); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + try + { + Pair searchParametersAndFilter = getSearchParameters(); + SearchParameters searchParameters = searchParametersAndFilter.getFirst(); + // these could either be checked & set here, or in the SolrQueryParser constructor + + String cmisVersionString = this.params.get("cmisVersion"); + CmisVersion cmisVersion = (cmisVersionString == null ? CmisVersion.CMIS_1_0 : CmisVersion.valueOf(cmisVersionString)); + + String altDic = this.params.get(SearchParameters.ALTERNATIVE_DICTIONARY); + org.alfresco.repo.search.impl.querymodel.Query queryModelQuery + = AlfrescoSolrDataModel.getInstance().parseCMISQueryToAlfrescoAbstractQuery(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS, searchParameters, req, altDic, cmisVersion); + + // build the sort param and update the params on the request if required ..... + + if ((queryModelQuery.getOrderings() != null) && (queryModelQuery.getOrderings().size() > 0)) + { + StringBuilder sortParameter = new StringBuilder(); + + for (Ordering ordering : queryModelQuery.getOrderings()) + { + if (ordering.getColumn().getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument property = (PropertyArgument) ordering.getColumn().getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY); + + if (property == null) + { + throw new IllegalStateException(); + } + + String propertyName = property.getPropertyName(); + + String luceneField = AlfrescoSolrDataModel.getInstance().getCMISFunctionEvaluationContext(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS,cmisVersion,altDic).getLuceneFieldName(propertyName); + +// Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(luceneField); +// PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), AlfrescoSolrDataModel.getInstance().getNamespaceDAO(), AlfrescoSolrDataModel.getInstance().getDictionaryService(altDic), fieldNameAndEnding.getFirst()); +// +// String solrSortField = null; +// if(propertyDef != null) +// { +// +// IndexedField fields = AlfrescoSolrDataModel.getInstance().getQueryableFields(propertyDef.getName(), AlfrescoSolrDataModel.getInstance().getTextField(fieldNameAndEnding.getSecond()), FieldUse.SORT); +// if(fields.getFields().size() > 0) +// { +// solrSortField = fields.getFields().get(0).getField(); +// } +// } +// else +// { +// solrSortField = AlfrescoSolrDataModel.getInstance().mapNonPropertyFields(luceneField); +// } + if(sortParameter.length() > 0) + { + sortParameter.append(", "); + } + sortParameter.append(luceneField).append(" "); + if(ordering.getOrder() == Order.DESCENDING) + { + sortParameter.append("desc"); + } + else + { + sortParameter.append("asc"); + } + + } + else if (ordering.getColumn().getFunction().getName().equals(Score.NAME)) + { + if(sortParameter.length() > 0) + { + sortParameter.append(", "); + } + sortParameter.append("SCORE "); + if(ordering.getOrder() == Order.DESCENDING) + { + sortParameter.append("desc"); + } + else + { + sortParameter.append("asc"); + } + } + else + { + throw new IllegalStateException(); + } + + } + + // update request params + + ModifiableSolrParams newParams = new ModifiableSolrParams(req.getParams()); + newParams.set("sort", sortParameter.toString()); + req.setParams(newParams); + this.params = newParams; + } + + Query query = AlfrescoSolrDataModel.getInstance().getCMISQuery(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS, searchParametersAndFilter, req, queryModelQuery, cmisVersion, altDic); + if(log.isDebugEnabled()) + { + log.debug("AFTS QP query as lucene:\t "+query); + } + return query; + } + catch(ParseException e) + { + throw new SyntaxError(e); + } + } + + + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingAnalyticsQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingAnalyticsQuery.java index efeca2487..1d6dbf3a8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingAnalyticsQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingAnalyticsQuery.java @@ -1,63 +1,63 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import org.apache.lucene.search.IndexSearcher; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.search.AnalyticsQuery; -import org.apache.solr.search.DelegatingCollector; - -/** - * @author Andy - * - */ -public class ContentSizeGroupingAnalyticsQuery extends AnalyticsQuery -{ - private int buckets; - private int scale; - - /** - * @param buckets - * @param mappings - */ - public ContentSizeGroupingAnalyticsQuery(int scale, int buckets) - { - this.scale = scale; - this.buckets = buckets; - - } - - /* (non-Javadoc) - * @see org.apache.solr.search.AnalyticsQuery#getAnalyticsCollector(org.apache.solr.handler.component.ResponseBuilder, org.apache.lucene.search.IndexSearcher) - */ - @Override - public DelegatingCollector getAnalyticsCollector(ResponseBuilder rb, IndexSearcher searcher) - { - return new ContentSizeGroupingCollector(rb, scale, buckets); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import org.apache.lucene.search.IndexSearcher; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.search.AnalyticsQuery; +import org.apache.solr.search.DelegatingCollector; + +/** + * @author Andy + * + */ +public class ContentSizeGroupingAnalyticsQuery extends AnalyticsQuery +{ + private int buckets; + private int scale; + + /** + * @param buckets + * @param mappings + */ + public ContentSizeGroupingAnalyticsQuery(int scale, int buckets) + { + this.scale = scale; + this.buckets = buckets; + + } + + /* (non-Javadoc) + * @see org.apache.solr.search.AnalyticsQuery#getAnalyticsCollector(org.apache.solr.handler.component.ResponseBuilder, org.apache.lucene.search.IndexSearcher) + */ + @Override + public DelegatingCollector getAnalyticsCollector(ResponseBuilder rb, IndexSearcher searcher) + { + return new ContentSizeGroupingCollector(rb, scale, buckets); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingCollector.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingCollector.java index b0d345a70..e1afb7242 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingCollector.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingCollector.java @@ -1,131 +1,131 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.math.BigDecimal; -import java.math.MathContext; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.alfresco.solr.tracker.TrackerStats.Bucket; -import org.alfresco.solr.tracker.TrackerStats.IncrementalStats; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.DelegatingCollector; - -/** - * @author Andy - * - */ -public class ContentSizeGroupingCollector extends DelegatingCollector -{ - ResponseBuilder rb; - IncrementalStats stats; - String schemaFieldName; - SchemaField schemaField; - NumericDocValues numericDocValues; - /** - * @param rb - * @param buckets - */ - public ContentSizeGroupingCollector(ResponseBuilder rb, int scale, int buckets) - { - this.rb = rb; - stats = new IncrementalStats(scale, buckets, null); - schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty("content.size", FieldUse.FACET, rb.req); - schemaField = rb.req.getSchema().getFieldOrNull(schemaFieldName); - } - - - - /* (non-Javadoc) - * @see org.apache.solr.search.DelegatingCollector#setNextReader(org.apache.lucene.index.AtomicReaderContext) - */ - @Override - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - if(schemaField != null) - { - if(schemaField.getType().getNumericType() != null) - { - try - { - numericDocValues = context.reader().getNumericDocValues(schemaFieldName); - } - catch (IOException e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - } - - - - public void collect(int doc) throws IOException - { - - if(numericDocValues != null) - { - long value = numericDocValues.get(doc); - stats.add(value); - } - leafDelegate.collect(doc); - } - - public void finish() throws IOException - { - NamedList analytics = new NamedList<>(); - rb.rsp.add("analytics", analytics); - NamedList fieldCounts = new NamedList<>(); - analytics.add("contentSize()", fieldCounts); - - for(Bucket bucket :stats.getHistogram()) - { - fieldCounts.add("["+(long)Math.ceil(bucket.leftBoundary)+ " TO "+(long)Math.ceil(bucket.rightBoundary)+">", (long)roundEven(bucket.countLeft + bucket.countRight)); - } - - - if(this.delegate instanceof DelegatingCollector) { - ((DelegatingCollector)this.delegate).finish(); - } - } - - - private long roundEven(double value) - { - BigDecimal bd = new BigDecimal(value); - bd = bd.round(MathContext.DECIMAL64); - return bd.longValue(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.MathContext; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.alfresco.solr.tracker.TrackerStats.Bucket; +import org.alfresco.solr.tracker.TrackerStats.IncrementalStats; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DelegatingCollector; + +/** + * @author Andy + * + */ +public class ContentSizeGroupingCollector extends DelegatingCollector +{ + ResponseBuilder rb; + IncrementalStats stats; + String schemaFieldName; + SchemaField schemaField; + NumericDocValues numericDocValues; + /** + * @param rb + * @param buckets + */ + public ContentSizeGroupingCollector(ResponseBuilder rb, int scale, int buckets) + { + this.rb = rb; + stats = new IncrementalStats(scale, buckets, null); + schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty("content.size", FieldUse.FACET, rb.req); + schemaField = rb.req.getSchema().getFieldOrNull(schemaFieldName); + } + + + + /* (non-Javadoc) + * @see org.apache.solr.search.DelegatingCollector#setNextReader(org.apache.lucene.index.AtomicReaderContext) + */ + @Override + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + if(schemaField != null) + { + if(schemaField.getType().getNumericType() != null) + { + try + { + numericDocValues = context.reader().getNumericDocValues(schemaFieldName); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + + + + public void collect(int doc) throws IOException + { + + if(numericDocValues != null) + { + long value = numericDocValues.get(doc); + stats.add(value); + } + leafDelegate.collect(doc); + } + + public void finish() throws IOException + { + NamedList analytics = new NamedList<>(); + rb.rsp.add("analytics", analytics); + NamedList fieldCounts = new NamedList<>(); + analytics.add("contentSize()", fieldCounts); + + for(Bucket bucket :stats.getHistogram()) + { + fieldCounts.add("["+(long)Math.ceil(bucket.leftBoundary)+ " TO "+(long)Math.ceil(bucket.rightBoundary)+">", (long)roundEven(bucket.countLeft + bucket.countRight)); + } + + + if(this.delegate instanceof DelegatingCollector) { + ((DelegatingCollector)this.delegate).finish(); + } + } + + + private long roundEven(double value) + { + BigDecimal bd = new BigDecimal(value); + bd = bd.round(MathContext.DECIMAL64); + return bd.longValue(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingQParserPlugin.java index ecdf069f9..fc3186884 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingQParserPlugin.java @@ -1,110 +1,110 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; - -/** - * @author Andy - */ -public class ContentSizeGroupingQParserPlugin extends QParserPlugin -{ - private int scale = 1; - - private int buckets = 10; - - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - @Override - public void init(NamedList args) - { - this.args = args; - if (args != null) - { - Object val = args.get("buckets"); - if (val != null) - { - buckets = Integer.parseInt(val.toString()); - } - - val = args.get("scale"); - if (val != null) - { - scale = Integer.parseInt(val.toString()); - } - } - - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new ContentSizeGroupingQParser(qstr, localParams, params, req, args, scale, buckets); - } - - public static class ContentSizeGroupingQParser extends AbstractQParser - { - - private int buckets; - - private int scale; - - public ContentSizeGroupingQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args, int scale, int buckets) - { - super(qstr, localParams, params, req, args); - this.scale = scale; - this.buckets = buckets; - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - return new ContentSizeGroupingAnalyticsQuery(scale, buckets); - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; + +/** + * @author Andy + */ +public class ContentSizeGroupingQParserPlugin extends QParserPlugin +{ + private int scale = 1; + + private int buckets = 10; + + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + @Override + public void init(NamedList args) + { + this.args = args; + if (args != null) + { + Object val = args.get("buckets"); + if (val != null) + { + buckets = Integer.parseInt(val.toString()); + } + + val = args.get("scale"); + if (val != null) + { + scale = Integer.parseInt(val.toString()); + } + } + + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new ContentSizeGroupingQParser(qstr, localParams, params, req, args, scale, buckets); + } + + public static class ContentSizeGroupingQParser extends AbstractQParser + { + + private int buckets; + + private int scale; + + public ContentSizeGroupingQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args, int scale, int buckets) + { + super(qstr, localParams, params, req, args); + this.scale = scale; + this.buckets = buckets; + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + return new ContentSizeGroupingAnalyticsQuery(scale, buckets); + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DescendantAndSelfStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DescendantAndSelfStructuredFieldPosition.java index dc72e3814..42cdf4177 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DescendantAndSelfStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DescendantAndSelfStructuredFieldPosition.java @@ -1,53 +1,53 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -public class DescendantAndSelfStructuredFieldPosition extends AnyStructuredFieldPosition -{ - public DescendantAndSelfStructuredFieldPosition() - { - super(); - } - - public String getDescription() - { - return "Descendant and Self Axis"; - } - - public boolean allowsLinkingBySelf() - { - return true; - } - - public boolean isDescendant() - { - return true; - } - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +public class DescendantAndSelfStructuredFieldPosition extends AnyStructuredFieldPosition +{ + public DescendantAndSelfStructuredFieldPosition() + { + super(); + } + + public String getDescription() + { + return "Descendant and Self Axis"; + } + + public boolean allowsLinkingBySelf() + { + return true; + } + + public boolean isDescendant() + { + return true; + } + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DocValuesCache.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DocValuesCache.java index 75d1a7b0e..013d6af2a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DocValuesCache.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DocValuesCache.java @@ -1,143 +1,143 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.WeakHashMap; - -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.NumericDocValues; - - -/** - * The DocValuesCache is an in-memory uncompressed numeric DocValues cache. It is designed to provide the fastest - * possible access to numeric docValues. The DocValuesCache can be used instead of the Direct DocValues format which also - * provides uncompressed in-memory docValues. The DocValuesCache can be used in situations when it is not - * practical to re-index to use Direct docValues. - **/ - -public class DocValuesCache -{ - private static Map> cache = new HashMap>(); - - public static synchronized NumericDocValues getNumericDocValues(String field, LeafReader reader) throws IOException - { - WeakHashMap fieldCache = cache.get(field); - - if(fieldCache == null) - { - fieldCache = new WeakHashMap(); - cache.put(field, fieldCache); - } - - Object cacheKey = reader.getCoreCacheKey(); - NumericDocValues cachedValues = fieldCache.get(cacheKey); - - if(cachedValues == null) - { - NumericDocValues fieldValues = reader.getNumericDocValues(field); - if(fieldValues == null) - { - return null; - } - else - { - int maxDoc = reader.maxDoc(); - boolean longs = false; - int[] intValues = new int[maxDoc]; //Always start off with an int array. - SettableDocValues settableValues = new IntValues(intValues); - - for(int i=0; i Integer.MAX_VALUE && !longs) - { - longs = true; - settableValues = new LongValues(intValues); - } - - settableValues.set(i, value); - } - fieldCache.put(cacheKey, settableValues); - return settableValues; - } - } - else - { - return cachedValues; - } - } - - private static abstract class SettableDocValues extends NumericDocValues - { - public abstract void set(int index, long value); - } - - private static class IntValues extends SettableDocValues - { - private int[] values; - - public IntValues(int[] values) - { - this.values = values; - } - - public void set(int index, long value) - { - this.values[index] = (int)value; - } - - public long get(int index) { - return values[index]; - } - } - - private static class LongValues extends SettableDocValues - { - private long[] values; - - public LongValues(int[] ivalues) - { - values = new long[ivalues.length]; - for(int i=0; i< ivalues.length; i++) { - values[i] = ivalues[i]; - } - } - - public void set(int index, long value) - { - this.values[index] = value; - } - - public long get(int index) - { - return values[index]; - } - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.NumericDocValues; + + +/** + * The DocValuesCache is an in-memory uncompressed numeric DocValues cache. It is designed to provide the fastest + * possible access to numeric docValues. The DocValuesCache can be used instead of the Direct DocValues format which also + * provides uncompressed in-memory docValues. The DocValuesCache can be used in situations when it is not + * practical to re-index to use Direct docValues. + **/ + +public class DocValuesCache +{ + private static Map> cache = new HashMap>(); + + public static synchronized NumericDocValues getNumericDocValues(String field, LeafReader reader) throws IOException + { + WeakHashMap fieldCache = cache.get(field); + + if(fieldCache == null) + { + fieldCache = new WeakHashMap(); + cache.put(field, fieldCache); + } + + Object cacheKey = reader.getCoreCacheKey(); + NumericDocValues cachedValues = fieldCache.get(cacheKey); + + if(cachedValues == null) + { + NumericDocValues fieldValues = reader.getNumericDocValues(field); + if(fieldValues == null) + { + return null; + } + else + { + int maxDoc = reader.maxDoc(); + boolean longs = false; + int[] intValues = new int[maxDoc]; //Always start off with an int array. + SettableDocValues settableValues = new IntValues(intValues); + + for(int i=0; i Integer.MAX_VALUE && !longs) + { + longs = true; + settableValues = new LongValues(intValues); + } + + settableValues.set(i, value); + } + fieldCache.put(cacheKey, settableValues); + return settableValues; + } + } + else + { + return cachedValues; + } + } + + private static abstract class SettableDocValues extends NumericDocValues + { + public abstract void set(int index, long value); + } + + private static class IntValues extends SettableDocValues + { + private int[] values; + + public IntValues(int[] values) + { + this.values = values; + } + + public void set(int index, long value) + { + this.values[index] = (int)value; + } + + public long get(int index) { + return values[index]; + } + } + + private static class LongValues extends SettableDocValues + { + private long[] values; + + public LongValues(int[] ivalues) + { + values = new long[ivalues.length]; + for(int i=0; i< ivalues.length; i++) { + values[i] = ivalues[i]; + } + } + + public void set(int index, long value) + { + this.values[index] = value; + } + + public long get(int index) + { + return values[index]; + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/EmptyHybridBitSet.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/EmptyHybridBitSet.java index d7ad4e196..8925bf0b5 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/EmptyHybridBitSet.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/EmptyHybridBitSet.java @@ -1,40 +1,40 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -public class EmptyHybridBitSet extends HybridBitSet -{ - public void set(long bit) - { - - } - - public boolean get(long bit) - { - return false; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +public class EmptyHybridBitSet extends HybridBitSet +{ + public void set(long bit) + { + + } + + public boolean get(long bit) + { + return false; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/HybridBitSet.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/HybridBitSet.java index 5c9170cbb..e33328c70 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/HybridBitSet.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/HybridBitSet.java @@ -1,80 +1,80 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.util.HashSet; -import java.util.Set; - -import org.apache.lucene.util.FixedBitSet; - -/** -* The HybridBitSet is a random access (doesn't support iteration) BitSet implementation that uses a FixedBitSet for the first N bits -* and a HashSet for higher bits. This is designed to provide a balance between the high performance of FixedBitSet and -* the efficient sparse behavior of a HashSet. -**/ - -public class HybridBitSet -{ - private FixedBitSet bits; - private Set set = new HashSet(); - private int maxBit; - - public HybridBitSet() - { - - } - - HybridBitSet(int maxBit) - { - this.bits = new FixedBitSet(maxBit); - this.maxBit = maxBit; - } - - public void set(long bit) - { - if(bit < maxBit) - { - bits.set((int)bit); - } - else - { - set.add((long)bit); - } - } - - public boolean get(long bit) - { - if(bit < maxBit) - { - return bits.get((int)bit); - } - else - { - return set.contains((long)bit); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.lucene.util.FixedBitSet; + +/** +* The HybridBitSet is a random access (doesn't support iteration) BitSet implementation that uses a FixedBitSet for the first N bits +* and a HashSet for higher bits. This is designed to provide a balance between the high performance of FixedBitSet and +* the efficient sparse behavior of a HashSet. +**/ + +public class HybridBitSet +{ + private FixedBitSet bits; + private Set set = new HashSet(); + private int maxBit; + + public HybridBitSet() + { + + } + + HybridBitSet(int maxBit) + { + this.bits = new FixedBitSet(maxBit); + this.maxBit = maxBit; + } + + public void set(long bit) + { + if(bit < maxBit) + { + bits.set((int)bit); + } + else + { + set.add((long)bit); + } + } + + public boolean get(long bit) + { + if(bit < maxBit) + { + return bits.get((int)bit); + } + else + { + return set.contains((long)bit); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryBuilderContextSolrImpl.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryBuilderContextSolrImpl.java index f67c0a111..c0cc36444 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryBuilderContextSolrImpl.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryBuilderContextSolrImpl.java @@ -1,106 +1,106 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.util.Properties; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.adaptor.QueryParserAdaptor; -import org.alfresco.repo.search.impl.parsers.FTSQueryParser; -import org.alfresco.repo.search.impl.querymodel.impl.lucene.QueryBuilderContext; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Sort; -import org.apache.lucene.util.Version; -import org.apache.solr.core.CoreDescriptorDecorator; -import org.apache.solr.request.SolrQueryRequest; - -/** - * @author andyh - */ -public class Lucene4QueryBuilderContextSolrImpl implements QueryBuilderContext -{ - private Solr4QueryParser lqp; - - private NamespacePrefixResolver namespacePrefixResolver; - - private QueryParserAdaptor lqpa; - - /** - * Context for building lucene queries - * - * @param dictionaryService - * @param namespacePrefixResolver - * @param tenantService - * @param searchParameters - * @param defaultSearchMLAnalysisMode - * @param req - * @param model - */ - public Lucene4QueryBuilderContextSolrImpl(DictionaryService dictionaryService, NamespacePrefixResolver namespacePrefixResolver, TenantService tenantService, - SearchParameters searchParameters, MLAnalysisMode defaultSearchMLAnalysisMode, SolrQueryRequest req, AlfrescoSolrDataModel model, FTSQueryParser.RerankPhase rerankPhase) - { - lqp = new Solr4QueryParser(req, Version.LATEST, searchParameters.getDefaultFieldName(), req.getSchema().getQueryAnalyzer(), rerankPhase); -// lqp.setDefaultOperator(AbstractLuceneQueryParser.OR_OPERATOR); - lqp.setDictionaryService(dictionaryService); - lqp.setNamespacePrefixResolver(namespacePrefixResolver); - lqp.setTenantService(tenantService); - lqp.setSearchParameters(searchParameters); -// lqp.setDefaultSearchMLAnalysisMode(defaultSearchMLAnalysisMode); -// lqp.setIndexReader(indexReader); -// lqp.setAllowLeadingWildcard(true); -// this.namespacePrefixResolver = namespacePrefixResolver; - - Properties props = new CoreDescriptorDecorator(req.getCore().getCoreDescriptor()).getProperties(); - int topTermSpanRewriteLimit = Integer.parseInt(props.getProperty("alfresco.topTermSpanRewriteLimit", "1000")); - lqp.setTopTermSpanRewriteLimit(topTermSpanRewriteLimit); - - lqpa = new Lucene4QueryParserAdaptor(lqp); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderContext#getLuceneQueryParser() - */ - public QueryParserAdaptor getLuceneQueryParserAdaptor() - { - return lqpa; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderContext#getNamespacePrefixResolver() - */ - public NamespacePrefixResolver getNamespacePrefixResolver() - { - return namespacePrefixResolver; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.util.Properties; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.adaptor.QueryParserAdaptor; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.repo.search.impl.querymodel.impl.lucene.QueryBuilderContext; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.util.Version; +import org.apache.solr.core.CoreDescriptorDecorator; +import org.apache.solr.request.SolrQueryRequest; + +/** + * @author andyh + */ +public class Lucene4QueryBuilderContextSolrImpl implements QueryBuilderContext +{ + private Solr4QueryParser lqp; + + private NamespacePrefixResolver namespacePrefixResolver; + + private QueryParserAdaptor lqpa; + + /** + * Context for building lucene queries + * + * @param dictionaryService + * @param namespacePrefixResolver + * @param tenantService + * @param searchParameters + * @param defaultSearchMLAnalysisMode + * @param req + * @param model + */ + public Lucene4QueryBuilderContextSolrImpl(DictionaryService dictionaryService, NamespacePrefixResolver namespacePrefixResolver, TenantService tenantService, + SearchParameters searchParameters, MLAnalysisMode defaultSearchMLAnalysisMode, SolrQueryRequest req, AlfrescoSolrDataModel model, FTSQueryParser.RerankPhase rerankPhase) + { + lqp = new Solr4QueryParser(req, Version.LATEST, searchParameters.getDefaultFieldName(), req.getSchema().getQueryAnalyzer(), rerankPhase); +// lqp.setDefaultOperator(AbstractLuceneQueryParser.OR_OPERATOR); + lqp.setDictionaryService(dictionaryService); + lqp.setNamespacePrefixResolver(namespacePrefixResolver); + lqp.setTenantService(tenantService); + lqp.setSearchParameters(searchParameters); +// lqp.setDefaultSearchMLAnalysisMode(defaultSearchMLAnalysisMode); +// lqp.setIndexReader(indexReader); +// lqp.setAllowLeadingWildcard(true); +// this.namespacePrefixResolver = namespacePrefixResolver; + + Properties props = new CoreDescriptorDecorator(req.getCore().getCoreDescriptor()).getProperties(); + int topTermSpanRewriteLimit = Integer.parseInt(props.getProperty("alfresco.topTermSpanRewriteLimit", "1000")); + lqp.setTopTermSpanRewriteLimit(topTermSpanRewriteLimit); + + lqpa = new Lucene4QueryParserAdaptor(lqp); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderContext#getLuceneQueryParser() + */ + public QueryParserAdaptor getLuceneQueryParserAdaptor() + { + return lqpa; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderContext#getNamespacePrefixResolver() + */ + public NamespacePrefixResolver getNamespacePrefixResolver() + { + return namespacePrefixResolver; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryParserAdaptor.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryParserAdaptor.java index 932ba20f8..3184232fc 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryParserAdaptor.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryParserAdaptor.java @@ -1,427 +1,427 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.util.List; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.search.adaptor.QueryParserAdaptor; -import org.alfresco.repo.search.adaptor.QueryParserExpressionAdaptor; -import org.alfresco.repo.search.adaptor.AnalysisMode; -import org.alfresco.repo.search.adaptor.LuceneFunction; -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; -import org.alfresco.repo.search.impl.querymodel.Ordering; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.cmr.search.SearchParameters; -import org.apache.lucene.index.Term; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BoostQuery; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.TermQuery; - -/** - * @author Andy - * - */ -public class Lucene4QueryParserAdaptor implements QueryParserAdaptor -{ - - private Solr4QueryParser lqp; - - /** - * @param lqp - */ - public Lucene4QueryParserAdaptor(Solr4QueryParser lqp) - { - this.lqp = lqp; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) - */ - @Override - public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException - { - return lqp.getFieldQuery(field, queryText, analysisMode, luceneFunction); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getRangeQuery(java.lang.String, java.lang.String, java.lang.String, boolean, boolean, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) - */ - @Override - public Query getRangeQuery(String field, String lower, String upper, boolean includeLower, boolean includeUpper, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return lqp.getRangeQuery(field, lower, upper, includeLower, includeUpper, analysisMode, luceneFunction); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchAllQuery() - */ - @Override - public Query getMatchAllQuery() throws ParseException - { - return new MatchAllDocsQuery(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchNoneQuery() - */ - @Override - public Query getMatchNoneQuery() throws ParseException - { - return new TermQuery(new Term("NO_TOKENS", "__")); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getLikeQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) - */ - @Override - public Query getLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException - { - return lqp.getLikeQuery(field, sqlLikeClause, analysisMode); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSearchParameters() - */ - @Override - public SearchParameters getSearchParameters() - { - return lqp.getSearchParameters(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSortField(java.lang.String) - */ - @Override - public String getSortField(String field) throws ParseException - { - // TODO - //return field; - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getIdentifierQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) - */ - @Override - public Query getIdentifierQuery(String field, String stringValue, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException - { - String[] split = stringValue.split(";"); - if(split.length == 1) - { - return lqp.getFieldQuery(field, stringValue, AnalysisMode.IDENTIFIER, luceneFunction); - } - else - { - if(split[1].equalsIgnoreCase("PWC")) - { - return getMatchNoneQuery(); - } - - BooleanQuery.Builder query = new BooleanQuery.Builder(); - BooleanQuery.Builder part1 = new BooleanQuery.Builder(); - part1.add(lqp.getFieldQuery(field, split[0], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); - part1.add(lqp.getFieldQuery("@"+ContentModel.PROP_VERSION_LABEL.toString(), split[1], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); - query.add(part1.build(), Occur.SHOULD); - - if(split[1].equals("1.0")) - { - BooleanQuery.Builder part2 = new BooleanQuery.Builder(); - part2.add(lqp.getFieldQuery(field, split[0], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); - part2.add(lqp.getFieldQuery(QueryConstants.FIELD_ASPECT, ContentModel.ASPECT_VERSIONABLE.toString(), AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST_NOT); - query.add(part2.build(), Occur.SHOULD); - } - return query.build(); - } - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getIdentifieLikeQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) - */ - @Override - public Query getIdentifieLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException - { - return getLikeQuery(field, sqlLikeClause, analysisMode); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#sortFieldExists(java.lang.String) - */ - @Override - public boolean sortFieldExists(String noLocalField) - { - // TODO Auto-generated method stub - //return false; - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String) - */ - @Override - public Query getFieldQuery(String field, String queryText) throws ParseException - { - return lqp.getFieldQuery(field, queryText); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#buildSort(java.util.List, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) - */ - @Override - public Sort buildSort(List list, FunctionEvaluationContext functionContext) throws ParseException - { - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFuzzyQuery(java.lang.String, java.lang.String, java.lang.Float) - */ - @Override - public Query getFuzzyQuery(String luceneFieldName, String term, Float minSimilarity) throws ParseException - { - return lqp.getFuzzyQuery(luceneFieldName, term, minSimilarity); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getField() - */ - @Override - public String getField() - { - return lqp.getField(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getPhraseSlop() - */ - @Override - public int getPhraseSlop() - { - return lqp.getPhraseSlop(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, java.lang.Integer, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) - */ - @Override - public Query getFieldQuery(String luceneFieldName, String term, AnalysisMode analysisMode, Integer slop, LuceneFunction luceneFunction) throws ParseException - { - return lqp.getFieldQuery(luceneFieldName, term, analysisMode, slop, luceneFunction); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getPrefixQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) - */ - @Override - public Query getPrefixQuery(String luceneFieldName, String term, AnalysisMode analysisMode) throws ParseException - { - return lqp.getPrefixQuery(luceneFieldName, term, analysisMode); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSpanQuery(java.lang.String, java.lang.String, java.lang.String, int, boolean) - */ - @Override - public Query getSpanQuery(String field, String first, String last, int slop, boolean inOrder) throws ParseException - { - return lqp.getSpanQuery(field, first, last, slop, inOrder); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getWildcardQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) - */ - @Override - public Query getWildcardQuery(String luceneFieldName, String term, AnalysisMode analysisMode) throws ParseException - { - return lqp.getWildcardQuery(luceneFieldName, term, analysisMode); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getNegatedQuery(java.lang.Object) - */ - @Override - public Query getNegatedQuery(Query query) throws ParseException - { - QueryParserExpressionAdaptor expressionAdaptor = getExpressionAdaptor(); - expressionAdaptor.addRequired(getMatchAllQuery()); - expressionAdaptor.addExcluded(query); - return expressionAdaptor.getQuery(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getExpressionAdaptor() - */ - @Override - public QueryParserExpressionAdaptor getExpressionAdaptor() - { - return new Lucene4QueryParserExpressionAdaptor(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchAllNodesQuery() - */ - @Override - public Query getMatchAllNodesQuery() - { - return new TermQuery(new Term("ISNODE", "T")); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getDatetimeSortField(java.lang.String, org.alfresco.service.cmr.dictionary.PropertyDefinition) - */ - @Override - public String getDatetimeSortField(String field, PropertyDefinition propertyDef) - { - throw new UnsupportedOperationException(); - } - - private class Lucene4QueryParserExpressionAdaptor implements QueryParserExpressionAdaptor - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addRequired(java.lang.Object) - */ - @Override - public void addRequired(Query q) - { - booleanQuery.add(q, Occur.MUST); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addExcluded(java.lang.Object) - */ - @Override - public void addExcluded(Query q) - { - booleanQuery.add(q, Occur.MUST_NOT); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addOptoinal(java.lang.Object) - */ - @Override - public void addOptional(Query q) - { - booleanQuery.add(q, Occur.SHOULD); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#getQuery() - */ - @Override - public Query getQuery() throws ParseException - { - BooleanQuery query = booleanQuery.build(); - if(query.clauses().size() == 0) - { - return getMatchNoneQuery(); - } - else if (query.clauses().size() == 1) - { - BooleanClause clause = query.clauses().get(0); - if(clause.isProhibited()) - { - booleanQuery.add(getMatchAllQuery(), Occur.MUST); - return booleanQuery.build(); - } - else - { - return clause.getQuery(); - } - } - else - { - return query; - } - } - - public Query getNegatedQuery() throws ParseException - { - BooleanQuery query = booleanQuery.build(); - if(query.clauses().size() == 0) - { - return getMatchAllQuery(); - } - else if (query.clauses().size() == 1) - { - BooleanClause clause = query.clauses().get(0); - if(clause.isProhibited()) - { - return clause.getQuery(); - } - else - { - return Lucene4QueryParserAdaptor.this.getNegatedQuery(getQuery()); - } - } - else - { - return Lucene4QueryParserAdaptor.this.getNegatedQuery(getQuery()); - } - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addRequired(java.lang.Object, float) - */ - @Override - public void addRequired(Query q, float boost) throws ParseException - { - addRequired(new BoostQuery(q, boost)); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addExcluded(java.lang.Object, float) - */ - @Override - public void addExcluded(Query q, float boost) throws ParseException - { - addExcluded(new BoostQuery(q, boost)); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addOptional(java.lang.Object, float) - */ - @Override - public void addOptional(Query q, float boost) throws ParseException - { - addOptional(new BoostQuery(q, boost)); - - } - - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.search.adaptor.QueryParserAdaptor; +import org.alfresco.repo.search.adaptor.QueryParserExpressionAdaptor; +import org.alfresco.repo.search.adaptor.AnalysisMode; +import org.alfresco.repo.search.adaptor.LuceneFunction; +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.Ordering; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.search.SearchParameters; +import org.apache.lucene.index.Term; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BoostQuery; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.TermQuery; + +/** + * @author Andy + * + */ +public class Lucene4QueryParserAdaptor implements QueryParserAdaptor +{ + + private Solr4QueryParser lqp; + + /** + * @param lqp + */ + public Lucene4QueryParserAdaptor(Solr4QueryParser lqp) + { + this.lqp = lqp; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) + */ + @Override + public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException + { + return lqp.getFieldQuery(field, queryText, analysisMode, luceneFunction); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getRangeQuery(java.lang.String, java.lang.String, java.lang.String, boolean, boolean, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) + */ + @Override + public Query getRangeQuery(String field, String lower, String upper, boolean includeLower, boolean includeUpper, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return lqp.getRangeQuery(field, lower, upper, includeLower, includeUpper, analysisMode, luceneFunction); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchAllQuery() + */ + @Override + public Query getMatchAllQuery() throws ParseException + { + return new MatchAllDocsQuery(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchNoneQuery() + */ + @Override + public Query getMatchNoneQuery() throws ParseException + { + return new TermQuery(new Term("NO_TOKENS", "__")); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getLikeQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) + */ + @Override + public Query getLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException + { + return lqp.getLikeQuery(field, sqlLikeClause, analysisMode); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSearchParameters() + */ + @Override + public SearchParameters getSearchParameters() + { + return lqp.getSearchParameters(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSortField(java.lang.String) + */ + @Override + public String getSortField(String field) throws ParseException + { + // TODO + //return field; + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getIdentifierQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) + */ + @Override + public Query getIdentifierQuery(String field, String stringValue, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException + { + String[] split = stringValue.split(";"); + if(split.length == 1) + { + return lqp.getFieldQuery(field, stringValue, AnalysisMode.IDENTIFIER, luceneFunction); + } + else + { + if(split[1].equalsIgnoreCase("PWC")) + { + return getMatchNoneQuery(); + } + + BooleanQuery.Builder query = new BooleanQuery.Builder(); + BooleanQuery.Builder part1 = new BooleanQuery.Builder(); + part1.add(lqp.getFieldQuery(field, split[0], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); + part1.add(lqp.getFieldQuery("@"+ContentModel.PROP_VERSION_LABEL.toString(), split[1], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); + query.add(part1.build(), Occur.SHOULD); + + if(split[1].equals("1.0")) + { + BooleanQuery.Builder part2 = new BooleanQuery.Builder(); + part2.add(lqp.getFieldQuery(field, split[0], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); + part2.add(lqp.getFieldQuery(QueryConstants.FIELD_ASPECT, ContentModel.ASPECT_VERSIONABLE.toString(), AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST_NOT); + query.add(part2.build(), Occur.SHOULD); + } + return query.build(); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getIdentifieLikeQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) + */ + @Override + public Query getIdentifieLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException + { + return getLikeQuery(field, sqlLikeClause, analysisMode); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#sortFieldExists(java.lang.String) + */ + @Override + public boolean sortFieldExists(String noLocalField) + { + // TODO Auto-generated method stub + //return false; + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String) + */ + @Override + public Query getFieldQuery(String field, String queryText) throws ParseException + { + return lqp.getFieldQuery(field, queryText); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#buildSort(java.util.List, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public Sort buildSort(List list, FunctionEvaluationContext functionContext) throws ParseException + { + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFuzzyQuery(java.lang.String, java.lang.String, java.lang.Float) + */ + @Override + public Query getFuzzyQuery(String luceneFieldName, String term, Float minSimilarity) throws ParseException + { + return lqp.getFuzzyQuery(luceneFieldName, term, minSimilarity); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getField() + */ + @Override + public String getField() + { + return lqp.getField(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getPhraseSlop() + */ + @Override + public int getPhraseSlop() + { + return lqp.getPhraseSlop(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, java.lang.Integer, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) + */ + @Override + public Query getFieldQuery(String luceneFieldName, String term, AnalysisMode analysisMode, Integer slop, LuceneFunction luceneFunction) throws ParseException + { + return lqp.getFieldQuery(luceneFieldName, term, analysisMode, slop, luceneFunction); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getPrefixQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) + */ + @Override + public Query getPrefixQuery(String luceneFieldName, String term, AnalysisMode analysisMode) throws ParseException + { + return lqp.getPrefixQuery(luceneFieldName, term, analysisMode); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSpanQuery(java.lang.String, java.lang.String, java.lang.String, int, boolean) + */ + @Override + public Query getSpanQuery(String field, String first, String last, int slop, boolean inOrder) throws ParseException + { + return lqp.getSpanQuery(field, first, last, slop, inOrder); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getWildcardQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) + */ + @Override + public Query getWildcardQuery(String luceneFieldName, String term, AnalysisMode analysisMode) throws ParseException + { + return lqp.getWildcardQuery(luceneFieldName, term, analysisMode); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getNegatedQuery(java.lang.Object) + */ + @Override + public Query getNegatedQuery(Query query) throws ParseException + { + QueryParserExpressionAdaptor expressionAdaptor = getExpressionAdaptor(); + expressionAdaptor.addRequired(getMatchAllQuery()); + expressionAdaptor.addExcluded(query); + return expressionAdaptor.getQuery(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getExpressionAdaptor() + */ + @Override + public QueryParserExpressionAdaptor getExpressionAdaptor() + { + return new Lucene4QueryParserExpressionAdaptor(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchAllNodesQuery() + */ + @Override + public Query getMatchAllNodesQuery() + { + return new TermQuery(new Term("ISNODE", "T")); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getDatetimeSortField(java.lang.String, org.alfresco.service.cmr.dictionary.PropertyDefinition) + */ + @Override + public String getDatetimeSortField(String field, PropertyDefinition propertyDef) + { + throw new UnsupportedOperationException(); + } + + private class Lucene4QueryParserExpressionAdaptor implements QueryParserExpressionAdaptor + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addRequired(java.lang.Object) + */ + @Override + public void addRequired(Query q) + { + booleanQuery.add(q, Occur.MUST); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addExcluded(java.lang.Object) + */ + @Override + public void addExcluded(Query q) + { + booleanQuery.add(q, Occur.MUST_NOT); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addOptoinal(java.lang.Object) + */ + @Override + public void addOptional(Query q) + { + booleanQuery.add(q, Occur.SHOULD); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#getQuery() + */ + @Override + public Query getQuery() throws ParseException + { + BooleanQuery query = booleanQuery.build(); + if(query.clauses().size() == 0) + { + return getMatchNoneQuery(); + } + else if (query.clauses().size() == 1) + { + BooleanClause clause = query.clauses().get(0); + if(clause.isProhibited()) + { + booleanQuery.add(getMatchAllQuery(), Occur.MUST); + return booleanQuery.build(); + } + else + { + return clause.getQuery(); + } + } + else + { + return query; + } + } + + public Query getNegatedQuery() throws ParseException + { + BooleanQuery query = booleanQuery.build(); + if(query.clauses().size() == 0) + { + return getMatchAllQuery(); + } + else if (query.clauses().size() == 1) + { + BooleanClause clause = query.clauses().get(0); + if(clause.isProhibited()) + { + return clause.getQuery(); + } + else + { + return Lucene4QueryParserAdaptor.this.getNegatedQuery(getQuery()); + } + } + else + { + return Lucene4QueryParserAdaptor.this.getNegatedQuery(getQuery()); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addRequired(java.lang.Object, float) + */ + @Override + public void addRequired(Query q, float boost) throws ParseException + { + addRequired(new BoostQuery(q, boost)); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addExcluded(java.lang.Object, float) + */ + @Override + public void addExcluded(Query q, float boost) throws ParseException + { + addExcluded(new BoostQuery(q, boost)); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addOptional(java.lang.Object, float) + */ + @Override + public void addOptional(Query q, float boost) throws ParseException + { + addOptional(new BoostQuery(q, boost)); + + } + + } + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingAnalyticsQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingAnalyticsQuery.java index 6373e6f28..7b3a8116d 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingAnalyticsQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingAnalyticsQuery.java @@ -1,65 +1,65 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.util.HashMap; - -import org.apache.lucene.search.IndexSearcher; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.search.AnalyticsQuery; -import org.apache.solr.search.DelegatingCollector; - -/** - * @author Andy - * - */ -public class MimetypeGroupingAnalyticsQuery extends AnalyticsQuery -{ - private HashMap mappings; - private boolean group; - - /** - * @param mappings - * @param group - */ - public MimetypeGroupingAnalyticsQuery(HashMap mappings, boolean group) - { - this.mappings = mappings; - this.group = group; - - } - - /* (non-Javadoc) - * @see org.apache.solr.search.AnalyticsQuery#getAnalyticsCollector(org.apache.solr.handler.component.ResponseBuilder, org.apache.lucene.search.IndexSearcher) - */ - @Override - public DelegatingCollector getAnalyticsCollector(ResponseBuilder rb, IndexSearcher searcher) - { - return new MimetypeGroupingCollector(rb, mappings, group); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.util.HashMap; + +import org.apache.lucene.search.IndexSearcher; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.search.AnalyticsQuery; +import org.apache.solr.search.DelegatingCollector; + +/** + * @author Andy + * + */ +public class MimetypeGroupingAnalyticsQuery extends AnalyticsQuery +{ + private HashMap mappings; + private boolean group; + + /** + * @param mappings + * @param group + */ + public MimetypeGroupingAnalyticsQuery(HashMap mappings, boolean group) + { + this.mappings = mappings; + this.group = group; + + } + + /* (non-Javadoc) + * @see org.apache.solr.search.AnalyticsQuery#getAnalyticsCollector(org.apache.solr.handler.component.ResponseBuilder, org.apache.lucene.search.IndexSearcher) + */ + @Override + public DelegatingCollector getAnalyticsCollector(ResponseBuilder rb, IndexSearcher searcher) + { + return new MimetypeGroupingCollector(rb, mappings, group); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingCollector.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingCollector.java index e2495756b..704bc31c0 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingCollector.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingCollector.java @@ -1,132 +1,132 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map.Entry; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.SortedDocValues; -import org.apache.lucene.util.Counter; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.DelegatingCollector; - -/** - * @author Andy - * - */ -public class MimetypeGroupingCollector extends DelegatingCollector -{ - HashMap counters = new HashMap(); - ResponseBuilder rb; - private HashMap mappings; - private boolean doGroup; - String schemaFieldName; - SchemaField schemaField; - SortedDocValues sortedDocValues; - /** - * @param rb - * @param mappings - * @param group - */ - public MimetypeGroupingCollector(ResponseBuilder rb, HashMap mappings, boolean doGroup) - { - this.rb = rb; - this.mappings = mappings; - this.doGroup = doGroup; - schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty("content.mimetype", FieldUse.FACET, rb.req); - schemaField = rb.req.getSchema().getFieldOrNull(schemaFieldName); - } - - - @Override - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - if(schemaField != null) - { - try - { - sortedDocValues = context.reader().getSortedDocValues(schemaFieldName); - } - catch (IOException e) - { - - } - } - } - - @Override - public void collect(int doc) throws IOException - { - if(sortedDocValues != null) - { - int ordinal = sortedDocValues.getOrd(doc); - if(ordinal > -1) - { - String value = (String)schemaField.getType().toObject(schemaField, sortedDocValues.lookupOrd(ordinal)); - String group = doGroup ? mappings.get(value) : value; - if(group == null) - { - group = value; - } - - Counter counter = counters.get(group); - if(counter == null) - { - counter = Counter.newCounter(); - counters.put(group, counter); - } - counter.addAndGet(1); - } - } - - - leafDelegate.collect(doc); - } - - public void finish() throws IOException - { - NamedList analytics = new NamedList<>(); - rb.rsp.add("analytics", analytics); - NamedList fieldCounts = new NamedList<>(); - analytics.add("mimetype()", fieldCounts); - for (Entry counter : counters.entrySet()) - { - fieldCounts.add(counter.getKey(), counter.getValue().get()); - } - - if(this.delegate instanceof DelegatingCollector) { - ((DelegatingCollector)this.delegate).finish(); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map.Entry; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.util.Counter; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DelegatingCollector; + +/** + * @author Andy + * + */ +public class MimetypeGroupingCollector extends DelegatingCollector +{ + HashMap counters = new HashMap(); + ResponseBuilder rb; + private HashMap mappings; + private boolean doGroup; + String schemaFieldName; + SchemaField schemaField; + SortedDocValues sortedDocValues; + /** + * @param rb + * @param mappings + * @param group + */ + public MimetypeGroupingCollector(ResponseBuilder rb, HashMap mappings, boolean doGroup) + { + this.rb = rb; + this.mappings = mappings; + this.doGroup = doGroup; + schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty("content.mimetype", FieldUse.FACET, rb.req); + schemaField = rb.req.getSchema().getFieldOrNull(schemaFieldName); + } + + + @Override + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + if(schemaField != null) + { + try + { + sortedDocValues = context.reader().getSortedDocValues(schemaFieldName); + } + catch (IOException e) + { + + } + } + } + + @Override + public void collect(int doc) throws IOException + { + if(sortedDocValues != null) + { + int ordinal = sortedDocValues.getOrd(doc); + if(ordinal > -1) + { + String value = (String)schemaField.getType().toObject(schemaField, sortedDocValues.lookupOrd(ordinal)); + String group = doGroup ? mappings.get(value) : value; + if(group == null) + { + group = value; + } + + Counter counter = counters.get(group); + if(counter == null) + { + counter = Counter.newCounter(); + counters.put(group, counter); + } + counter.addAndGet(1); + } + } + + + leafDelegate.collect(doc); + } + + public void finish() throws IOException + { + NamedList analytics = new NamedList<>(); + rb.rsp.add("analytics", analytics); + NamedList fieldCounts = new NamedList<>(); + analytics.add("mimetype()", fieldCounts); + for (Entry counter : counters.entrySet()) + { + fieldCounts.add(counter.getKey(), counter.getValue().get()); + } + + if(this.delegate instanceof DelegatingCollector) { + ((DelegatingCollector)this.delegate).finish(); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingQParserPlugin.java index 3557525e3..e875aeaf8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingQParserPlugin.java @@ -1,163 +1,163 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; - -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.core.SolrResourceLoader; -import org.apache.solr.internal.csv.CSVParser; -import org.apache.solr.internal.csv.CSVStrategy; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class MimetypeGroupingQParserPlugin extends QParserPlugin -{ - protected static final Logger LOGGER = LoggerFactory.getLogger(MimetypeGroupingQParserPlugin.class); - - private static HashMap mappings = new HashMap<>(); - - private static HashMap> reverseMappings = new HashMap<>(); - - public static synchronized HashMap> getReverseMappings() - { - return reverseMappings; - } - - private static synchronized void initMap(String mappingFile) - { - String solrHome = SolrResourceLoader.locateSolrHome().toString(); - File file = new File(solrHome, mappingFile); - - CSVParser parser; - try - { - parser = new CSVParser(new FileReader(file), CSVStrategy.DEFAULT_STRATEGY); - // parse the fieldnames from the header of the file - parser.getLine(); - - // read the rest of the CSV file - for (;;) - { - String[] vals = null; - - vals = parser.getLine(); - - if (vals == null) - break; - - mappings.put(vals[1], vals[2]); - - ArrayList grouped = reverseMappings.get(vals[2]); - if(grouped == null) - { - grouped = new ArrayList<>(); - reverseMappings.put(vals[2], grouped); - } - if (!grouped.contains(vals[1])) - { - grouped.add(vals[1]); - } - } - } - catch (Exception exception) - { - LOGGER.error("Exception during the MimetypeGroupingQParserPlugin. See the stacktrace below for further details.", exception); - } - } - - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - @Override - public void init(NamedList args) - { - this.args = args; - String mappingFile = (String) args.get("mapping"); - initMap(mappingFile); - - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new MimetypeGroupingQParser(qstr, localParams, params, req, args, mappings); - } - - public static class MimetypeGroupingQParser extends AbstractQParser - { - private HashMap mappings; - - private boolean group = true; - - public MimetypeGroupingQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args, HashMap mappings) - { - super(qstr, localParams, params, req, args); - Boolean doGroup = localParams.getBool("group"); - if(doGroup != null) - { - group = doGroup.booleanValue(); - } - - this.mappings = mappings; - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - return new MimetypeGroupingAnalyticsQuery(mappings, group); - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.core.SolrResourceLoader; +import org.apache.solr.internal.csv.CSVParser; +import org.apache.solr.internal.csv.CSVStrategy; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class MimetypeGroupingQParserPlugin extends QParserPlugin +{ + protected static final Logger LOGGER = LoggerFactory.getLogger(MimetypeGroupingQParserPlugin.class); + + private static HashMap mappings = new HashMap<>(); + + private static HashMap> reverseMappings = new HashMap<>(); + + public static synchronized HashMap> getReverseMappings() + { + return reverseMappings; + } + + private static synchronized void initMap(String mappingFile) + { + String solrHome = SolrResourceLoader.locateSolrHome().toString(); + File file = new File(solrHome, mappingFile); + + CSVParser parser; + try + { + parser = new CSVParser(new FileReader(file), CSVStrategy.DEFAULT_STRATEGY); + // parse the fieldnames from the header of the file + parser.getLine(); + + // read the rest of the CSV file + for (;;) + { + String[] vals = null; + + vals = parser.getLine(); + + if (vals == null) + break; + + mappings.put(vals[1], vals[2]); + + ArrayList grouped = reverseMappings.get(vals[2]); + if(grouped == null) + { + grouped = new ArrayList<>(); + reverseMappings.put(vals[2], grouped); + } + if (!grouped.contains(vals[1])) + { + grouped.add(vals[1]); + } + } + } + catch (Exception exception) + { + LOGGER.error("Exception during the MimetypeGroupingQParserPlugin. See the stacktrace below for further details.", exception); + } + } + + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + @Override + public void init(NamedList args) + { + this.args = args; + String mappingFile = (String) args.get("mapping"); + initMap(mappingFile); + + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new MimetypeGroupingQParser(qstr, localParams, params, req, args, mappings); + } + + public static class MimetypeGroupingQParser extends AbstractQParser + { + private HashMap mappings; + + private boolean group = true; + + public MimetypeGroupingQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args, HashMap mappings) + { + super(qstr, localParams, params, req, args); + Boolean doGroup = localParams.getBool("group"); + if(doGroup != null) + { + group = doGroup.booleanValue(); + } + + this.mappings = mappings; + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + return new MimetypeGroupingAnalyticsQuery(mappings, group); + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/PostFilterQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/PostFilterQuery.java index ae3888b9e..386598b2d 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/PostFilterQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/PostFilterQuery.java @@ -1,257 +1,257 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import org.alfresco.solr.ContextAwareQuery; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BoostQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.solr.search.DelegatingCollector; -import org.apache.solr.search.PostFilter; - -public class PostFilterQuery extends Query implements PostFilter -{ - private int cost; - private final Query query; - - public PostFilterQuery(int cost, Query query) - { - this.cost = cost; - if (query == null) throw new IllegalStateException("query cannot be null"); - this.query = query; - } - - @Override - public int hashCode() { - //DON'T INCLUDE COST?? - int result = cost; - result = 31 * result + query.hashCode(); - return result; - } - - //THIS WAS WRONG public int hashcode() - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof PostFilterQuery)) return false; - - PostFilterQuery that = (PostFilterQuery) o; - - //DON'T INCLUDE COST?? if (cost != that.cost) return false; - return query.equals(that.query); - } - - public int getCost() - { - return cost; - } - - @Override - public void setCost(int cost) - { - this.cost = cost; - } - - public boolean getCache() - { - return false; - } - - @Override - public void setCache(boolean cache) - { - - } - - public boolean getCacheSep() - { - return false; - } - - @Override - public void setCacheSep(boolean cacheSep) - { - - } - - public String toString(String s) - { - return s; - } - - public DelegatingCollector getFilterCollector(IndexSearcher searcher) - { - List postFilters = new ArrayList(); - getPostFilters(query, postFilters); - - Collections.sort(postFilters, new PostFilterComp()); - - List delegatingCollectors = new ArrayList(); - for(PostFilter postFilter : postFilters) - { - DelegatingCollector delegatingCollector = postFilter.getFilterCollector(searcher); - if(!(delegatingCollector instanceof AllAccessCollector)) { - delegatingCollectors.add(delegatingCollector); - } - } - - if(delegatingCollectors.size() == 0) - { - return new AllAccessCollector(); - } - else if(delegatingCollectors.size() == 1) - { - return delegatingCollectors.get(0); - } - else - { - return new WrapperCollector(delegatingCollectors); - } - } - - private static class WrapperCollector extends DelegatingCollector - { - private DelegatingCollector innerDelegate; - private CollectorSink sink; - - public WrapperCollector(List delegatingCollectors) - { - for(DelegatingCollector delegatingCollector : delegatingCollectors) - { - if(innerDelegate == null) - { - innerDelegate = delegatingCollector; - } - else - { - innerDelegate.setLastDelegate(delegatingCollector); - } - } - - this.sink = new CollectorSink(); - innerDelegate.setLastDelegate(this.sink); - } - - public void setScorer(Scorer scorer) throws IOException - { - super.setScorer(scorer); - innerDelegate.setScorer(scorer); - } - - protected void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - innerDelegate.getLeafCollector(context); - } - - - public void collect(int doc) throws IOException - { - innerDelegate.collect(doc); - if(sink.doc == doc) { - sink.doc = -1; - super.collect(doc); - } - } - } - - private static class CollectorSink extends DelegatingCollector - { - public int doc = -1; - - public void collect(int doc) throws IOException - { - this.doc = doc; - } - - public void doSetNextReader(LeafReaderContext context) - { - - } - } - - private void getPostFilters(Query q, List postFilters) - { - if(q instanceof BooleanQuery) - { - BooleanQuery bq = (BooleanQuery) q; - List clauses = bq.clauses(); - for (BooleanClause clause : clauses) - { - Query q1 = clause.getQuery(); - getPostFilters(q1, postFilters); - } - } - else if(q instanceof ContextAwareQuery) - { - ContextAwareQuery cq = (ContextAwareQuery)q; - getPostFilters(cq.getLuceneQuery(), postFilters); - } - else if(q instanceof PostFilter) - { - postFilters.add((PostFilter)q); - } - else if(q instanceof BoostQuery) - { - BoostQuery bq = (BoostQuery)q; - getPostFilters(bq.getQuery(), postFilters); - } - } - - private class PostFilterComp implements Comparator - { - public int compare(PostFilter a, PostFilter b) - { - int costa = a.getCost(); - int costb = b.getCost(); - if(costa == costb) - { - return 0; - } - else if(costa < costb) - { - return -1; - } - else - { - return 1; - } - } - } - -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.alfresco.solr.ContextAwareQuery; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BoostQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.solr.search.DelegatingCollector; +import org.apache.solr.search.PostFilter; + +public class PostFilterQuery extends Query implements PostFilter +{ + private int cost; + private final Query query; + + public PostFilterQuery(int cost, Query query) + { + this.cost = cost; + if (query == null) throw new IllegalStateException("query cannot be null"); + this.query = query; + } + + @Override + public int hashCode() { + //DON'T INCLUDE COST?? + int result = cost; + result = 31 * result + query.hashCode(); + return result; + } + + //THIS WAS WRONG public int hashcode() + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PostFilterQuery)) return false; + + PostFilterQuery that = (PostFilterQuery) o; + + //DON'T INCLUDE COST?? if (cost != that.cost) return false; + return query.equals(that.query); + } + + public int getCost() + { + return cost; + } + + @Override + public void setCost(int cost) + { + this.cost = cost; + } + + public boolean getCache() + { + return false; + } + + @Override + public void setCache(boolean cache) + { + + } + + public boolean getCacheSep() + { + return false; + } + + @Override + public void setCacheSep(boolean cacheSep) + { + + } + + public String toString(String s) + { + return s; + } + + public DelegatingCollector getFilterCollector(IndexSearcher searcher) + { + List postFilters = new ArrayList(); + getPostFilters(query, postFilters); + + Collections.sort(postFilters, new PostFilterComp()); + + List delegatingCollectors = new ArrayList(); + for(PostFilter postFilter : postFilters) + { + DelegatingCollector delegatingCollector = postFilter.getFilterCollector(searcher); + if(!(delegatingCollector instanceof AllAccessCollector)) { + delegatingCollectors.add(delegatingCollector); + } + } + + if(delegatingCollectors.size() == 0) + { + return new AllAccessCollector(); + } + else if(delegatingCollectors.size() == 1) + { + return delegatingCollectors.get(0); + } + else + { + return new WrapperCollector(delegatingCollectors); + } + } + + private static class WrapperCollector extends DelegatingCollector + { + private DelegatingCollector innerDelegate; + private CollectorSink sink; + + public WrapperCollector(List delegatingCollectors) + { + for(DelegatingCollector delegatingCollector : delegatingCollectors) + { + if(innerDelegate == null) + { + innerDelegate = delegatingCollector; + } + else + { + innerDelegate.setLastDelegate(delegatingCollector); + } + } + + this.sink = new CollectorSink(); + innerDelegate.setLastDelegate(this.sink); + } + + public void setScorer(Scorer scorer) throws IOException + { + super.setScorer(scorer); + innerDelegate.setScorer(scorer); + } + + protected void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + innerDelegate.getLeafCollector(context); + } + + + public void collect(int doc) throws IOException + { + innerDelegate.collect(doc); + if(sink.doc == doc) { + sink.doc = -1; + super.collect(doc); + } + } + } + + private static class CollectorSink extends DelegatingCollector + { + public int doc = -1; + + public void collect(int doc) throws IOException + { + this.doc = doc; + } + + public void doSetNextReader(LeafReaderContext context) + { + + } + } + + private void getPostFilters(Query q, List postFilters) + { + if(q instanceof BooleanQuery) + { + BooleanQuery bq = (BooleanQuery) q; + List clauses = bq.clauses(); + for (BooleanClause clause : clauses) + { + Query q1 = clause.getQuery(); + getPostFilters(q1, postFilters); + } + } + else if(q instanceof ContextAwareQuery) + { + ContextAwareQuery cq = (ContextAwareQuery)q; + getPostFilters(cq.getLuceneQuery(), postFilters); + } + else if(q instanceof PostFilter) + { + postFilters.add((PostFilter)q); + } + else if(q instanceof BoostQuery) + { + BoostQuery bq = (BoostQuery)q; + getPostFilters(bq.getQuery(), postFilters); + } + } + + private class PostFilterComp implements Comparator + { + public int compare(PostFilter a, PostFilter b) + { + int costa = a.getCost(); + int costb = b.getCost(); + if(costa == costb) + { + return 0; + } + else if(costa < costb) + { + return -1; + } + else + { + return 1; + } + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/RelativeStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/RelativeStructuredFieldPosition.java index a530329d7..77b481a9f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/RelativeStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/RelativeStructuredFieldPosition.java @@ -1,142 +1,142 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -/** - * Search for a term relative to the last one found. - * - * @author andyh - */ -public class RelativeStructuredFieldPosition extends AbstractStructuredFieldPosition -{ - - int relativePosition; - - /** - * - */ - public RelativeStructuredFieldPosition(String termText) - { - super(getTermText(termText), true, false); - relativePosition = 1; - - } - - private static String getTermText(String termText) - { - if((termText != null) && (termText.equals("*"))) - { - return null; - } - else - { - return termText; - } - } - - public RelativeStructuredFieldPosition() - { - super(null, false, false); - relativePosition = 1; - } - - /* - * (non-Javadoc) - * - * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, - * int, org.apache.lucene.index.TermPositions) - */ - public int matches(int start, int end, int offset) throws IOException - { - - if (getCachingTermPositions() != null) - { - // Doing "termText" - getCachingTermPositions().reset(); - int count = getCachingTermPositions().freq(); - int requiredPosition = offset + relativePosition; - int realPosition = 0; - int adjustedPosition = 0; - for (int i = 0; i < count; i++) - { - realPosition = getCachingTermPositions().nextPosition(); - adjustedPosition = realPosition - start; - if ((end != -1) && (realPosition > end)) - { - return -1; - } - if (adjustedPosition == requiredPosition) - { - return adjustedPosition; - } - if (adjustedPosition > requiredPosition) - { - return -1; - } - } - } - else - { - // Doing "*"; - return offset + 1; - } - return -1; - } - - public String getDescription() - { - return "Relative Named child"; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + relativePosition; - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - RelativeStructuredFieldPosition other = (RelativeStructuredFieldPosition) obj; - if (relativePosition != other.relativePosition) - return false; - return true; - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +/** + * Search for a term relative to the last one found. + * + * @author andyh + */ +public class RelativeStructuredFieldPosition extends AbstractStructuredFieldPosition +{ + + int relativePosition; + + /** + * + */ + public RelativeStructuredFieldPosition(String termText) + { + super(getTermText(termText), true, false); + relativePosition = 1; + + } + + private static String getTermText(String termText) + { + if((termText != null) && (termText.equals("*"))) + { + return null; + } + else + { + return termText; + } + } + + public RelativeStructuredFieldPosition() + { + super(null, false, false); + relativePosition = 1; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, + * int, org.apache.lucene.index.TermPositions) + */ + public int matches(int start, int end, int offset) throws IOException + { + + if (getCachingTermPositions() != null) + { + // Doing "termText" + getCachingTermPositions().reset(); + int count = getCachingTermPositions().freq(); + int requiredPosition = offset + relativePosition; + int realPosition = 0; + int adjustedPosition = 0; + for (int i = 0; i < count; i++) + { + realPosition = getCachingTermPositions().nextPosition(); + adjustedPosition = realPosition - start; + if ((end != -1) && (realPosition > end)) + { + return -1; + } + if (adjustedPosition == requiredPosition) + { + return adjustedPosition; + } + if (adjustedPosition > requiredPosition) + { + return -1; + } + } + } + else + { + // Doing "*"; + return offset + 1; + } + return -1; + } + + public String getDescription() + { + return "Relative Named child"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + relativePosition; + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + RelativeStructuredFieldPosition other = (RelativeStructuredFieldPosition) obj; + if (relativePosition != other.relativePosition) + return false; + return true; + } + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SelfAxisStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SelfAxisStructuredFieldPosition.java index da6a32acd..7465f2dce 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SelfAxisStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SelfAxisStructuredFieldPosition.java @@ -1,62 +1,62 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -public class SelfAxisStructuredFieldPosition extends AbstractStructuredFieldPosition -{ - - public SelfAxisStructuredFieldPosition() - { - super(null, true, false); - } - - public int matches(int start, int end, int offset) throws IOException - { - return offset; - } - - public String getDescription() - { - return "Self Axis"; - } - - public boolean linkSelf() - { - return true; - } - - public boolean isTerminal() - { - return false; - } - - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +public class SelfAxisStructuredFieldPosition extends AbstractStructuredFieldPosition +{ + + public SelfAxisStructuredFieldPosition() + { + super(null, true, false); + } + + public int matches(int start, int end, int offset) throws IOException + { + return offset; + } + + public String getDescription() + { + return "Self Axis"; + } + + public boolean linkSelf() + { + return true; + } + + public boolean isTerminal() + { + return false; + } + + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Solr4QueryParser.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Solr4QueryParser.java index 4dfa281c2..a0538163d 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Solr4QueryParser.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Solr4QueryParser.java @@ -1,5563 +1,5563 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import edu.umd.cs.findbugs.annotations.SuppressWarnings; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.dictionary.IndexTokenisationMode; -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.adaptor.AnalysisMode; -import org.alfresco.repo.search.adaptor.LuceneFunction; -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.repo.search.impl.QueryParserUtils; -import org.alfresco.repo.search.impl.lucene.analysis.MLTokenDuplicator; -import org.alfresco.repo.search.impl.parsers.FTSQueryException; -import org.alfresco.repo.search.impl.parsers.FTSQueryParser; -import org.alfresco.repo.search.impl.parsers.FTSQueryParser.RerankPhase; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.cmr.dictionary.AspectDefinition; -import org.alfresco.service.cmr.dictionary.ClassDefinition; -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.dictionary.TypeDefinition; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.AlfrescoAnalyzerWrapper; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.SpecializedFieldType; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldInstance; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.alfresco.solr.AlfrescoSolrDataModel.IndexedField; -import org.alfresco.solr.component.FingerPrintComponent; -import org.alfresco.solr.utils.ThrowingFunction; -import org.alfresco.util.CachingDateFormat; -import org.alfresco.util.Pair; -import org.alfresco.util.SearchLanguageConversion; -import org.antlr.misc.OrderedHashSet; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.commongrams.CommonGramsFilter; -import org.apache.lucene.analysis.shingle.ShingleFilterFactory; -import org.apache.lucene.analysis.synonym.SynonymFilter; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; -import org.apache.lucene.analysis.util.TokenFilterFactory; -import org.apache.lucene.document.Document; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.index.Term; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.queryparser.classic.QueryParser; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BooleanQuery.Builder; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.LegacyNumericRangeQuery; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.MultiTermQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.RegexpQuery; -import org.apache.lucene.search.ScoreDoc; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.TermRangeQuery; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper; -import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper.TopTermsSpanBooleanQueryRewrite; -import org.apache.lucene.search.spans.SpanNearQuery; -import org.apache.lucene.search.spans.SpanOrQuery; -import org.apache.lucene.search.spans.SpanQuery; -import org.apache.lucene.search.spans.SpanTermQuery; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.Version; -import org.apache.solr.analysis.TokenizerChain; -import org.apache.solr.client.solrj.SolrRequest; -import org.apache.solr.client.solrj.impl.HttpClientUtil; -import org.apache.solr.client.solrj.impl.HttpSolrClient; -import org.apache.solr.client.solrj.request.QueryRequest; -import org.apache.solr.client.solrj.response.QueryResponse; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.core.SolrCore; -import org.apache.solr.handler.component.HttpShardHandlerFactory; -import org.apache.solr.handler.component.ShardHandlerFactory; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.DocsStreamer; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.update.UpdateShardHandlerConfig; -import org.jaxen.saxpath.SAXPathException; -import org.jaxen.saxpath.base.XPathReader; -import org.json.JSONObject; -import org.springframework.extensions.surf.util.I18NUtil; - -import java.io.IOException; -import java.io.StringReader; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.apache.solr.search.QueryParsing.DEFTYPE; - -/** - * @author Andy - * - */ -public class Solr4QueryParser extends QueryParser implements QueryConstants -{ - - private static final String CMIS_REQUEST_HANDLER = "cmis"; - - public Solr4QueryParser(SolrQueryRequest req, Version matchVersion, String f, Analyzer a, - FTSQueryParser.RerankPhase rerankPhase) - { - super(f, a); - setAllowLeadingWildcard(true); - setAnalyzeRangeTerms(true); - this.rerankPhase = rerankPhase; - this.schema = req.getSchema(); - this.solrParams = req.getParams(); - SolrCore core = req.getCore(); - if(core != null) { - this.shardHandlerFactory = core.getCoreContainer().getShardHandlerFactory(); - } - this.request = req; - - } - - private RerankPhase rerankPhase; - private SolrParams solrParams; - private ShardHandlerFactory shardHandlerFactory; - private SolrQueryRequest request; - - - IndexSchema schema; - - @SuppressWarnings("unused") - private static Log logger = LogFactory.getLog(Solr4QueryParser.class); - - protected NamespacePrefixResolver namespacePrefixResolver; - - protected DictionaryService dictionaryService; - - private TenantService tenantService; - - private SearchParameters searchParameters; - - private MLAnalysisMode mlAnalysisMode = MLAnalysisMode.EXACT_LANGUAGE_AND_ALL; - - private int internalSlop = 0; - - int topTermSpanRewriteLimit = 1000; - - /** - * @param topTermSpanRewriteLimit - * the topTermSpanRewriteLimit to set - */ - public void setTopTermSpanRewriteLimit(int topTermSpanRewriteLimit) - { - this.topTermSpanRewriteLimit = topTermSpanRewriteLimit; - } - - /** - * @param searchParameters - */ - public void setSearchParameters(SearchParameters searchParameters) - { - this.searchParameters = searchParameters; - } - - /** - * @param namespacePrefixResolver - */ - public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) - { - this.namespacePrefixResolver = namespacePrefixResolver; - } - - /** - * @param tenantService - */ - public void setTenantService(TenantService tenantService) - { - this.tenantService = tenantService; - } - - public SearchParameters getSearchParameters() - { - return searchParameters; - } - - @Override - protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException - { - try - { - internalSlop = slop; - Query query; - query = getFieldQuery(field, queryText); - return query; - } - finally - { - internalSlop = 0; - } - - } - - /** - * @param field - * @param queryText - * @param analysisMode - * @param slop - * @param luceneFunction - * @return the query - * @throws ParseException - * @throws IOException - */ - public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, int slop, - LuceneFunction luceneFunction) throws ParseException - { - try - { - internalSlop = slop; - Query query = getFieldQuery(field, queryText, analysisMode, luceneFunction); - return query; - } - finally - { - internalSlop = 0; - } - - } - - /** - * @param field - * @param sqlLikeClause - * @param analysisMode - * @return the query - * @throws ParseException - */ - public Query getLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException - { - String luceneWildCardExpression = translate(sqlLikeClause); - return getWildcardQuery(field, luceneWildCardExpression, AnalysisMode.LIKE); - } - - private String translate(String string) - { - StringBuilder builder = new StringBuilder(string.length()); - - boolean lastWasEscape = false; - - for (int i = 0; i < string.length(); i++) - { - char c = string.charAt(i); - if (lastWasEscape) - { - builder.append(c); - lastWasEscape = false; - } else - { - if (c == '\\') - { - lastWasEscape = true; - } else if (c == '%') - { - builder.append('*'); - } else if (c == '_') - { - builder.append('?'); - } else if (c == '*') - { - builder.append('\\'); - builder.append(c); - } else if (c == '?') - { - builder.append('\\'); - builder.append(c); - } else - { - builder.append(c); - } - } - } - if (lastWasEscape) - { - throw new FTSQueryException("Escape character at end of string " + string); - } - - return builder.toString(); - } - - /** - * @param field - * @param queryText - * @param analysisMode - * @param luceneFunction - * @return the query - * @throws ParseException - * @throws IOException - */ - public Query getDoesNotMatchFieldQuery(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - Query allQuery = new MatchAllDocsQuery(); - Query matchQuery = getFieldQuery(field, queryText, analysisMode, luceneFunction); - if ((matchQuery != null)) - { - query.add(allQuery, Occur.MUST); - query.add(matchQuery, Occur.MUST_NOT); - } else - { - throw new UnsupportedOperationException(); - } - return query.build(); - } - - public Query getFieldQuery(String field, String queryText) throws ParseException - { - return getFieldQuery(field, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); - } - - /** - * @param field - * @param first - * @param last - * @param slop - * @param inOrder - * @return the query - * @throws ParseException - */ - public Query getSpanQuery(String field, String first, String last, int slop, boolean inOrder) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATH); - } - else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - return createDefaultTextQuery(textField -> getSpanQuery(textField, first, last, slop, inOrder)); - } else if (field.equals(FIELD_CLASS)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_CLASS); - } else if (field.equals(FIELD_TYPE)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_TYPE); - } else if (field.equals(FIELD_EXACTTYPE)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTTYPE); - } else if (field.equals(FIELD_ASPECT)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_ASPECT); - } else if (field.equals(FIELD_EXACTASPECT)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTASPECT); - } else if (isPropertyField(field)) - { - return spanQueryBuilder(field, first, last, slop, inOrder); - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); - query.add(part, Occur.SHOULD); - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getSpanQuery(fieldName, first, last, slop, inOrder); - query.add(part, Occur.SHOULD); - } - return query.build(); - } - - } else if (field.equals(FIELD_ISUNSET)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISUNSET); - } else if (field.equals(FIELD_ISNULL)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNULL); - } else if (field.equals(FIELD_ISNOTNULL)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNOTNULL); - } else if (field.equals(FIELD_EXISTS)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXISTS); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); - query.add(part, Occur.SHOULD); - } - return query.build(); - } else if (field.equals(FIELD_FTSSTATUS)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_FTSSTATUS); - } else if (field.equals(FIELD_TAG)) - { - return null; - } else if (isPropertyField(field)) - { - return spanQueryBuilder(field, first, last, slop, inOrder); - } else - { - BytesRef firstBytes = analyzeMultitermTerm(field, first); - BytesRef lastBytes = analyzeMultitermTerm(field, last); - SpanQuery firstTerm = new SpanTermQuery(new Term(field, firstBytes)); - SpanQuery lastTerm = new SpanTermQuery(new Term(field, lastBytes)); - return new SpanNearQuery(new SpanQuery[] - { firstTerm, lastTerm }, slop, inOrder); - } - } - - protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException - { - return getFieldQuery(field, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); - } - - /** - * @param field - * @param queryText - * @param analysisMode - * @param luceneFunction - * @return the query - * @throws ParseException - * @throws IOException - */ - @SuppressWarnings("deprecation") - public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - try - { - if (field.equals(FIELD_PATH)) - { - return createPathQuery(queryText, false); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - return createPathQuery(queryText, true); - } else if (field.equals(FIELD_TEXT)) - { - return createTextQuery(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_ID)) - { - return createIdQuery(queryText); - } else if (field.equals(FIELD_SOLR4_ID)) - { - return createSolr4IdQuery(queryText); - } else if (field.equals(FIELD_DBID)) - { - return createDbidQuery(queryText); - } else if (field.equals(FIELD_ACLID)) - { - return createAclIdQuery(queryText); - } else if (field.equals(FIELD_OWNER)) - { - return createOwnerQuery(queryText); - } else if (field.equals(FIELD_OWNERSET)) - { - return createOwnerSetQuery(queryText); - } else if (field.equals(FIELD_READER)) - { - return createReaderQuery(queryText); - } else if (field.equals(FIELD_READERSET)) - { - return createReaderSetQuery(queryText); - } else if (field.equals(FIELD_AUTHORITY)) - { - /* - * ACL DOCUMENTATION STARTS HERE - * This creates the query that applies the ACL filter - */ - return createAuthorityQuery(queryText); - } else if (field.equals(FIELD_AUTHORITYSET)) - { - /* - * ACL DOCUMENTATION STARTS HERE - * This creates the query that applies the ACL filter - */ - return createAuthoritySetQuery(queryText); - } else if (field.equals(FIELD_DENIED)) - { - return createDeniedQuery(queryText); - } else if (field.equals(FIELD_DENYSET)) - { - return createDenySetQuery(queryText); - } else if (field.equals(FIELD_ISROOT)) - { - return createIsRootQuery(queryText); - } else if (field.equals(FIELD_ISCONTAINER)) - { - return createIsContainerQuery(queryText); - } else if (field.equals(FIELD_ISNODE)) - { - return createIsNodeQuery(queryText); - } else if (field.equals(FIELD_TX)) - { - return createTransactionQuery(queryText); - } else if (field.equals(FIELD_INTXID)) - { - return createInTxIdQuery(queryText); - } else if (field.equals(FIELD_INACLTXID)) - { - return createInAclTxIdQuery(queryText); - } else if (field.equals(FIELD_PARENT)) - { - return createParentQuery(queryText); - } else if (field.equals(FIELD_PRIMARYPARENT)) - { - return createPrimaryParentQuery(queryText); - } else if (field.equals(FIELD_QNAME)) - { - return createQNameQuery(queryText); - } else if (field.equals(FIELD_PRIMARYASSOCQNAME)) - { - return createPrimaryAssocQNameQuery(queryText); - } else if (field.equals(FIELD_PRIMARYASSOCTYPEQNAME)) - { - return createPrimaryAssocTypeQNameQuery(queryText); - } else if (field.equals(FIELD_ASSOCTYPEQNAME)) - { - return createAssocTypeQNameQuery(queryText); - } else if (field.equals(FIELD_CLASS)) - { - ClassDefinition target = QueryParserUtils.matchClassDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (target == null) - { - return new TermQuery(new Term(FIELD_TYPE, "_unknown_")); - } - return getFieldQuery(target.isAspect() ? FIELD_ASPECT : FIELD_TYPE, queryText, analysisMode, - luceneFunction); - } else if (field.equals(FIELD_TYPE)) - { - return createTypeQuery(queryText, false); - } else if (field.equals(FIELD_EXACTTYPE)) - { - return createTypeQuery(queryText, true); - } else if (field.equals(FIELD_ASPECT)) - { - return createAspectQuery(queryText, false); - } else if (field.equals(FIELD_EXACTASPECT)) - { - return createAspectQuery(queryText, true); - } else if (isPropertyField(field)) - { - Query query = attributeQueryBuilder(field, queryText, new FieldQuery(), analysisMode, luceneFunction); - return query; - } else if (field.equals(FIELD_ALL)) - { - return createAllQuery(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_ISUNSET)) - { - return createIsUnsetQuery(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_ISNULL)) - { - return createIsNullQuery(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_ISNOTNULL)) - { - return createIsNotNull(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_EXISTS)) - { - return createExistsQuery(queryText, analysisMode, luceneFunction); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field) != null) - { - return createDataTypeDefinitionQuery(field, queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_TXID)) - { - return createTxIdQuery(queryText); - } else if (field.equals(FIELD_ACLTXID)) - { - return createAclTxIdQuery(queryText); - } else if (field.equals(FIELD_TXCOMMITTIME)) - { - return createTxCommitTimeQuery(queryText); - } else if (field.equals(FIELD_ACLTXCOMMITTIME)) - { - return createAclTxCommitTimeQuery(queryText); - } else if (field.equals(FIELD_TAG)) - { - return createTagQuery(queryText); - } else if (field.equals(FIELD_SITE)) - { - return createSiteQuery(queryText); - } else if (field.equals(FIELD_PNAME)) - { - return createPNameQuery(queryText); - } else if (field.equals(FIELD_NPATH)) - { - return createNPathQuery(queryText); - } else if (field.equals(FIELD_TENANT)) - { - return createTenantQuery(queryText); - } else if (field.equals(FIELD_ANCESTOR)) - { - return createAncestorQuery(queryText); - } else if (field.equals(FIELD_FINGERPRINT)) - { - String[] parts = queryText.split("_"); - Collection values = null; - String nodeId = parts[0]; - - JSONObject json = (JSONObject)request.getContext().get(AbstractQParser.ALFRESCO_JSON); - String fingerPrint = null; - if(json != null) { - //Was the fingerprint passed in - String fingerPrintKey = "fingerprint." + nodeId; - if(json.has(fingerPrintKey)) { - fingerPrint = (String) json.get("fingerprint." + nodeId); - if (fingerPrint != null) { - List l = new ArrayList(); - String[] hashes = fingerPrint.split(" "); - for (String hash : hashes) { - l.add(hash); - } - values = l; - } - } - } else { - json = new JSONObject(); - } - - if (values == null) { - values = fetchMinHash(nodeId); - } - - String shards = this.solrParams.get("shards"); - if(values == null && shards != null) { - //we are in distributed mode - //Fetch the fingerPrint from the shards. - //The UUID and DBID will both work for method call. - values = fetchFingerPrint(shards, nodeId); - } - - //If we're in distributed mode then add the fingerprint to the json - if(values != null && shards != null && fingerPrint == null) { - ModifiableSolrParams newParams = new ModifiableSolrParams(); - newParams.add(solrParams); - solrParams = newParams; - json.put("fingerprint." + nodeId, join(values, " ")); - String jsonString = json.toString(); - newParams.add(AbstractQParser.ALFRESCO_JSON, jsonString); - request.getContext().put(AbstractQParser.ALFRESCO_JSON, json); - request.setParams(newParams); - } - - return createFingerPrintQuery(field, queryText, values, analysisMode, luceneFunction); - - } else - { - return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); - } - - } catch (SAXPathException e) - { - throw new ParseException("Failed to parse XPath...\n" + e.getMessage()); - } catch (IOException e) - { - throw new ParseException("IO: " + e.getMessage()); - } - - } - - private boolean isNumber(String s) { - for(int i=0; i 0) { - builder.append(" "); - } - builder.append(o.toString()); - } - return builder.toString(); - } - - - /** - * fetch MINHASH values from lucene index - * @param id argument of search. It is taken as DBID if is numberic, LID otherwise - * @return List of minhashes - * @throws IOException - */ - private Collection fetchMinHash(String id) throws IOException { - SolrIndexSearcher searcher = request.getSearcher(); - - - Query q; - if(isNumber(id)) - { - long dbid = Long.parseLong(id); - q = LegacyNumericRangeQuery.newLongRange("DBID", dbid, dbid + 1, true, false); - } - else - { - String query = id.startsWith("workspace") ? id : "workspace://SpacesStore/"+id; - q = new TermQuery(new Term(FIELD_LID, query)); - } - - TopDocs docs = searcher.search(q, 1); - Set fields = new HashSet(); - fields.add(FIELD_FINGERPRINT); - - if(docs.totalHits == 1) { - ScoreDoc scoreDoc = docs.scoreDocs[0]; - Document doc = searcher.doc(scoreDoc.doc, fields); - - List values = new ArrayList<>(); - - IndexableField[] minHashes = doc.getFields("MINHASH"); - for (IndexableField minHash : minHashes) - { - SchemaField sf = schema.getFieldOrNull(minHash.name()); - Object value = DocsStreamer.getValue(sf, minHash); - values.add(value); - } - - return values; - } - - return null; - } - - private Collection fetchFingerPrint(String shards, String nodeId) { - shards = shards.replace(",", "|"); - List urls = ((HttpShardHandlerFactory)shardHandlerFactory).buildURLList(shards); - ExecutorService executorService = null; - List futures = new ArrayList(); - Collection fingerPrint = null; - try { - executorService = Executors.newCachedThreadPool(); - for (String url : urls) { - futures.add(executorService.submit(new FingerPrintFetchTask(url, nodeId))); - } - - for (Future future : futures) { - Collection col = future.get(); - if(col != null) { - fingerPrint = col; - } - } - - }catch(Exception e) { - logger.error(e); - } finally { - if (executorService != null) - { - executorService.shutdown(); - } - } - return fingerPrint; - } - - - - - private class FingerPrintFetchTask implements Callable { - private String url; - private String id; - - public FingerPrintFetchTask(String url, String id) { - this.url = url; - this.id = id; - } - - public Collection call() throws Exception { - HttpSolrClient solrClient = null; - CloseableHttpClient closeableHttpClient = null; - try { - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add(FingerPrintComponent.COMPONENT_NAME, "true"); - params.add("id", id); - params.add("qt","/fingerprint"); - closeableHttpClient = HttpClientUtil.createClient(getClientParams()); - solrClient = new HttpSolrClient.Builder(url).withHttpClient(closeableHttpClient).build();; - QueryRequest request = new QueryRequest(params, SolrRequest.METHOD.POST); - QueryResponse response = request.process(solrClient); - NamedList dataResponse = response.getResponse(); - NamedList fingerprint = (NamedList) dataResponse.get("fingerprint"); - return (Collection)fingerprint.get("MINHASH"); - } finally { - if (closeableHttpClient != null) - { - closeableHttpClient.close(); - } - if (solrClient != null) - { - solrClient.close(); - } - } - } - - protected ModifiableSolrParams getClientParams() { - ModifiableSolrParams clientParams = new ModifiableSolrParams(); - clientParams.set(HttpClientUtil.PROP_SO_TIMEOUT, UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATESOTIMEOUT); - clientParams.set(HttpClientUtil.PROP_CONNECTION_TIMEOUT, UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATECONNTIMEOUT); - return clientParams; - } - } - - /** - * @param field - * @param queryText - * @param analysisMode - * - * @param luceneFunction - * @return - * @throws IOException - * @throws ParseException - */ - private Query createFingerPrintQuery(String field, String queryText, Collection values, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws IOException, ParseException - { - String[] parts = queryText.split("_"); - if (parts.length == 0) - { - return createIsNodeQuery("T"); - } - - if (values != null) - { - int bandSize = 1; - float fraction = -1; - float truePositive = 1; - if (parts.length > 1) - { - fraction = Float.parseFloat(parts[1]); - if (fraction > 1) - { - fraction /= 100; - } - } - if (parts.length > 2) - { - truePositive = Float.parseFloat(parts[2]); - if (truePositive > 1) - { - truePositive /= 100; - } - bandSize = computeBandSize(values.size(), fraction, truePositive); - } - BooleanQuery.Builder builder = new BooleanQuery.Builder(); - BooleanQuery.Builder childBuilder = new BooleanQuery.Builder(); - int rowInBand = 0; - for (Object token : values) - { - TermQuery tq = new TermQuery(new Term("MINHASH", token.toString())); - if (bandSize == 1) - { - builder.add(new ConstantScoreQuery(tq), Occur.SHOULD); - } else - { - childBuilder.add(new ConstantScoreQuery(tq), Occur.MUST); - rowInBand++; - if (rowInBand == bandSize) - { - builder.add(new ConstantScoreQuery(childBuilder.setDisableCoord(true).build()), - Occur.SHOULD); - childBuilder = new BooleanQuery.Builder(); - rowInBand = 0; - } - } - } - // Avoid a dubious narrow band .... wrap around and pad with the - // start - if (childBuilder.build().clauses().size() > 0) - { - for (Object token : values) - { - TermQuery tq = new TermQuery(new Term("MINHASH", token.toString())); - childBuilder.add(new ConstantScoreQuery(tq), Occur.MUST); - rowInBand++; - if (rowInBand == bandSize) - { - builder.add(new ConstantScoreQuery(childBuilder.setDisableCoord(true).build()), - Occur.SHOULD); - break; - } - } - } - builder.setDisableCoord(true); - if (parts.length == 2) - { - builder.setMinimumNumberShouldMatch((int) (Math.ceil(values.size() * fraction))); - } - Query q = builder.build(); - return q; - } else - { - return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); - } - } - - private int computeBandSize(int numHash, double sim, double expectedTruePositive) - { - for (int bands = 1; bands <= numHash; bands++) - { - int rowsInBand = numHash / bands; - double truePositive = 1 - Math.pow(1 - Math.pow(sim, rowsInBand), bands); - if (truePositive > expectedTruePositive) - { - return rowsInBand; - } - } - return 1; - } - - private ArrayList getTokens(IndexableField indexableField) throws IOException - { - ArrayList tokens = new ArrayList(); - - TokenStream ts = indexableField.tokenStream(schema.getIndexAnalyzer(), null); - CharTermAttribute termAttribute = ts.getAttribute(CharTermAttribute.class); - ts.reset(); - while (ts.incrementToken()) - { - String token = new String(termAttribute.buffer(), 0, termAttribute.length()); - tokens.add(token); - } - ts.end(); - ts.close(); - - return tokens; - } - - /** - * @param queryText - * @return - */ - private org.apache.lucene.search.Query createNPathQuery(String queryText) - { - return createTermQuery(FIELD_NPATH, queryText); - } - - /** - * @param queryText - * @return - */ - private Query createPNameQuery(String queryText) - { - return createTermQuery(FIELD_PNAME, queryText); - } - - /** - * @param queryText - * @return - */ - private Query createSiteQuery(String queryText) - { - if (queryText.equals("_EVERYTHING_")) - { - return createTermQuery(FIELD_ISNODE, "T"); - } - else if (queryText.equals("_ALL_SITES_")) - { - BooleanQuery.Builder invertedRepositoryQuery = new BooleanQuery.Builder(); - invertedRepositoryQuery.add(createTermQuery(FIELD_ISNODE, "T"), Occur.MUST); - invertedRepositoryQuery.add(createTermQuery(FIELD_SITE, "_REPOSITORY_"), Occur.MUST_NOT); - return invertedRepositoryQuery.build(); - } - else - { - return createTermQuery(FIELD_SITE, queryText); - } - } - - private boolean isPropertyField(String field) - { - if (field.startsWith(PROPERTY_FIELD_PREFIX)) - { - return true; - } - int index = field.lastIndexOf('@'); - if (index > -1) - { - PropertyDefinition pDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field.substring(index + 1)); - if (pDef != null) - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance() - .getIndexedFieldNamesForProperty(pDef.getName()); - for (FieldInstance instance : indexedField.getFields()) - { - if (instance.getField().equals(field)) - { - return true; - } - } - return false; - } else - { - return false; - } - } else - { - return false; - } - - } - - protected Query createTenantQuery(String queryText) throws ParseException - { - - if (queryText.length() > 0) - { - - return getFieldQueryImplWithIOExceptionWrapped(FIELD_TENANT, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - - } - else - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_TENANT, "_DEFAULT_", AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - } - - protected Query createAncestorQuery(String queryText) throws ParseException - { - return createNodeRefQuery(FIELD_ANCESTOR, queryText); - } - - /** - * @param tag - * (which will then be ISO9075 encoded) - * @return - * @throws ParseException - */ - protected Query createTagQuery(String tag) throws ParseException - { - return createTermQuery(FIELD_TAG, tag.toLowerCase()); - } - - private Query getFieldQueryImplWithIOExceptionWrapped(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException - { - try - { - return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); - } - catch (IOException e) - { - throw new ParseException("IO: " + e.getMessage()); - } - } - - /** - * @param queryText - * @return - */ - protected Query createDbidQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_DBID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createTxIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_TXID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createAclTxIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLTXID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createTxCommitTimeQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_TXCOMMITTIME, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createAclTxCommitTimeQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLTXCOMMITTIME, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createDataTypeDefinitionQuery(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc if - // required - Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), queryText, analysisMode, - luceneFunction); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } - - protected Query createIsNotNull(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (pd != null) - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - query.add(createTermQuery(FIELD_PROPERTIES, pd.getName().toString()), Occur.MUST); - query.add(createTermQuery(FIELD_NULLPROPERTIES, pd.getName().toString()), Occur.MUST_NOT); - ; - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - - Query presenceQuery = getWildcardQuery(queryText, "*"); - if (presenceQuery != null) - { - query.add(presenceQuery, Occur.MUST); - } - return query.build(); - } - } - - protected Query createIsNullQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (pd != null) - { - return createTermQuery(FIELD_NULLPROPERTIES, pd.getName().toString()); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - Query presenceQuery = getWildcardQuery(queryText, "*"); - if (presenceQuery != null) - { - query.add(createIsNodeQuery("T"), Occur.MUST); - query.add(presenceQuery, Occur.MUST_NOT); - } - return query.build(); - } - } - - protected Query createIsUnsetQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (pd != null) - { - ClassDefinition containerClass = pd.getContainerClass(); - QName container = containerClass.getName(); - String classType = containerClass.isAspect() ? FIELD_ASPECT : FIELD_TYPE; - Query typeQuery = getFieldQuery(classType, container.toString(), analysisMode, luceneFunction); - - BooleanQuery.Builder query = new BooleanQuery.Builder(); - Query presenceQuery = createTermQuery(FIELD_PROPERTIES, pd.getName().toString()); - if (presenceQuery != null) - { - query.add(typeQuery, Occur.MUST); - query.add(presenceQuery, Occur.MUST_NOT); - } - - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - - Query presenceQuery = getWildcardQuery(queryText, "*"); - if (presenceQuery != null) - { - query.add(presenceQuery, Occur.MUST_NOT); - } - return query.build(); - } - } - - protected Query createExistsQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (pd != null) - { - return createTermQuery(FIELD_PROPERTIES, pd.getName().toString()); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - - Query presenceQuery = getWildcardQuery(queryText, "*"); - if (presenceQuery != null) - { - query.add(createIsNodeQuery("T"), Occur.MUST); - query.add(presenceQuery, Occur.MUST_NOT); - } - return query.build(); - } - } - - protected Query createAllQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - // Set all = searchParameters.getAllAttributes(); - // if ((all == null) || (all.size() == 0)) - // { - // Collection contentAttributes = - // dictionaryService.getAllProperties(null); - // BooleanQuery query = new BooleanQuery(); - // for (QName qname : contentAttributes) - // { - // // The super implementation will create phrase queries etc if - // required - // Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), - // queryText, analysisMode, luceneFunction); - // if (part != null) - // { - // query.add(part, Occur.SHOULD); - // } - // else - // { - // query.add(createNoMatchQuery(), Occur.SHOULD); - // } - // } - // return query; - // } - // else - // { - // BooleanQuery query = new BooleanQuery(); - // for (String fieldName : all) - // { - // Query part = getFieldQuery(fieldName, queryText, analysisMode, - // luceneFunction); - // if (part != null) - // { - // query.add(part, Occur.SHOULD); - // } - // else - // { - // query.add(createNoMatchQuery(), Occur.SHOULD); - // } - // } - // return query; - // } - throw new UnsupportedOperationException(); - } - - protected Query createAspectQuery(String queryText, boolean exactOnly) - { - AspectDefinition target = QueryParserUtils.matchAspectDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (target == null) - { - return new TermQuery(new Term(FIELD_ASPECT, "_unknown_")); - } - - if (exactOnly) - { - QName targetQName = target.getName(); - TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, targetQName.toString())); - - return termQuery; - } - else - { - Collection subclasses = dictionaryService.getSubAspects(target.getName(), true); - - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - for (QName qname : subclasses) - { - AspectDefinition current = dictionaryService.getAspect(qname); - if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) - { - TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, qname.toString())); - if (termQuery != null) - { - booleanQuery.add(termQuery, Occur.SHOULD); - } - } - } - return booleanQuery.build(); - } - - } - - protected Query createTypeQuery(String queryText, boolean exactOnly) throws ParseException - { - TypeDefinition target = QueryParserUtils.matchTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (target == null) - { - return new TermQuery(new Term(FIELD_TYPE, "_unknown_")); - } - if (exactOnly) - { - QName targetQName = target.getName(); - TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, targetQName.toString())); - return termQuery; - } - else - { - Collection subclasses = dictionaryService.getSubTypes(target.getName(), true); - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - for (QName qname : subclasses) - { - TypeDefinition current = dictionaryService.getType(qname); - if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) - { - TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, qname.toString())); - if (termQuery != null) - { - booleanQuery.add(termQuery, Occur.SHOULD); - } - } - } - return booleanQuery.build(); - } - } - - protected Query createInTxIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_INTXID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createInAclTxIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_INACLTXID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - @SuppressWarnings("deprecation") - protected Query createTransactionQuery(String queryText) - { - return createTermQuery(FIELD_TX, queryText); - } - - protected Query createIsNodeQuery(String queryText) - { - return createTermQuery(FIELD_ISNODE, queryText); - } - - protected Query createIsContainerQuery(String queryText) - { - return createTermQuery(FIELD_ISCONTAINER, queryText); - } - - protected Query createIsRootQuery(String queryText) - { - return createTermQuery(FIELD_ISROOT, queryText); - } - - protected Query createTermQuery(String field, String queryText) - { - TermQuery termQuery = new TermQuery(new Term(field, queryText)); - return termQuery; - } - - protected Query createPrimaryParentQuery(String queryText) - { - return createNodeRefQuery(FIELD_PRIMARYPARENT, queryText); - } - - protected Query createParentQuery(String queryText) - { - return createNodeRefQuery(FIELD_PARENT, queryText); - } - - protected Query createNodeRefQuery(String field, String queryText) - { - if (tenantService.isTenantUser() && (queryText.contains(StoreRef.URI_FILLER))) - { - // assume NodeRef, since it contains StorRef URI filler - queryText = tenantService.getName(new NodeRef(queryText)).toString(); - } - return createTermQuery(field, queryText); - } - - - - /** - * - * Get generic text query - * - * - */ - protected Query createDefaultTextQuery(ThrowingFunction getQuery) throws ParseException - { - - Set text = searchParameters.getTextAttributes(); - if (text == null || text.isEmpty()) - { - text = Stream.of(AlfrescoDefaultTextFields.values()) - .map(t -> PROPERTY_FIELD_PREFIX + t.getFieldName()) - .collect(Collectors.toSet()); - } - - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : text) - { - Query part; - try { - part = getQuery.apply(fieldName); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } catch (UnsupportedOperationException e) { - logger.warn(e); - } - } - return query.build(); - - } - - - protected Query createTextQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return createDefaultTextQuery(textField -> getFieldQuery(textField, queryText, analysisMode, luceneFunction)); - } - - @SuppressWarnings("unchecked") - protected Query getFieldQueryImpl(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException, IOException - { - // make sure the field exists or return a dummy query so we have no - // error ....ACE-3231 - SchemaField schemaField = schema.getFieldOrNull(field); - boolean isNumeric = false; - if (schemaField == null) - { - return new TermQuery(new Term("_dummy_", "_miss_")); - } - else - { - isNumeric = (schemaField.getType().getNumericType() != null); - if (isNumeric) - { - //Check to see if queryText is numeric or else it will fail. - try - { - Double.valueOf(queryText); - } - catch (NumberFormatException e) - { - return new TermQuery(new Term("_dummy_", "_miss_")); - } - } - } - - // Use the analyzer to get all the tokens, and then build a TermQuery, - // PhraseQuery, or noth - - // TODO: Untokenised columns with functions require special handling - - if (luceneFunction != LuceneFunction.FIELD) - { - throw new UnsupportedOperationException( - "Field queries are not supported on lucene functions (UPPER, LOWER, etc)"); - } - - // if the incoming string already has a language identifier we strip it - // iff and addit back on again - - String localePrefix = ""; - - String toTokenise = queryText; - - if (queryText.startsWith("{")) - { - int position = queryText.indexOf("}"); - if (position > 0) - { - String language = queryText.substring(0, position + 1); - Locale locale = new Locale(queryText.substring(1, position)); - String token = queryText.substring(position + 1); - boolean found = false; - for (Locale current : Locale.getAvailableLocales()) - { - if (current.toString().equalsIgnoreCase(locale.toString())) - { - found = true; - break; - } - } - if (found) - { - localePrefix = language; - toTokenise = token; - } else - { - // toTokenise = token; - } - } - } - - String testText = toTokenise; - boolean requiresMLTokenDuplication = false; - String localeString = null; - if (isPropertyField(field) && (localePrefix.length() == 0)) - { - if ((queryText.length() > 0) && (queryText.charAt(0) == '\u0000')) - { - int position = queryText.indexOf("\u0000", 1); - testText = queryText.substring(position + 1); - requiresMLTokenDuplication = true; - localeString = queryText.substring(1, position); - - } - } - - // find the positions of any escaped * and ? and ignore them - - Set wildcardPoistions = getWildcardPositions(testText); - - TokenStream source = null; - ArrayList list = new ArrayList(); - boolean severalTokensAtSamePosition = false; - PackedTokenAttributeImpl nextToken; - int positionCount = 0; - - try - { - source = getAnalyzer().tokenStream(field, new StringReader(toTokenise)); - source.reset(); - while (source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if (source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if (source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - nextToken = new PackedTokenAttributeImpl(); - nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); - nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if (typeAtt != null) - { - nextToken.setType(typeAtt.type()); - } - if (posIncAtt != null) - { - nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - - list.add(nextToken); - if (nextToken.getPositionIncrement() != 0) - positionCount += nextToken.getPositionIncrement(); - else - severalTokensAtSamePosition = true; - } - } - finally - { - try - { - if (source != null) - { - source.close(); - } - } catch (IOException e) - { - // ignore - } - } - - // add any alpha numeric wildcards that have been missed - // Fixes most stop word and wild card issues - - for (int index = 0; index < testText.length(); index++) - { - char current = testText.charAt(index); - if (((current == '*') || (current == '?')) && wildcardPoistions.contains(index)) - { - StringBuilder pre = new StringBuilder(10); - if (index == 0) - { - // "*" and "?" at the start - - boolean found = false; - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl test = list.get(j); - if ((test.startOffset() <= 0) && (0 < test.endOffset())) - { - found = true; - break; - } - } - if (!found && (list.size() == 0)) - { - // Add new token followed by * not given by the - // tokeniser - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append("", 0, 0); - newToken.setType("ALPHANUM"); - if (requiresMLTokenDuplication) - { - Locale locale = I18NUtil.parseLocale(localeString); - @SuppressWarnings("resource") - MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, MLAnalysisMode.EXACT_LANGUAGE); - Iterator it = duplicator.buildIterator(newToken); - if (it != null) - { - int count = 0; - while (it.hasNext()) - { - list.add(it.next()); - count++; - if (count > 1) - { - severalTokensAtSamePosition = true; - } - } - } - } - // content - else - { - list.add(newToken); - } - } - } - else if (index > 0) - { - // Add * and ? back into any tokens from which it has been - // removed - - boolean tokenFound = false; - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl test = list.get(j); - if ((test.startOffset() <= index) && (index < test.endOffset())) - { - if (requiresMLTokenDuplication) - { - String termText = test.toString(); - int position = termText.indexOf("}"); - String language = termText.substring(0, position + 1); - String token = termText.substring(position + 1); - if (index >= test.startOffset() + token.length()) - { - test.setEmpty(); - test.append(language + token + current); - } - } - else - { - if (index >= test.startOffset() + test.length()) - { - test.setEmpty(); - test.append(test.toString() + current); - } - } - tokenFound = true; - break; - } - } - - if (!tokenFound) - { - for (int i = index - 1; i >= 0; i--) - { - char c = testText.charAt(i); - if (Character.isLetterOrDigit(c)) - { - boolean found = false; - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl test = list.get(j); - if ((test.startOffset() <= i) && (i < test.endOffset())) - { - found = true; - break; - } - } - if (found) - { - break; - } - else - { - pre.insert(0, c); - } - } - else - { - break; - } - } - if (pre.length() > 0) - { - // Add new token followed by * not given by the - // tokeniser - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(pre.toString()); - newToken.setOffset(index - pre.length(), index); - newToken.setType("ALPHANUM"); - if (requiresMLTokenDuplication) - { - Locale locale = I18NUtil.parseLocale(localeString); - @SuppressWarnings("resource") - MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, - MLAnalysisMode.EXACT_LANGUAGE); - Iterator it = duplicator.buildIterator(newToken); - if (it != null) - { - int count = 0; - while (it.hasNext()) - { - list.add(it.next()); - count++; - if (count > 1) - { - severalTokensAtSamePosition = true; - } - } - } - } - // content - else - { - list.add(newToken); - } - } - } - } - - StringBuilder post = new StringBuilder(10); - if (index > 0) - { - for (int i = index + 1; i < testText.length(); i++) - { - char c = testText.charAt(i); - if (Character.isLetterOrDigit(c)) - { - boolean found = false; - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl test = list.get(j); - if ((test.startOffset() <= i) && (i < test.endOffset())) - { - found = true; - break; - } - } - if (found) - { - break; - } - else - { - post.append(c); - } - } - else - { - break; - } - } - if (post.length() > 0) - { - // Add new token followed by * not given by the - // tokeniser - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(post.toString()); - newToken.setOffset(index + 1, index + 1 + post.length()); - newToken.setType("ALPHANUM"); - if (requiresMLTokenDuplication) - { - Locale locale = I18NUtil.parseLocale(localeString); - @SuppressWarnings("resource") - MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, MLAnalysisMode.EXACT_LANGUAGE); - Iterator it = duplicator.buildIterator(newToken); - if (it != null) - { - int count = 0; - while (it.hasNext()) - { - list.add(it.next()); - count++; - if (count > 1) - { - severalTokensAtSamePosition = true; - } - } - } - } - // content - else - { - list.add(newToken); - } - } - } - - } - } - - // Put in real position increments as we treat them correctly - - int curentIncrement = -1; - for (PackedTokenAttributeImpl c : list) - { - if (curentIncrement == -1) - { - curentIncrement = c.getPositionIncrement(); - } else if (c.getPositionIncrement() > 0) - { - curentIncrement = c.getPositionIncrement(); - } else - { - c.setPositionIncrement(curentIncrement); - } - } - - // Fix up position increments for in phrase isolated wildcards - - boolean lastWasWild = false; - for(int i = 0; i < list.size() -1; i++) - { - for(int j = list.get(i).endOffset() + 1; j < list.get(i + 1).startOffset() - 1; j++) - { - if(wildcardPoistions.contains(j)) - { - if(!lastWasWild) - { - list.get(i+1).setPositionIncrement(list.get(i+1).getPositionIncrement() + 1); - } - lastWasWild = true; - } - else - { - lastWasWild = false; - } - } - } - - Collections.sort(list, new Comparator() - { - - public int compare(PackedTokenAttributeImpl o1, PackedTokenAttributeImpl o2) - { - int dif = o1.startOffset() - o2.startOffset(); - return dif; - - } - }); - - // Combined * and ? based strings - should redo the tokeniser - - // Build tokens by position - - LinkedList> tokensByPosition = new LinkedList>(); - LinkedList currentList = null; - int lastStart = 0; - for (PackedTokenAttributeImpl c : list) - { - if (c.startOffset() == lastStart) - { - if (currentList == null) - { - currentList = new LinkedList(); - tokensByPosition.add(currentList); - } - currentList.add(c); - } else - { - currentList = new LinkedList(); - tokensByPosition.add(currentList); - currentList.add(c); - } - lastStart = c.startOffset(); - } - - // Build all the token sequences and see which ones get strung together - - OrderedHashSet> allTokenSequencesSet = new OrderedHashSet>(); - for (LinkedList tokensAtPosition : tokensByPosition) - { - OrderedHashSet> positionalSynonymSequencesSet = new OrderedHashSet>(); - - OrderedHashSet> newAllTokenSequencesSet = new OrderedHashSet>(); - - FOR_FIRST_TOKEN_AT_POSITION_ONLY: for (PackedTokenAttributeImpl t : tokensAtPosition) - { - PackedTokenAttributeImpl replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(t); - replace.setOffset(t.startOffset(), t.endOffset()); - replace.setType(t.type()); - replace.setPositionIncrement(t.getPositionIncrement()); - - boolean tokenFoundSequence = false; - for (LinkedList tokenSequence : allTokenSequencesSet) - { - LinkedList newEntry = new LinkedList(); - newEntry.addAll(tokenSequence); - if ((newEntry.getLast().endOffset() == replace.endOffset()) - && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) - { - if ((newEntry.getLast().startOffset() == replace.startOffset()) - && newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM)) - { - positionalSynonymSequencesSet.add(tokenSequence); - newEntry.add(replace); - tokenFoundSequence = true; - } - else if (newEntry.getLast().type().equals(CommonGramsFilter.GRAM_TYPE)) - { - if (newEntry.toString().endsWith(replace.toString())) - { - // already in the gram - positionalSynonymSequencesSet.add(tokenSequence); - tokenFoundSequence = true; - } - else - { - // need to replace the synonym in the current - // gram - tokenFoundSequence = true; - StringBuffer old = new StringBuffer(newEntry.getLast().toString()); - old.replace(replace.startOffset() - newEntry.getLast().startOffset(), - replace.endOffset() - newEntry.getLast().startOffset(), replace.toString()); - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(old.toString()); - newToken.setOffset(newEntry.getLast().startOffset(), newEntry.getLast().endOffset()); - newEntry.removeLast(); - newEntry.add(newToken); - } - } - } - else if ((newEntry.getLast().startOffset() < replace.startOffset()) - && (newEntry.getLast().endOffset() < replace.endOffset())) - { - if (newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM) - && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) - { - positionalSynonymSequencesSet.add(tokenSequence); - } - newEntry.add(replace); - tokenFoundSequence = true; - } - newAllTokenSequencesSet.add(newEntry); - } - if (false == tokenFoundSequence) - { - for (LinkedList tokenSequence : newAllTokenSequencesSet) - { - LinkedList newEntry = new LinkedList(); - newEntry.addAll(tokenSequence); - if ((newEntry.getLast().endOffset() == replace.endOffset()) - && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) - { - if ((newEntry.getLast().startOffset() == replace.startOffset()) - && newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM)) - { - positionalSynonymSequencesSet.add(tokenSequence); - newEntry.add(replace); - tokenFoundSequence = true; - } else if (newEntry.getLast().type().equals(CommonGramsFilter.GRAM_TYPE)) - { - if (newEntry.toString().endsWith(replace.toString())) - { - // already in the gram - positionalSynonymSequencesSet.add(tokenSequence); - tokenFoundSequence = true; - } else - { - // need to replace the synonym in the - // current gram - tokenFoundSequence = true; - StringBuffer old = new StringBuffer(newEntry.getLast().toString()); - old.replace(replace.startOffset() - newEntry.getLast().startOffset(), - replace.endOffset() - newEntry.getLast().startOffset(), replace.toString()); - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(old.toString()); - newToken.setOffset(newEntry.getLast().startOffset(), - newEntry.getLast().endOffset()); - newEntry.removeLast(); - newEntry.add(newToken); - positionalSynonymSequencesSet.add(newEntry); - } - } - } else if ((newEntry.getLast().startOffset() < replace.startOffset()) - && (newEntry.getLast().endOffset() < replace.endOffset())) - { - if (newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM) - && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) - { - positionalSynonymSequencesSet.add(tokenSequence); - newEntry.add(replace); - tokenFoundSequence = true; - } - } - } - } - if (false == tokenFoundSequence) - { - LinkedList newEntry = new LinkedList(); - newEntry.add(replace); - newAllTokenSequencesSet.add(newEntry); - } - // Limit the max number of permutations we consider - if (newAllTokenSequencesSet.size() > 64) - { - break FOR_FIRST_TOKEN_AT_POSITION_ONLY; - } - } - allTokenSequencesSet = newAllTokenSequencesSet; - allTokenSequencesSet.addAll(positionalSynonymSequencesSet); - - } - - LinkedList> allTokenSequences = new LinkedList>( - allTokenSequencesSet); - - // build the unique - - LinkedList> fixedTokenSequences = new LinkedList>(); - for (LinkedList tokenSequence : allTokenSequences) - { - LinkedList fixedTokenSequence = new LinkedList(); - fixedTokenSequences.add(fixedTokenSequence); - PackedTokenAttributeImpl replace = null; - for (PackedTokenAttributeImpl c : tokenSequence) - { - if (replace == null) - { - StringBuilder prefix = new StringBuilder(); - for (int i = c.startOffset() - 1; i >= 0; i--) - { - char test = testText.charAt(i); - if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) - { - prefix.insert(0, test); - } else - { - break; - } - } - String pre = prefix.toString(); - if (requiresMLTokenDuplication) - { - String termText = c.toString(); - int position = termText.indexOf("}"); - String language = termText.substring(0, position + 1); - String token = termText.substring(position + 1); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(language + pre + token); - replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); - replace.setType(c.type()); - replace.setPositionIncrement(c.getPositionIncrement()); - } else - { - String termText = c.toString(); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(pre + termText); - replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); - replace.setType(c.type()); - replace.setPositionIncrement(c.getPositionIncrement()); - } - } else - { - StringBuilder prefix = new StringBuilder(); - StringBuilder postfix = new StringBuilder(); - StringBuilder builder = prefix; - for (int i = c.startOffset() - 1; i >= replace.endOffset(); i--) - { - char test = testText.charAt(i); - if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) - { - builder.insert(0, test); - } else - { - builder = postfix; - postfix.setLength(0); - } - } - String pre = prefix.toString(); - String post = postfix.toString(); - - // Does it bridge? - if ((pre.length() > 0) && (replace.endOffset() + pre.length()) == c.startOffset()) - { - String termText = c.toString(); - if (requiresMLTokenDuplication) - { - int position = termText.indexOf("}"); - @SuppressWarnings("unused") - String language = termText.substring(0, position + 1); - String token = termText.substring(position + 1); - int oldPositionIncrement = replace.getPositionIncrement(); - String replaceTermText = replace.toString(); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(replaceTermText + pre + token); - replace.setOffset(replace.startOffset(), c.endOffset()); - replace.setType(replace.type()); - replace.setPositionIncrement(oldPositionIncrement); - } else - { - int oldPositionIncrement = replace.getPositionIncrement(); - String replaceTermText = replace.toString(); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(replaceTermText + pre + termText); - replace.setOffset(replace.startOffset(), c.endOffset()); - replace.setType(replace.type()); - replace.setPositionIncrement(oldPositionIncrement); - } - } else - { - String termText = c.toString(); - if (requiresMLTokenDuplication) - { - int position = termText.indexOf("}"); - String language = termText.substring(0, position + 1); - String token = termText.substring(position + 1); - String replaceTermText = replace.toString(); - PackedTokenAttributeImpl last = new PackedTokenAttributeImpl(); - last.setEmpty().append(replaceTermText + post); - last.setOffset(replace.startOffset(), replace.endOffset() + post.length()); - last.setType(replace.type()); - last.setPositionIncrement(replace.getPositionIncrement()); - fixedTokenSequence.add(last); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(language + pre + token); - replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); - replace.setType(c.type()); - replace.setPositionIncrement(c.getPositionIncrement()); - } else - { - String replaceTermText = replace.toString(); - PackedTokenAttributeImpl last = new PackedTokenAttributeImpl(); - last.setEmpty().append(replaceTermText + post); - last.setOffset(replace.startOffset(), replace.endOffset() + post.length()); - last.setType(replace.type()); - last.setPositionIncrement(replace.getPositionIncrement()); - fixedTokenSequence.add(last); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(pre + termText); - replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); - replace.setType(c.type()); - replace.setPositionIncrement(c.getPositionIncrement()); - } - } - } - } - // finish last - if (replace != null) - { - StringBuilder postfix = new StringBuilder(); - if ((replace.endOffset() >= 0) && (replace.endOffset() < testText.length())) - { - for (int i = replace.endOffset(); i < testText.length(); i++) - { - char test = testText.charAt(i); - if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) - { - postfix.append(test); - } else - { - break; - } - } - } - String post = postfix.toString(); - int oldPositionIncrement = replace.getPositionIncrement(); - String replaceTermText = replace.toString(); - PackedTokenAttributeImpl terminal = new PackedTokenAttributeImpl(); - terminal.setEmpty().append(replaceTermText + post); - terminal.setOffset(replace.startOffset(), replace.endOffset() + post.length()); - terminal.setType(replace.type()); - terminal.setPositionIncrement(oldPositionIncrement); - fixedTokenSequence.add(terminal); - } - } - - // rebuild fixed list - - ArrayList fixed = new ArrayList(); - for (LinkedList tokenSequence : fixedTokenSequences) - { - for (PackedTokenAttributeImpl token : tokenSequence) - { - fixed.add(token); - } - } - - // reorder by start position and increment - - Collections.sort(fixed, new Comparator() - { - - public int compare(PackedTokenAttributeImpl o1, PackedTokenAttributeImpl o2) - { - int dif = o1.startOffset() - o2.startOffset(); - if (dif != 0) - { - return dif; - } else - { - return o1.getPositionIncrement() - o2.getPositionIncrement(); - } - } - }); - - // make sure we remove any tokens we have duplicated - - @SuppressWarnings("rawtypes") - OrderedHashSet unique = new OrderedHashSet(); - unique.addAll(fixed); - fixed = new ArrayList(unique); - - list = fixed; - - // add any missing locales back to the tokens - - if (localePrefix.length() > 0) - { - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl currentToken = list.get(j); - String termText = currentToken.toString(); - currentToken.setEmpty(); - currentToken.append(localePrefix + termText); - } - } - - SchemaField sf = schema.getField(field); - - boolean isShingled = false; - @SuppressWarnings("resource") - TokenizerChain tokenizerChain = (sf.getType().getQueryAnalyzer() instanceof TokenizerChain) - ? ((TokenizerChain) sf.getType().getQueryAnalyzer()) : null; - if (tokenizerChain != null) - { - for (TokenFilterFactory factory : tokenizerChain.getTokenFilterFactories()) - { - if (factory instanceof ShingleFilterFactory) - { - isShingled = true; - break; - } - } - } - @SuppressWarnings("resource") - AlfrescoAnalyzerWrapper analyzerWrapper = (sf.getType().getQueryAnalyzer() instanceof AlfrescoAnalyzerWrapper) - ? ((AlfrescoAnalyzerWrapper) sf.getType().getQueryAnalyzer()) : null; - if (analyzerWrapper != null) - { - // assume if there are no term positions it is shingled .... - isShingled = true; - } - - boolean forceConjuncion = (rerankPhase == RerankPhase.QUERY_PHASE && !isExactTermSearch(analysisMode)); - if (list.size() == 0) - { - return null; - } - else if (list.size() == 1) - { - nextToken = list.get(0); - String termText = nextToken.toString(); - if (!isNumeric && (termText.contains("*") || termText.contains("?"))) - { - return newWildcardQuery(new Term(field, termText)); - } - else - { - return newTermQuery(new Term(field, termText)); - } - } - else - { - if (severalTokensAtSamePosition) - { - if (positionCount == 1) - { - // no phrase query: - Builder q = newBooleanQuery(); - for (int i = 0; i < list.size(); i++) - { - Query currentQuery; - nextToken = list.get(i); - String termText = nextToken.toString(); - if (termText.contains("*") || termText.contains("?")) - { - currentQuery = newWildcardQuery(new Term(field, termText)); - } else - { - currentQuery = newTermQuery(new Term(field, termText)); - } - q.add(currentQuery, BooleanClause.Occur.SHOULD); - } - return q.build(); - } - else if (forceConjuncion) - { - BooleanQuery.Builder or = new BooleanQuery.Builder(); - - for (LinkedList tokenSequence : fixedTokenSequences) - { - BooleanQuery.Builder and = new BooleanQuery.Builder(); - for (int i = 0; i < tokenSequence.size(); i++) - { - nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); - String termText = nextToken.toString(); - - Term term = new Term(field, termText); - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - and.add(wildQuery, Occur.MUST); - } else - { - TermQuery termQuery = new TermQuery(term); - and.add(termQuery, Occur.MUST); - } - } - if (and.build().clauses().size() > 0) - { - or.add(and.build(), Occur.SHOULD); - } - } - return or.build(); - } - // shingle - else if (sf.omitPositions() && isShingled) - { - - ArrayList nonContained = getNonContained(list); - Query currentQuery; - - BooleanQuery.Builder weakPhrase = new BooleanQuery.Builder(); - for (PackedTokenAttributeImpl shingleToken : nonContained) - { - String termText = shingleToken.toString(); - Term term = new Term(field, termText); - - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - currentQuery = new org.apache.lucene.search.WildcardQuery(term); - } else - { - currentQuery = new TermQuery(term); - } - weakPhrase.add(currentQuery, Occur.MUST); - } - - return weakPhrase.build(); - - } - // Word delimiter factory and other odd things generate complex - // token patterns - // Smart skip token sequences with small tokens that generate - // toomany wildcards - // Fall back to the larger pattern - // e.g Site1* will not do (S ite 1*) or (Site 1*) if 1* matches - // too much (S ite1*) and (Site1*) will still be OK - // If we skip all (for just 1* in the input) this is still an - // issue. - else - { - - return generateSpanOrQuery(field, fixedTokenSequences); - - } - } - else - { - if (forceConjuncion) - { - BooleanQuery.Builder or = new BooleanQuery.Builder(); - - for (LinkedList tokenSequence : fixedTokenSequences) - { - BooleanQuery.Builder and = new BooleanQuery.Builder(); - for (int i = 0; i < tokenSequence.size(); i++) - { - nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); - String termText = nextToken.toString(); - - Term term = new Term(field, termText); - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - and.add(wildQuery, Occur.MUST); - } - else - { - TermQuery termQuery = new TermQuery(term); - and.add(termQuery, Occur.MUST); - } - } - if (and.build().clauses().size() > 0) - { - or.add(and.build(), Occur.SHOULD); - } - } - return or.build(); - } - else - { - SpanQuery spanQuery = null; - ArrayList atSamePositionSpanOrQueryParts = new ArrayList(); - int gap = 0; - for (int i = 0; i < list.size(); i++) - { - nextToken = list.get(i); - String termText = nextToken.toString(); - Term term = new Term(field, termText); - if (getEnablePositionIncrements()) - { - SpanQuery nextSpanQuery; - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( - wildQuery); - wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); - nextSpanQuery = wrapper; - } - else - { - nextSpanQuery = new SpanTermQuery(term); - } - if (gap == 0) - { - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } - else - { - if (atSamePositionSpanOrQueryParts.size() == 0) - { - if (spanQuery == null) - { - spanQuery = nextSpanQuery; - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, nextSpanQuery }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - } - else if (atSamePositionSpanOrQueryParts.size() == 1) - { - if (spanQuery == null) - { - spanQuery = atSamePositionSpanOrQueryParts.get(0); - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, - internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } - else - { - if (spanQuery == null) - { - spanQuery = new SpanOrQuery( - atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})); - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, - new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})) }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } - } - gap = nextToken.getPositionIncrement(); - } - else - { - SpanQuery nextSpanQuery; - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( - wildQuery); - wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); - nextSpanQuery = wrapper; - } - else - { - nextSpanQuery = new SpanTermQuery(term); - } - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(nextSpanQuery); - } - else - { - spanQuery = new SpanOrQuery(spanQuery, nextSpanQuery); - } - } - } - if (atSamePositionSpanOrQueryParts.size() == 0) - { - return spanQuery; - } - else if (atSamePositionSpanOrQueryParts.size() == 1) - { - if (spanQuery == null) - { - spanQuery = atSamePositionSpanOrQueryParts.get(0); - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, - internalSlop < 2); - } - return spanQuery; - } - else - { - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})); - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})) }, (gap - 1) + internalSlop, internalSlop < 2); - } - return spanQuery; - } - } - } - } - } - - /** - * @param list - * @return - */ - private ArrayList getNonContained(ArrayList list) - { - ArrayList nonContained = new ArrayList(); - NEXT_CANDIDATE: for (PackedTokenAttributeImpl candidate : list) - { - NEXT_TEST: for (PackedTokenAttributeImpl test : list) - { - if (candidate == test) - { - continue NEXT_TEST; - } else if ((test.startOffset() == candidate.startOffset()) - && (candidate.endOffset() == test.endOffset()) - && (test.toString().equals(candidate.toString()))) - { - continue NEXT_TEST; - } else if ((test.startOffset() <= candidate.startOffset()) - && (candidate.endOffset() <= test.endOffset()) - && (test.toString().contains(candidate.toString()))) - { - continue NEXT_CANDIDATE; - } - } - nonContained.add(candidate); - } - return nonContained; - } - - /** - * @param field - * @param fixedTokenSequences - * LinkedList> - * @return Query - */ - protected SpanQuery generateSpanOrQuery(String field, - LinkedList> fixedTokenSequences) - { - PackedTokenAttributeImpl nextToken; - ArrayList spanOrQueryParts = new ArrayList(); - - for (LinkedList tokenSequence : fixedTokenSequences) - { - int gap = 1; - SpanQuery spanQuery = null; - ArrayList atSamePositionSpanOrQueryParts = new ArrayList(); - - // MNT-13239: if all tokens's positions are incremented by one then - // create flat nearQuery - if (getEnablePositionIncrements() && isAllTokensSequentiallyShifted(tokenSequence)) - { - // there will be no tokens at same position - List wildWrappedList = new ArrayList(tokenSequence.size()); - for (PackedTokenAttributeImpl token : tokenSequence) - { - String termText = token.toString(); - Term term = new Term(field, termText); - SpanQuery nextSpanQuery = wrapWildcardTerms(term); - wildWrappedList.add(nextSpanQuery); - } - if (wildWrappedList.size() == 1) - { - spanQuery = wildWrappedList.get(0); - } else - { - spanQuery = new SpanNearQuery(wildWrappedList.toArray(new SpanQuery[wildWrappedList.size()]), 0, - true); - } - } else - { - for (int i = 0; i < tokenSequence.size(); i++) - { - nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); - String termText = nextToken.toString(); - - Term term = new Term(field, termText); - - if (getEnablePositionIncrements()) - { - SpanQuery nextSpanQuery = wrapWildcardTerms(term); - if (gap == 0) - { - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } else - { - if (atSamePositionSpanOrQueryParts.size() == 0) - { - if (spanQuery == null) - { - spanQuery = nextSpanQuery; - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, nextSpanQuery }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - } else if (atSamePositionSpanOrQueryParts.size() == 1) - { - if (spanQuery == null) - { - spanQuery = atSamePositionSpanOrQueryParts.get(0); - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, - internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } else - { - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})); - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, spanQuery = new SpanOrQuery( - atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})) }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } - } - gap = nextToken.getPositionIncrement(); - - } else - { - SpanQuery nextSpanQuery; - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( - wildQuery); - wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); - nextSpanQuery = wrapper; - } else - { - nextSpanQuery = new SpanTermQuery(term); - } - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(nextSpanQuery); - } else - { - spanQuery = new SpanOrQuery(spanQuery, nextSpanQuery); - } - } - } - } - - if (atSamePositionSpanOrQueryParts.size() == 0) - { - spanOrQueryParts.add(spanQuery); - } else if (atSamePositionSpanOrQueryParts.size() == 1) - { - if (spanQuery == null) - { - spanQuery = atSamePositionSpanOrQueryParts.get(0); - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - spanOrQueryParts.add(spanQuery); - } else - { - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})); - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})) }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - spanOrQueryParts.add(spanQuery); - } - } - if (spanOrQueryParts.size() == 1) - { - return spanOrQueryParts.get(0); - } else - { - return new SpanOrQuery(spanOrQueryParts.toArray(new SpanQuery[] - {})); - } - } - - private SpanQuery wrapWildcardTerms(org.apache.lucene.index.Term term) - { - String termText = term.text(); - SpanQuery nextSpanQuery; - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery(term); - SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( - wildQuery); - wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); - nextSpanQuery = wrapper; - } else - { - nextSpanQuery = new SpanTermQuery(term); - } - return nextSpanQuery; - } - - protected boolean isAllTokensSequentiallyShifted(List tokenSequence) - { - if (0 == internalSlop) - { - // check if all tokens have gap = 1 - for (PackedTokenAttributeImpl tokenToCheck : tokenSequence) - { - if (1 != tokenToCheck.getPositionIncrement()) - { - return false; - } - } - return true; - } - return false; - } - - private Set getWildcardPositions(String string) - { - HashSet wildcardPositions = new HashSet(); - - boolean lastWasEscape = false; - - for (int i = 0; i < string.length(); i++) - { - char c = string.charAt(i); - if (lastWasEscape) - { - lastWasEscape = false; - } else - { - if (c == '\\') - { - lastWasEscape = true; - } else if (c == '*') - { - wildcardPositions.add(i); - } else if (c == '?') - { - wildcardPositions.add(i); - } - } - } - - return wildcardPositions; - } - - /** - * @exception ParseException - * throw in overridden method to disallow - */ - - protected Query getRangeQuery(String field, String part1, String part2, boolean startInclusive, - boolean endInclusive) throws ParseException - { - return getRangeQuery(field, part1, part2, startInclusive, endInclusive, AnalysisMode.IDENTIFIER, - LuceneFunction.FIELD); - } - - protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException - { - return getRangeQuery(field, part1, part2, inclusive, inclusive, AnalysisMode.IDENTIFIER, LuceneFunction.FIELD); - } - - /** - * @param field - * @param part1 - * @param part2 - * @param includeLower - * @param includeUpper - * @param analysisMode - * @param luceneFunction - * @return the query - * @exception ParseException - * throw in overridden method to disallow - */ - public Query getRangeQuery(String field, String part1, String part2, boolean includeLower, boolean includeUpper, - AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATH); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - - return createDefaultTextQuery(textField -> getRangeQuery(textField, part1, part2, includeLower, - includeUpper, analysisMode, luceneFunction)); - - - } else if (field.equals(FIELD_CASCADETX)) - { - SchemaField sf = schema.getField(FIELD_CASCADETX); - if (sf != null) - { - String start = null; - try - { - analyzeMultitermTerm(FIELD_CASCADETX, part1); - start = part1; - } catch (Exception e) - { - - } - String end = null; - try - { - analyzeMultitermTerm(FIELD_CASCADETX, part2); - end = part2; - } catch (Exception e) - { - - } - return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); - } else - { - throw new UnsupportedOperationException(); - } - } - // FIELD_ID uses the default - // FIELD_DBID uses the default - // FIELD_ISROOT uses the default - // FIELD_ISCONTAINER uses the default - // FIELD_ISNODE uses the default - // FIELD_TX uses the default - // FIELD_PARENT uses the default - // FIELD_PRIMARYPARENT uses the default - // FIELD_QNAME uses the default - // FIELD_PRIMARYASSOCTYPEQNAME uses the default - // FIELD_ASSOCTYPEQNAME uses the default - // FIELD_CLASS uses the default - // FIELD_TYPE uses the default - // FIELD_EXACTTYPE uses the default - // FIELD_ASPECT uses the default - // FIELD_EXACTASPECT uses the default - // FIELD_TYPE uses the default - // FIELD_TYPE uses the default - if (isPropertyField(field)) - { - Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(field); - - String expandedFieldName = null; - PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, fieldNameAndEnding.getFirst()); - IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; - if (propertyDef != null) - { - tokenisationMode = propertyDef.getIndexTokenisationMode(); - if (tokenisationMode == null) - { - tokenisationMode = IndexTokenisationMode.TRUE; - } - } else - { - expandedFieldName = expandAttributeFieldName(field); - } - - if (propertyDef != null) - { - // LOWER AND UPPER - if (luceneFunction != LuceneFunction.FIELD) - { - if (luceneFunction == LuceneFunction.LOWER) - { - if ((false == part1.toLowerCase().equals(part1)) - || (false == part2.toLowerCase().equals(part2))) - { - return createNoMatchQuery(); - } - } - - if (luceneFunction == LuceneFunction.UPPER) - { - if ((false == part1.toUpperCase().equals(part1)) - || (false == part2.toUpperCase().equals(part2))) - { - return createNoMatchQuery(); - } - } - - if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - addLocaleSpecificUntokenisedTextRangeFunction(expandedFieldName, propertyDef, part1, part2, - includeLower, includeUpper, luceneFunction, booleanQuery, locale, tokenisationMode); - } - return booleanQuery.build(); - } else - { - throw new UnsupportedOperationException("Lucene Function"); - } - } - - if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) - { - return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, analysisMode, - expandedFieldName, propertyDef, tokenisationMode); - } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) - { - String solrField = null; - switch (fieldNameAndEnding.getSecond()) - { - case FIELD_SIZE_SUFFIX: - solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_SIZE, FieldUse.ID) - .getFields().get(0).getField(); - break; - case FIELD_MIMETYPE_SUFFIX: - solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_MIMETYPE, FieldUse.ID) - .getFields().get(0).getField(); - break; - case FIELD_ENCODING_SUFFIX: - solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_ENCODING, FieldUse.ID) - .getFields().get(0).getField(); - break; - case FIELD_LOCALE_SUFFIX: - solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_LOCALE, FieldUse.ID) - .getFields().get(0).getField(); - break; - } - if (solrField != null) - { - String start = null; - try - { - analyzeMultitermTerm(solrField, part1); - start = part1; - } catch (Exception e) - { - - } - String end = null; - try - { - analyzeMultitermTerm(solrField, part2); - end = part2; - } catch (Exception e) - { - - } - - SchemaField sf = schema.getField(solrField); - return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); - } else - { - return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, - analysisMode, expandedFieldName, propertyDef, tokenisationMode); - } - } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) - { - return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, analysisMode, - expandedFieldName, propertyDef, tokenisationMode); - } else if ((propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) - || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE)) && - !AlfrescoSolrDataModel.getInstance().isDerivedDateField(fieldNameAndEnding.getSecond())) - { - Pair dateAndResolution1 = parseDateString(part1); - Pair dateAndResolution2 = parseDateString(part2); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - IndexedField indexedField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), null, FieldUse.ID); - for (FieldInstance instance : indexedField.getFields()) - { - String start = dateAndResolution1 == null ? part1 - : (includeLower ? getDateStart(dateAndResolution1) : getDateEnd(dateAndResolution1)); - String end = dateAndResolution2 == null ? part2 - : (includeUpper ? getDateEnd(dateAndResolution2) : getDateStart(dateAndResolution2)); - if (start.equals("*")) - { - start = null; - } - if (end.equals("*")) - { - end = null; - } - - SchemaField sf = schema.getField(instance.getField()); - - Query query = sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); - if (query != null) - { - bQuery.add(query, Occur.SHOULD); - } - } - return bQuery.build(); - } - else - { - String solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), - AlfrescoSolrDataModel.getInstance().getTextField(fieldNameAndEnding.getSecond()), - FieldUse.ID) - .getFields().get(0).getField(); - - String start = null; - try - { - analyzeMultitermTerm(solrField, part1); - start = part1; - } catch (Exception e) - { - - } - String end = null; - try - { - analyzeMultitermTerm(solrField, part2); - end = part2; - } catch (Exception e) - { - - } - - SchemaField sf = schema.getField(solrField); - return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); - } - } else - { - throw new UnsupportedOperationException(); - } - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, - includeUpper, analysisMode, luceneFunction); - query.add(part, Occur.SHOULD); - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getRangeQuery(fieldName, part1, part2, includeLower, includeUpper, analysisMode, - luceneFunction); - query.add(part, Occur.SHOULD); - } - return query.build(); - } - - } - // FIELD_ISUNSET uses the default - // FIELD_ISNULL uses the default - // FIELD_ISNOTNULL uses the default - // FIELD_EXISTS uses the default - else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, - includeUpper, analysisMode, luceneFunction); - query.add(part, Occur.SHOULD); - } - return query.build(); - } - // FIELD_FTSSTATUS uses the default - if (field.equals(FIELD_TAG)) - { - throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_TAG); - } else - { - // None property - leave alone - throw new UnsupportedOperationException(); - } - } - - /** - * @param field - * @param part1 - * @param part2 - * @param includeLower - * @param includeUpper - * @param analysisMode - * @param expandedFieldName - * @param propertyDef - * @param tokenisationMode - * @return - * @throws ParseException - */ - private Query buildTextMLTextOrContentRange(String field, String part1, String part2, boolean includeLower, - boolean includeUpper, AnalysisMode analysisMode, String expandedFieldName, PropertyDefinition propertyDef, - IndexTokenisationMode tokenisationMode) throws ParseException - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - - try - { - addTextRange(field, propertyDef, part1, part2, includeLower, includeUpper, analysisMode, - expandedFieldName, propertyDef, tokenisationMode, booleanQuery, locale); - } catch (IOException e) - { - throw new ParseException( - "Failed to tokenise: <" + part1 + "> or <" + part2 + "> for " + propertyDef.getName()); - } - - } - return booleanQuery.build(); - } - - private String expandAttributeFieldName(String field) - { - return PROPERTY_FIELD_PREFIX + QueryParserUtils.expandQName(searchParameters.getNamespace(), - namespacePrefixResolver, field.substring(1)); - } - - // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) - @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") - protected String getToken(String field, String value, AnalysisMode analysisMode) throws ParseException - { - try (TokenStream source = getAnalyzer().tokenStream(field, new StringReader(value))) - { - String tokenised = null; - - while (source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if (source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if (source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - PackedTokenAttributeImpl token = new PackedTokenAttributeImpl(); - token.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); - token.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if (typeAtt != null) - { - token.setType(typeAtt.type()); - } - if (posIncAtt != null) - { - token.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - - tokenised = token.toString(); - } - return tokenised; - } catch (IOException e) - { - throw new ParseException("IO" + e.getMessage()); - } - - } - - @Override - public Query getPrefixQuery(String field, String termStr) throws ParseException - { - return getPrefixQuery(field, termStr, AnalysisMode.PREFIX); - } - - @SuppressWarnings("deprecation") - public Query getPrefixQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATH); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - - return createDefaultTextQuery(textField -> getPrefixQuery(textField, termStr, analysisMode)); - - } else if (field.equals(FIELD_ID)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getPrefixQuery(FIELD_LID, termStr); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) - || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) - || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) - || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getPrefixQuery(field, termStr); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_CLASS)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_CLASS); - } else if (field.equals(FIELD_TYPE)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_TYPE); - } else if (field.equals(FIELD_EXACTTYPE)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_EXACTTYPE); - } else if (field.equals(FIELD_ASPECT)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_ASPECT); - } else if (field.equals(FIELD_EXACTASPECT)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_EXACTASPECT); - } else if (isPropertyField(field)) - { - return attributeQueryBuilder(field, termStr, new PrefixQuery(), analysisMode, LuceneFunction.FIELD); - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc - // if required - Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getPrefixQuery(fieldName, termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } - } else if (field.equals(FIELD_ISUNSET)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISUNSET); - } else if (field.equals(FIELD_ISNULL)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNULL); - } else if (field.equals(FIELD_ISNOTNULL)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNOTNULL); - } else if (field.equals(FIELD_EXISTS)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_EXISTS); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc if - // required - Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else if (field.equals(FIELD_FTSSTATUS)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_FTSSTATUS); - } else if (field.equals(FIELD_TAG)) - { - return super.getPrefixQuery(field, termStr); - } else if (field.equals(FIELD_SITE)) - { - return super.getPrefixQuery(field, termStr); - } else if (field.equals(FIELD_NPATH)) - { - return super.getPrefixQuery(field, termStr); - } else if (field.equals(FIELD_PNAME)) - { - return super.getPrefixQuery(field, termStr); - } else - { - return super.getPrefixQuery(field, termStr); - } - } - - @Override - public Query getWildcardQuery(String field, String termStr) throws ParseException - { - return getWildcardQuery(field, termStr, AnalysisMode.WILD); - } - - @SuppressWarnings("deprecation") - public Query getWildcardQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_PATH); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - - return createDefaultTextQuery(textField -> getWildcardQuery(textField, termStr, analysisMode)); - - } else if (field.equals(FIELD_ID)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getWildcardQuery(FIELD_LID, termStr); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) - || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) - || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) - || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getWildcardQuery(field, termStr); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_CLASS)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_CLASS); - } else if (field.equals(FIELD_TYPE)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_TYPE); - } else if (field.equals(FIELD_EXACTTYPE)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_EXACTTYPE); - } else if (field.equals(FIELD_ASPECT)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_ASPECT); - } else if (field.equals(FIELD_EXACTASPECT)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_EXACTASPECT); - } else if (isPropertyField(field)) - { - return attributeQueryBuilder(field, termStr, new WildcardQuery(), analysisMode, LuceneFunction.FIELD); - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc - // if required - Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getWildcardQuery(fieldName, termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } - } else if (field.equals(FIELD_ISUNSET)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISUNSET); - } else if (field.equals(FIELD_ISNULL)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNULL); - } else if (field.equals(FIELD_ISNOTNULL)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNOTNULL); - } else if (field.equals(FIELD_EXISTS)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_EXISTS); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc if - // required - Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else if (field.equals(FIELD_FTSSTATUS)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_FTSSTATUS); - } else if (field.equals(FIELD_TAG)) - { - return super.getWildcardQuery(field, termStr); - } else if (field.equals(FIELD_SITE)) - { - return super.getWildcardQuery(field, termStr); - } else if (field.equals(FIELD_PNAME)) - { - return super.getWildcardQuery(field, termStr); - } else if (field.equals(FIELD_NPATH)) - { - return super.getWildcardQuery(field, termStr); - } else - { - return super.getWildcardQuery(field, termStr); - } - } - - @SuppressWarnings("deprecation") - @Override - public Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATH); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - - return createDefaultTextQuery(textField -> getFuzzyQuery(textField, termStr, minSimilarity)); - - } else if (field.equals(FIELD_ID) || field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) - || field.equals(FIELD_ISCONTAINER) || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) - || field.equals(FIELD_PARENT) || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) - || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getFuzzyQuery(field, termStr, minSimilarity); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_CLASS)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_CLASS); - } else if (field.equals(FIELD_TYPE)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_TYPE); - } else if (field.equals(FIELD_EXACTTYPE)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTTYPE); - } else if (field.equals(FIELD_ASPECT)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ASPECT); - } else if (field.equals(FIELD_EXACTASPECT)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTASPECT); - } else if (isPropertyField(field)) - { - return attributeQueryBuilder(field, termStr, new FuzzyQuery(minSimilarity), AnalysisMode.FUZZY, - LuceneFunction.FIELD); - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc - // if required - Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getFuzzyQuery(fieldName, termStr, minSimilarity); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } - } else if (field.equals(FIELD_ISUNSET)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISUNSET); - } else if (field.equals(FIELD_ISNULL)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNULL); - } else if (field.equals(FIELD_ISNOTNULL)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNOTNULL); - } else if (field.equals(FIELD_EXISTS)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXISTS); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc if - // required - Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else if (field.equals(FIELD_FTSSTATUS)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_FTSSTATUS); - } else if (field.equals(FIELD_TAG)) - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } else if (field.equals(FIELD_SITE)) - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } else if (field.equals(FIELD_PNAME)) - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } else if (field.equals(FIELD_NPATH)) - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } else - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } - } - - /** - * @param dictionaryService - */ - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - /** - * @param field - * @param queryText - * @param analysisMode - * @param luceneFunction - * @return the query - * @throws ParseException - */ - public Query getSuperFieldQuery(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(field, queryText, analysisMode, luceneFunction); - } - - /** - * @param field - * @param termStr - * @param minSimilarity - * @return the query - * @throws ParseException - */ - public Query getSuperFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } - - /** - * @param field - * @param termStr - * @return the query - * @throws ParseException - */ - public Query getSuperPrefixQuery(String field, String termStr) throws ParseException - { - return super.getPrefixQuery(field, termStr); - } - - /** - * @param field - * @param termStr - * @return the query - * @throws ParseException - */ - public Query getSuperWildcardQuery(String field, String termStr) throws ParseException - { - return super.getWildcardQuery(field, termStr); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.lucene.queryParser.QueryParser#newWildcardQuery(org.apache. - * lucene.index.Term) - */ - @Override - protected Query newWildcardQuery(Term t) - { - if (t.text().equals("*")) - { - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - bQuery.add(createTermQuery(FIELD_FIELDS, t.field()), Occur.SHOULD); - bQuery.add(createTermQuery(FIELD_PROPERTIES, t.field()), Occur.SHOULD); - return bQuery.build(); - } else if (t.text().contains("\\")) - { - String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, - SearchLanguageConversion.DEF_REGEX, t.text()); - return new RegexpQuery(new Term(t.field(), regexp)); - } else - { - org.apache.lucene.search.WildcardQuery query = new org.apache.lucene.search.WildcardQuery(t); - query.setRewriteMethod(new MultiTermQuery.TopTermsScoringBooleanQueryRewrite(topTermSpanRewriteLimit)); - return query; - } - } - - /* - * (non-Javadoc) - * - * @see org.apache.lucene.queryParser.QueryParser#newPrefixQuery(org.apache. - * lucene.index.Term) - */ - @Override - protected Query newPrefixQuery(Term prefix) - { - if (prefix.text().contains("\\")) - { - String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, - SearchLanguageConversion.DEF_REGEX, prefix.text()); - return new RegexpQuery(new Term(prefix.field(), regexp)); - } else - { - return super.newPrefixQuery(prefix); - } - - } - - public interface SubQuery - { - /** - * @param field - * @param queryText - * @param analysisMode - * @param luceneFunction - * @return the query - * @throws ParseException - */ - Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException; - } - - class FieldQuery implements SubQuery - { - public Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return getSuperFieldQuery(field, queryText, analysisMode, luceneFunction); - } - } - - class FuzzyQuery implements SubQuery - { - float minSimilarity; - - FuzzyQuery(float minSimilarity) - { - this.minSimilarity = minSimilarity; - } - - public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return getSuperFuzzyQuery(field, translateLocale(termStr), minSimilarity); - } - } - - private String translateLocale(String localised) - { - if (localised.startsWith("\u0000")) - { - if (localised.startsWith("\u0000\u0000")) - { - if (localised.length() < 3) - { - return ""; - } else - { - return localised.substring(2); - } - } else - { - int end = localised.indexOf('\u0000', 1); - if (end == -1) - { - return localised; - } else - { - StringBuilder buffer = new StringBuilder(); - buffer.append("{"); - buffer.append(localised.substring(1, end)); - buffer.append("}"); - buffer.append(localised.substring(end + 1)); - return buffer.toString(); - } - } - } else - { - return localised; - } - } - - class PrefixQuery implements SubQuery - { - public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - StringBuilder builder = new StringBuilder(termStr.length() + 1); - builder.append(termStr); - builder.append("*"); - return getSuperFieldQuery(field, builder.toString(), analysisMode, luceneFunction); - // return getSuperPrefixQuery(field, termStr); - } - } - - class WildcardQuery implements SubQuery - { - public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return getSuperFieldQuery(field, termStr, analysisMode, luceneFunction); - // return getSuperWildcardQuery(field, termStr); - } - } - - private Query spanQueryBuilder(String field, String first, String last, int slop, boolean inOrder) - throws ParseException - { - String propertyFieldName = field.substring(1); - String expandedFieldName = null; - - PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, propertyFieldName); - IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; - if (propertyDef != null) - { - tokenisationMode = propertyDef.getIndexTokenisationMode(); - if (tokenisationMode == null) - { - tokenisationMode = IndexTokenisationMode.TRUE; - } - } else - { - expandedFieldName = expandAttributeFieldName(field); - } - - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) - { - // Build a sub query for each locale and or the results together - - // the analysis will take care of - // cross language matching for each entry - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - addMLTextSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, propertyDef, - tokenisationMode, booleanQuery, locale); - } - return booleanQuery.build(); - } - // Content - else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales - .addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); - } - - return addContentSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, - expandedLocales); - - } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - - addTextSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, - booleanQuery, locale); - - } - return booleanQuery.build(); - } else - { - throw new UnsupportedOperationException( - "Span queries are only supported for d:text, d:mltext and d:content data types"); - } - } - - private Query attributeQueryBuilder(String field, String queryText, SubQuery subQueryBuilder, - AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException - { - // TODO: Fix duplicate token generation for mltext, content and text. - // -locale expansion here and in tokeisation -> duplicates - - // Get type info etc - - // TODO: additional suffixes - - Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(field); - - String expandedFieldName = null; - QName propertyQName; - PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, fieldNameAndEnding.getFirst()); - IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; - if (propertyDef != null) - { - tokenisationMode = propertyDef.getIndexTokenisationMode(); - if (tokenisationMode == null) - { - tokenisationMode = IndexTokenisationMode.TRUE; - } - propertyQName = propertyDef.getName(); - } else - { - expandedFieldName = expandAttributeFieldName(field); - propertyQName = QName.createQName(fieldNameAndEnding.getFirst()); - } - - if (isAllStar(queryText)) - { - return createTermQuery(FIELD_PROPERTIES, propertyQName.toString()); - } - - if (luceneFunction != LuceneFunction.FIELD) - { - if ((tokenisationMode == IndexTokenisationMode.FALSE) || (tokenisationMode == IndexTokenisationMode.BOTH)) - { - if (luceneFunction == LuceneFunction.LOWER) - { - if (false == queryText.toLowerCase().equals(queryText)) - { - return createNoMatchQuery(); - } - } - if (luceneFunction == LuceneFunction.UPPER) - { - if (false == queryText.toUpperCase().equals(queryText)) - { - return createNoMatchQuery(); - } - } - - return functionQueryBuilder(expandedFieldName, fieldNameAndEnding.getSecond(), propertyQName, - propertyDef, tokenisationMode, queryText, luceneFunction); - } - } - - // Mime type - if (fieldNameAndEnding.getSecond().equals(FIELD_MIMETYPE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_MIMETYPE, FieldUse.ID).getFields().get(0) - .getField(), queryText, analysisMode, luceneFunction); - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_SIZE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_SIZE, FieldUse.ID).getFields().get(0) - .getField(), queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_LOCALE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_LOCALE, FieldUse.ID).getFields().get(0) - .getField(), queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_ENCODING_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_ENCODING, FieldUse.ID).getFields().get(0) - .getField(), queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_STATUS_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder - .getQuery( - AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_STATUS, - FieldUse.ID) - .getFields().get(0).getField(), - queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_TIME_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder - .getQuery( - AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_TIME, - FieldUse.ID) - .getFields().get(0).getField(), - queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery( - AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_EXCEPTION, - FieldUse.ID) - .getFields().get(0).getField(), - queryText, analysisMode, luceneFunction); - - } - - } - else if (AlfrescoSolrDataModel.getInstance().isDerivedDateField(fieldNameAndEnding.getSecond())) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE) - || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME ))) - { - return subQueryBuilder.getQuery( - AlfrescoSolrDataModel.getInstance() - .getDateDerivedField(propertyQName, fieldNameAndEnding.getSecond()), - queryText, analysisMode, luceneFunction); - } - } - - // Already in expanded form - - // ML - - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) - { - // Build a sub query for each locale and or the results together - - // the analysis will take care of - // cross language matching for each entry - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - addMLTextAttributeQuery(field, propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, propertyDef, tokenisationMode, booleanQuery, locale); - } - return getNonEmptyBooleanQuery(booleanQuery.build()); - } - // Content - else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - // Identifier request are ignored for content - - // Build a sub query for each locale and or the results together - - // - add an explicit condition for the locale - - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales - .addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); - } - - return addContentAttributeQuery(propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, expandedLocales); - - } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) - { - // if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) || - // propertyQName.equals(ContentModel.PROP_USERNAME) || - // propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) - // { - // // nasty work around for solr support for user and group look up - // as we can not support lowercased identifiers in the model - // if(isLucene()) - // { - // return subQueryBuilder.getQuery(expandedFieldName, queryText, - // analysisMode, luceneFunction); - // } - // } - - boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) - || propertyQName.equals(ContentModel.PROP_USERNAME) - || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); - - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - Locale fixedLocale = locale; - if (fixedLocale.getLanguage().equals("*")) - { - fixedLocale = new Locale("??"); - } - addTextAttributeQuery(field, propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, tokenisationMode, booleanQuery, fixedLocale); - - } - return getNonEmptyBooleanQuery(booleanQuery.build()); - } else - { - // Date does not support like - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME))) - { - if (analysisMode == AnalysisMode.LIKE) - { - throw new UnsupportedOperationException("Wild cards are not supported for the datetime type"); - } - } - - // expand date for loose date parsing - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) - || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE))) - { - Pair dateAndResolution = parseDateString(queryText); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - IndexedField indexedField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), null, FieldUse.FTS); - for (FieldInstance instance : indexedField.getFields()) - { - if (dateAndResolution != null) - { - Query query = newRangeQuery(instance.getField(), getDateStart(dateAndResolution), - getDateEnd(dateAndResolution), true, true); - if (query != null) - { - bQuery.add(query, Occur.SHOULD); - } - } else - { - Query query = subQueryBuilder.getQuery(instance.getField(), queryText, AnalysisMode.DEFAULT, - luceneFunction); - if (query != null) - { - bQuery.add(query, Occur.SHOULD); - } - } - } - if (bQuery.build().clauses().size() > 0) - { - return bQuery.build(); - } else - { - return createNoMatchQuery(); - } - } - - if ((propertyDef != null) && (tenantService.isTenantUser()) - && (propertyDef.getDataType().getName().equals(DataTypeDefinition.NODE_REF)) - && (queryText.contains(StoreRef.URI_FILLER))) - { - // ALF-6202 - queryText = tenantService.getName(new NodeRef(queryText)).toString(); - } - - // Sort and id is only special for MLText, text, and content - // Dates are not special in this case - if (propertyDef != null) - { - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - IndexedField indexedField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), null, FieldUse.FTS); - for (FieldInstance instance : indexedField.getFields()) - { - Query query = subQueryBuilder.getQuery(instance.getField(), queryText, AnalysisMode.DEFAULT, - luceneFunction); - if (query != null) - { - bQuery.add(query, Occur.SHOULD); - } - } - if (bQuery.build().clauses().size() > 0) - { - return bQuery.build(); - } else - { - return createNoMatchQuery(); - } - } else - { - Query query = subQueryBuilder.getQuery(expandedFieldName, queryText, AnalysisMode.DEFAULT, - luceneFunction); - if (query != null) - { - return query; - } else - { - return createNoMatchQuery(); - } - } - } - } - - /** - * @param queryText - * @return - */ - private boolean isAllStar(String queryText) - { - if ((queryText == null) || (queryText.length() == 0)) - { - return false; - } - for (char c : queryText.toCharArray()) - { - if (c != '*') - { - return false; - } - } - return true; - - } - - /** - * @param dateAndResolution - * @return - */ - private String getDateEnd(Pair dateAndResolution) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.setTime(dateAndResolution.getFirst()); - switch (dateAndResolution.getSecond()) - { - case Calendar.YEAR: - cal.set(Calendar.MONTH, cal.getActualMaximum(Calendar.MONTH)); - case Calendar.MONTH: - cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); - case Calendar.DAY_OF_MONTH: - cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY)); - case Calendar.HOUR_OF_DAY: - cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE)); - case Calendar.MINUTE: - cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND)); - case Calendar.SECOND: - cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND)); - case Calendar.MILLISECOND: - default: - } - SimpleDateFormat formatter = CachingDateFormat.getSolrDatetimeFormat(); - formatter.setTimeZone(TimeZone.getTimeZone("UTC")); - return formatter.format(cal.getTime()); - } - - /** - * Returns the formatted start (i.e. lower bound) of a range query. - */ - private String getDateStart(Pair dateAndResolution) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.setTime(dateAndResolution.getFirst()); - switch (dateAndResolution.getSecond()) - { - case Calendar.YEAR: - cal.set(Calendar.MONTH, cal.getActualMinimum(Calendar.MONTH)); - case Calendar.MONTH: - cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH)); - case Calendar.DAY_OF_MONTH: - cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); - case Calendar.HOUR_OF_DAY: - cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE)); - case Calendar.MINUTE: - cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND)); - case Calendar.SECOND: - cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND)); - case Calendar.MILLISECOND: - default: - } - return CachingDateFormat.getSolrDatetimeFormat().format(cal.getTime()); - } - - private Pair parseDateString(String dateString) - { - try - { - return CachingDateFormat.lenientParse(dateString, Calendar.YEAR); - } catch (java.text.ParseException e) - { - SimpleDateFormat oldDf = CachingDateFormat.getDateFormat(); - oldDf.setTimeZone(TimeZone.getTimeZone("UTC")); - try - { - Date date = oldDf.parse(dateString); - return new Pair<>(date, Calendar.SECOND); - } catch (java.text.ParseException ee) - { - if (dateString.equalsIgnoreCase("min")) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.set(Calendar.YEAR, cal.getMinimum(Calendar.YEAR)); - cal.set(Calendar.DAY_OF_YEAR, cal.getMinimum(Calendar.DAY_OF_YEAR)); - cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY)); - cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE)); - cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND)); - cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND)); - return new Pair(cal.getTime(), Calendar.MILLISECOND); - } else if (dateString.equalsIgnoreCase("now")) - { - return new Pair(new Date(), Calendar.MILLISECOND); - } else if (dateString.equalsIgnoreCase("today")) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.setTime(new Date()); - cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY)); - cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE)); - cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND)); - cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND)); - return new Pair(cal.getTime(), Calendar.DAY_OF_MONTH); - } else if (dateString.equalsIgnoreCase("max")) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.set(Calendar.YEAR, cal.getMaximum(Calendar.YEAR)); - cal.set(Calendar.DAY_OF_YEAR, cal.getMaximum(Calendar.DAY_OF_YEAR)); - cal.set(Calendar.HOUR_OF_DAY, cal.getMaximum(Calendar.HOUR_OF_DAY)); - cal.set(Calendar.MINUTE, cal.getMaximum(Calendar.MINUTE)); - cal.set(Calendar.SECOND, cal.getMaximum(Calendar.SECOND)); - cal.set(Calendar.MILLISECOND, cal.getMaximum(Calendar.MILLISECOND)); - return new Pair(cal.getTime(), Calendar.MILLISECOND); - } else - { - return null; // delegate to SOLR date parsing - } - } - } - } - - protected Query functionQueryBuilder(String expandedFieldName, String ending, QName propertyQName, - PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, String queryText, - LuceneFunction luceneFunction) throws ParseException - { - - // Mime type - if (ending.equals(FIELD_MIMETYPE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_SIZE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_LOCALE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_ENCODING_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_CONTENT_DOC_ID_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_TRANSFORMATION_TIME_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_TRANSFORMATION_STATUS_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } - - // Already in expanded form - - // ML - - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) - { - // Build a sub query for each locale and or the results together - - // the analysis will take care of - // cross language matching for each entry - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - - addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, propertyDef, queryText, luceneFunction, - booleanQuery, locale, tokenisationMode); - - } - return booleanQuery.build(); - } - // Content - else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene functions not supported for content"); - } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) - { - if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) - || propertyQName.equals(ContentModel.PROP_USERNAME) - || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) - { - throw new UnsupportedOperationException("Functions are not supported agaisnt special text fields"); - } - - boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) - || propertyQName.equals(ContentModel.PROP_USERNAME) - || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); - - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, propertyDef, queryText, luceneFunction, - booleanQuery, locale, tokenisationMode); - - } - return booleanQuery.build(); - } else - { - throw new UnsupportedOperationException("Lucene Function"); - } - } - - protected TermQuery createNoMatchQuery() - { - return new TermQuery(new Term("NO_TOKENS", "__")); - } - - /** - * Returns null if all clause words were filtered away by the analyzer - * - * @param booleanQuery - * - initial BooleanQuery - * @return BooleanQuery or null if booleanQuery has no clauses - */ - protected BooleanQuery getNonEmptyBooleanQuery(BooleanQuery booleanQuery) - { - if (booleanQuery.clauses().size() > 0) - { - return booleanQuery; - } else - { - return null; - } - } - - protected Query createSolr4IdQuery(String queryText) - { - return createTermQuery(FIELD_SOLR4_ID, queryText); - } - - // Previous SOLR - - protected Query createIdQuery(String queryText) - { - if (NodeRef.isNodeRef(queryText)) - { - return createNodeRefQuery(FIELD_LID, queryText); - } else - { - return createNodeRefQuery(FIELD_ID, queryText); - } - } - - protected Query createPathQuery(String queryText, boolean withRepeats) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse(queryText); - SolrPathQuery pathQuery = handler.getQuery(); - pathQuery.setRepeats(withRepeats); - return new SolrCachingPathQuery(pathQuery); - } - - protected Query createQNameQuery(String queryText) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse("//" + queryText); - SolrPathQuery pathQuery = handler.getQuery(); - return new SolrCachingPathQuery(pathQuery); - } - - protected Query createPrimaryAssocQNameQuery(String queryText) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse("//" + queryText); - SolrPathQuery pathQuery = handler.getQuery(); - pathQuery.setPathField(FIELD_PRIMARYASSOCQNAME); - return new SolrCachingPathQuery(pathQuery); - } - - protected Query createPrimaryAssocTypeQNameQuery(String queryText) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse("//" + queryText); - SolrPathQuery pathQuery = handler.getQuery(); - pathQuery.setPathField(FIELD_PRIMARYASSOCTYPEQNAME); - return new SolrCachingPathQuery(pathQuery); - } - - protected Query createAssocTypeQNameQuery(String queryText) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse("//" + queryText); - SolrPathQuery pathQuery = handler.getQuery(); - pathQuery.setPathField(FIELD_ASSOCTYPEQNAME); - return new SolrCachingPathQuery(pathQuery); - } - - /** - * @param queryText - * @return - */ - protected Query createAclIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - /** - * @param queryText - * @return - */ - protected Query createOwnerQuery(String queryText) throws ParseException - { - return new SolrOwnerQuery(queryText); - } - - /** - * @param queryText - * @return - */ - protected Query createReaderQuery(String queryText) throws ParseException - { - return new SolrReaderQuery(queryText); - } - - /** - * @param queryText - * @return - */ - protected Query createAuthorityQuery(String queryText) throws ParseException - { - //return new SolrAuthorityQuery(queryText); - return new SolrAuthoritySetQuery(","+queryText); - } - - // TODO: correct field names - protected Query addContentAttributeQuery(PropertyDefinition pDef, String queryText, SubQuery subQueryBuilder, - AnalysisMode analysisMode, LuceneFunction luceneFunction, String expandedFieldName, - List expandedLocales) throws ParseException - { - BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder(); - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, - FieldUse.FTS); - if (isExactTermSearch(analysisMode)) - { - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - Query subQuery = subQueryBuilder.getQuery(field.getField(), queryText, analysisMode, - luceneFunction); - if (subQuery != null) - { - booleanQueryBuilder.add(subQuery, Occur.SHOULD); - } - } - } - if (booleanQueryBuilder.build().clauses().size() == 0) - { - throw new UnsupportedOperationException("Exact Term search is not supported unless you configure the field <"+pDef.getName()+"> for cross locale search"); - } - } else - { - for (Locale locale : expandedLocales) - { - boolean localisedSearch = locale.toString().length() != 0; - if (localisedSearch) - { - StringBuilder builder = new StringBuilder(queryText.length() + 10); - builder.append("\u0000").append(locale.toString()).append("\u0000").append(queryText); - } - for (FieldInstance field : indexedField.getFields()) - { - if ((!localisedSearch && !field.isLocalised()) || (localisedSearch && field.isLocalised())) - { - Query subQuery = subQueryBuilder.getQuery(field.getField(), queryText, analysisMode, - luceneFunction); - if (subQuery != null) - { - booleanQueryBuilder.add(subQuery, Occur.SHOULD); - } - } - } - } - } - return getNonEmptyBooleanQuery(booleanQueryBuilder.build()); - } - - protected void addLocaleSpecificUntokenisedMLOrTextFunction(String expandedFieldName, PropertyDefinition pDef, - String queryText, LuceneFunction luceneFunction, Builder booleanQuery, Locale locale, - IndexTokenisationMode tokenisationMode) - { - // Query subQuery = new CaseInsensitiveFieldQuery(new - // Term(getFieldName(expandedFieldName, locale, tokenisationMode, - // IndexTokenisationMode.FALSE), getFixedFunctionQueryText( - // queryText, locale, tokenisationMode, IndexTokenisationMode.FALSE))); - // booleanQuery.add(subQuery, Occur.SHOULD); - // - // if (booleanQuery.getClauses().length == 0) - // { - // booleanQuery.add(createNoMatchQuery(), Occur.SHOULD); - // } - throw new UnsupportedOperationException(); - } - - private FieldInstance getFieldInstance(String baseFieldName, PropertyDefinition pDef, Locale locale, - IndexTokenisationMode preferredIndexTokenisationMode, AnalysisMode analysisMode) - { - if (pDef != null) - { - switch (preferredIndexTokenisationMode) - { - case BOTH: - throw new IllegalStateException("Preferred mode can not be BOTH"); - case FALSE: - if (locale.toString().length() == 0) - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.ID); - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - return field; - } - } - } else - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.ID); - for (FieldInstance field : indexedField.getFields()) - { - if (field.isLocalised()) - { - return field; - } - } - } - break; - case TRUE: - { - IndexedField indexedField; - if (isExactTermSearch(analysisMode)) - { - indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.EXACT); - } else - { - indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.FTS); - } - if (locale.toString().length() == 0) - { - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - return field; - } - } - } else - { - for (FieldInstance field : indexedField.getFields()) - { - if (field.isLocalised()) - { - return field; - } - } - } - break; - } - } - return null; - - } - - return new FieldInstance(baseFieldName, false, false); - - } - - private boolean isExactTermSearch(AnalysisMode analysisMode) { - return analysisMode==AnalysisMode.IDENTIFIER && !this.solrParams.get(DEFTYPE).equals(CMIS_REQUEST_HANDLER); - } - - protected void addLocaleSpecificUntokenisedTextRangeFunction(String expandedFieldName, PropertyDefinition pDef, - String lower, String upper, boolean includeLower, boolean includeUpper, LuceneFunction luceneFunction, - Builder booleanQuery, Locale locale, IndexTokenisationMode tokenisationMode) throws ParseException - { - // String field = getFieldName(expandedFieldName, locale, - // tokenisationMode, IndexTokenisationMode.FALSE); - // - // StringBuilder builder = new StringBuilder(); - // builder.append("\u0000").append(locale.toString()).append("\u0000").append(lower); - // String first = getToken(field, builder.toString(), - // AnalysisMode.IDENTIFIER); - // - // builder = new StringBuilder(); - // builder.append("\u0000").append(locale.toString()).append("\u0000").append(upper); - // String last = getToken(field, builder.toString(), - // AnalysisMode.IDENTIFIER); - // - // Query query = new CaseInsensitiveFieldRangeQuery(field, first, last, - // includeLower, includeUpper); - // booleanQuery.add(query, Occur.SHOULD); - - throw new UnsupportedOperationException(); - - } - - protected void addMLTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, - SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, - String expandedFieldName, PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, - Builder booleanQuery, Locale locale) throws ParseException - { - - addMLTextOrTextAttributeQuery(field, pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, tokenisationMode, booleanQuery, locale); - } - - private void addMLTextOrTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, - SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, - String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) - throws ParseException - { - - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - switch (tokenisationMode) - { - case BOTH: - switch (analysisMode) - { - default: - case DEFAULT: - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); - - if (ContentModel.PROP_NAME.equals(pDef.getName())) - { - setLowercaseExpandedTerms(false); - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); - } - - break; - case TOKENISE: - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); - break; - case IDENTIFIER: - setLowercaseExpandedTerms(false); - if (isExactTermSearch(analysisMode)) - {//with exact search we favour tokenization, specifically cross locale tokenization - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); - } else - { - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); - } - break; - case FUZZY: - case PREFIX: - case WILD: - case LIKE: - setLowercaseExpandedTerms(false); - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); - - break; - } - break; - case FALSE: - setLowercaseExpandedTerms(false); - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); - break; - case TRUE: - default: - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); - break; - } - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - - } - - protected void addTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, - SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, - String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) - throws ParseException - { - - addMLTextOrTextAttributeQuery(field, pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, tokenisationMode, booleanQuery, locale); - } - - private void addLocaleSpecificMLOrTextAttribute(PropertyDefinition pDef, String queryText, SubQuery subQueryBuilder, - AnalysisMode analysisMode, LuceneFunction luceneFunction, Builder booleanQuery, Locale locale, - String textFieldName, IndexTokenisationMode tokenisationMode, - IndexTokenisationMode preferredTokenisationMode) throws ParseException - { - - FieldInstance fieldInstance = getFieldInstance(textFieldName, pDef, locale, preferredTokenisationMode, analysisMode); - if (fieldInstance != null) - { - StringBuilder builder = new StringBuilder(queryText.length() + 10); - if (fieldInstance.isLocalised()) - { - builder.append("\u0000").append(locale.toString()).append("\u0000"); - } - builder.append(queryText); - Query subQuery = subQueryBuilder.getQuery(fieldInstance.getField(), builder.toString(), analysisMode, - luceneFunction); - if (subQuery != null) - { - booleanQuery.add(subQuery, Occur.SHOULD); - } - } - } - - protected void addTextRange(String field, PropertyDefinition pDef, String part1, String part2, boolean includeLower, - boolean includeUpper, AnalysisMode analysisMode, String fieldName, PropertyDefinition propertyDef, - IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) - throws ParseException, IOException - { - switch (tokenisationMode) - { - case BOTH: - switch (analysisMode) - { - case DEFAULT: - case TOKENISE: - addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, - locale, analysisMode, tokenisationMode, IndexTokenisationMode.TRUE); - break; - case IDENTIFIER: - addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, - locale, analysisMode, tokenisationMode, IndexTokenisationMode.FALSE); - break; - case WILD: - case LIKE: - case PREFIX: - case FUZZY: - default: - throw new UnsupportedOperationException(); - } - break; - case FALSE: - addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, locale, - analysisMode, tokenisationMode, IndexTokenisationMode.FALSE); - break; - case TRUE: - addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, locale, - analysisMode, tokenisationMode, IndexTokenisationMode.TRUE); - break; - default: - } - - } - - private void addLocaleSpecificTextRange(String expandedFieldName, PropertyDefinition pDef, String part1, - String part2, boolean includeLower, boolean includeUpper, Builder booleanQuery, Locale locale, - AnalysisMode analysisMode, IndexTokenisationMode tokenisationMode, - IndexTokenisationMode preferredTokenisationMode) throws ParseException, IOException - { - FieldInstance fieldInstance = getFieldInstance(expandedFieldName, pDef, locale, preferredTokenisationMode, analysisMode); - if (fieldInstance != null) - { - String firstString = null; - if ((part1 != null) && !part1.equals("\u0000")) - { - if (fieldInstance.isLocalised()) - { - firstString = getFirstTokenForRange(getLocalePrefixedText(part1, locale), fieldInstance); - if (firstString == null) - { - firstString = "{" + locale.getLanguage() + "}"; - } - } else - { - firstString = getFirstTokenForRange(part1, fieldInstance); - } - } else - { - if (fieldInstance.isLocalised()) - { - firstString = "{" + locale.getLanguage() + "}"; - } else - { - firstString = null; - } - } - - String lastString = null; - if ((part2 != null) && !part2.equals("\uffff")) - { - if (fieldInstance.isLocalised()) - { - lastString = getFirstTokenForRange(getLocalePrefixedText(part2, locale), fieldInstance); - if (lastString == null) - { - lastString = "{" + locale.getLanguage() + "}\uffff"; - } - } else - { - lastString = getFirstTokenForRange(part2, fieldInstance); - } - } else - { - if (fieldInstance.isLocalised()) - { - lastString = "{" + locale.getLanguage() + "}\uffff"; - } else - { - lastString = null; - } - } - - - // * is returned by stringField but it is not correct in case of range queries. - if (firstString != null && firstString.equals("*")) - { - firstString = null; - } - - if (lastString != null && lastString.equals("*")) - { - lastString = null; - } - - TermRangeQuery query = new TermRangeQuery(fieldInstance.getField(), - firstString == null ? null : new BytesRef(firstString), - lastString == null ? null : new BytesRef(lastString), includeLower, includeUpper); - booleanQuery.add(query, Occur.SHOULD); - } - } - - private String getFirstTokenForRange(String string, FieldInstance field) throws IOException - { - PackedTokenAttributeImpl nextToken; - TokenStream source = null; - ; - - try - { - source = getAnalyzer().tokenStream(field.getField(), new StringReader(string)); - source.reset(); - while (source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if (source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if (source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - nextToken = new PackedTokenAttributeImpl(); - nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); - nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if (typeAtt != null) - { - nextToken.setType(typeAtt.type()); - } - if (posIncAtt != null) - { - nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - - return nextToken.toString(); - } - } finally - { - try - { - if (source != null) - { - source.close(); - } - } catch (IOException e) - { - // ignore - } - } - return null; - } - - protected void addTextSpanQuery(String field, PropertyDefinition pDef, String first, String last, int slop, - boolean inOrder, String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, - Locale locale) { - addMLTextOrTextSpanQuery(field, pDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, - booleanQuery, locale); - } - - protected org.apache.lucene.search.Query addContentSpanQuery(String afield, PropertyDefinition pDef, String first, - String last, int slop, boolean inOrder, String expandedFieldName, List expandedLocales) - { - try - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - for (Locale locale : expandedLocales) - { - if (locale.toString().length() == 0) - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.FTS); - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - SpanQuery firstQuery = buildSpanOrQuery(first, field); - SpanQuery lastQuery = buildSpanOrQuery(last, field); - SpanNearQuery result = new SpanNearQuery(new SpanQuery[] - { firstQuery, lastQuery }, slop, inOrder); - booleanQuery.add(result, Occur.SHOULD); - } - } - } else - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.FTS); - for (FieldInstance field : indexedField.getFields()) - { - if (field.isLocalised()) - { - SpanQuery firstQuery = buildSpanOrQuery(getLocalePrefixedText(first, locale), field); - SpanQuery lastQuery = buildSpanOrQuery(getLocalePrefixedText(last, locale), field); - SpanNearQuery result = new SpanNearQuery(new SpanQuery[] - { firstQuery, lastQuery }, slop, inOrder); - booleanQuery.add(result, Occur.SHOULD); - } - } - } - } - return getNonEmptyBooleanQuery(booleanQuery.build()); - } catch (IOException ioe) - { - return createNoMatchQuery(); - } - - } - - private String getLocalePrefixedText(String text, Locale locale) - { - StringBuilder builder = new StringBuilder(text.length() + 10); - builder.append("\u0000").append(locale.toString()).append("\u0000").append(text); - return builder.toString(); - } - - /** - * @param first - * @param field - * @return SpanOrQuery - * @throws IOException - */ - private SpanQuery buildSpanOrQuery(String first, FieldInstance field) throws IOException - { - ArrayList spanOrQueryParts = new ArrayList(); - - PackedTokenAttributeImpl nextToken; - TokenStream source = null; - - try - { - source = getAnalyzer().tokenStream(field.getField(), new StringReader(first)); - source.reset(); - while (source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if (source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if (source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - nextToken = new PackedTokenAttributeImpl(); - nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); - nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if (typeAtt != null) - { - nextToken.setType(typeAtt.type()); - } - if (posIncAtt != null) - { - nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - - SpanQuery termQuery = new SpanTermQuery(new Term(field.getField(), nextToken.toString())); - spanOrQueryParts.add(termQuery); - } - } finally - { - try - { - if (source != null) - { - source.close(); - } - } catch (IOException e) - { - // ignore - } - } - - if (spanOrQueryParts.size() == 1) - { - return spanOrQueryParts.get(0); - } else - { - return new SpanOrQuery(spanOrQueryParts.toArray(new SpanQuery[] - {})); - } - } - - protected void addMLTextSpanQuery(String field, PropertyDefinition pDef, String first, String last, int slop, - boolean inOrder, String expandedFieldName, PropertyDefinition propertyDef, - IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) { - addMLTextOrTextSpanQuery(field, pDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, - booleanQuery, locale); - } - - private void addMLTextOrTextSpanQuery(String afield, PropertyDefinition pDef, String first, String last, int slop, - boolean inOrder, String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, - Locale locale) - { - try - { - - if (locale.toString().length() == 0) - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, - FieldUse.FTS); - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - SpanQuery firstQuery = buildSpanOrQuery(first, field); - SpanQuery lastQuery = buildSpanOrQuery(last, field); - SpanNearQuery result = new SpanNearQuery(new SpanQuery[] - { firstQuery, lastQuery }, slop, inOrder); - booleanQuery.add(result, Occur.SHOULD); - } - } - } else - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, - FieldUse.FTS); - for (FieldInstance field : indexedField.getFields()) - { - if (field.isLocalised()) - { - SpanQuery firstQuery = buildSpanOrQuery(getLocalePrefixedText(first, locale), field); - SpanQuery lastQuery = buildSpanOrQuery(getLocalePrefixedText(last, locale), field); - SpanNearQuery result = new SpanNearQuery(new SpanQuery[] - { firstQuery, lastQuery }, slop, inOrder); - booleanQuery.add(result, Occur.SHOULD); - } - } - } - - } catch (IOException ioe) - { - - } - } - - public boolean addContentCrossLocaleWildcards() - { - return false; - } - - protected Query createOwnerSetQuery(String queryText) throws ParseException - { - return new SolrOwnerSetQuery(queryText); - } - - protected Query createReaderSetQuery(String queryText) throws ParseException - { - return new SolrReaderSetQuery(queryText); - } - - protected Query createAuthoritySetQuery(String queryText) throws ParseException - { - return new SolrAuthoritySetQuery(queryText); - } - - protected Query createDeniedQuery(String queryText) throws ParseException - { - return new SolrDeniedQuery(queryText); - } - - protected Query createDenySetQuery(String queryText) throws ParseException - { - return new SolrDenySetQuery(queryText); - } - - private BytesRef analyzeMultitermTerm(String field, String part) { - return analyzeMultitermTerm(field, part, getAnalyzer()); - } - - // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) - @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") - protected BytesRef analyzeMultitermTerm(String field, String part, Analyzer analyzerIn) { - if (analyzerIn == null) analyzerIn = getAnalyzer(); - - try (TokenStream source = analyzerIn.tokenStream(field, part)) { - source.reset(); - - TermToBytesRefAttribute termAtt = source.getAttribute(TermToBytesRefAttribute.class); - - if (!source.incrementToken()) - throw new IllegalArgumentException("analyzer returned no terms for multiTerm term: " + part); - BytesRef bytes = BytesRef.deepCopyOf(termAtt.getBytesRef()); - if (source.incrementToken()) - throw new IllegalArgumentException("analyzer returned too many terms for multiTerm term: " + part); - source.end(); - return bytes; - } catch (IOException e) { - throw new RuntimeException("Error analyzing multiTerm term: " + part, e); - } - } - - protected Query newRangeQuery(String field, String part1, String part2, boolean startInclusive, boolean endInclusive) { - final BytesRef start; - final BytesRef end; - - if (part1 == null) { - start = null; - } else { - start = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part1) : new BytesRef(part1); - } - - if (part2 == null) { - end = null; - } else { - end = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part2) : new BytesRef(part2); - } - - final TermRangeQuery query = new TermRangeQuery(field, start, end, startInclusive, endInclusive); - - query.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE); - return query; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import edu.umd.cs.findbugs.annotations.SuppressWarnings; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.dictionary.IndexTokenisationMode; +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.adaptor.AnalysisMode; +import org.alfresco.repo.search.adaptor.LuceneFunction; +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.repo.search.impl.QueryParserUtils; +import org.alfresco.repo.search.impl.lucene.analysis.MLTokenDuplicator; +import org.alfresco.repo.search.impl.parsers.FTSQueryException; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser.RerankPhase; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +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.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.AlfrescoAnalyzerWrapper; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.SpecializedFieldType; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldInstance; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.alfresco.solr.AlfrescoSolrDataModel.IndexedField; +import org.alfresco.solr.component.FingerPrintComponent; +import org.alfresco.solr.utils.ThrowingFunction; +import org.alfresco.util.CachingDateFormat; +import org.alfresco.util.Pair; +import org.alfresco.util.SearchLanguageConversion; +import org.antlr.misc.OrderedHashSet; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.commongrams.CommonGramsFilter; +import org.apache.lucene.analysis.shingle.ShingleFilterFactory; +import org.apache.lucene.analysis.synonym.SynonymFilter; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; +import org.apache.lucene.analysis.util.TokenFilterFactory; +import org.apache.lucene.document.Document; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.Term; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.queryparser.classic.QueryParser; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BooleanQuery.Builder; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.LegacyNumericRangeQuery; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.RegexpQuery; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TermRangeQuery; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper; +import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper.TopTermsSpanBooleanQueryRewrite; +import org.apache.lucene.search.spans.SpanNearQuery; +import org.apache.lucene.search.spans.SpanOrQuery; +import org.apache.lucene.search.spans.SpanQuery; +import org.apache.lucene.search.spans.SpanTermQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.Version; +import org.apache.solr.analysis.TokenizerChain; +import org.apache.solr.client.solrj.SolrRequest; +import org.apache.solr.client.solrj.impl.HttpClientUtil; +import org.apache.solr.client.solrj.impl.HttpSolrClient; +import org.apache.solr.client.solrj.request.QueryRequest; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.core.SolrCore; +import org.apache.solr.handler.component.HttpShardHandlerFactory; +import org.apache.solr.handler.component.ShardHandlerFactory; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.update.UpdateShardHandlerConfig; +import org.jaxen.saxpath.SAXPathException; +import org.jaxen.saxpath.base.XPathReader; +import org.json.JSONObject; +import org.springframework.extensions.surf.util.I18NUtil; + +import java.io.IOException; +import java.io.StringReader; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.TimeZone; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.apache.solr.search.QueryParsing.DEFTYPE; + +/** + * @author Andy + * + */ +public class Solr4QueryParser extends QueryParser implements QueryConstants +{ + + private static final String CMIS_REQUEST_HANDLER = "cmis"; + + public Solr4QueryParser(SolrQueryRequest req, Version matchVersion, String f, Analyzer a, + FTSQueryParser.RerankPhase rerankPhase) + { + super(f, a); + setAllowLeadingWildcard(true); + setAnalyzeRangeTerms(true); + this.rerankPhase = rerankPhase; + this.schema = req.getSchema(); + this.solrParams = req.getParams(); + SolrCore core = req.getCore(); + if(core != null) { + this.shardHandlerFactory = core.getCoreContainer().getShardHandlerFactory(); + } + this.request = req; + + } + + private RerankPhase rerankPhase; + private SolrParams solrParams; + private ShardHandlerFactory shardHandlerFactory; + private SolrQueryRequest request; + + + IndexSchema schema; + + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(Solr4QueryParser.class); + + protected NamespacePrefixResolver namespacePrefixResolver; + + protected DictionaryService dictionaryService; + + private TenantService tenantService; + + private SearchParameters searchParameters; + + private MLAnalysisMode mlAnalysisMode = MLAnalysisMode.EXACT_LANGUAGE_AND_ALL; + + private int internalSlop = 0; + + int topTermSpanRewriteLimit = 1000; + + /** + * @param topTermSpanRewriteLimit + * the topTermSpanRewriteLimit to set + */ + public void setTopTermSpanRewriteLimit(int topTermSpanRewriteLimit) + { + this.topTermSpanRewriteLimit = topTermSpanRewriteLimit; + } + + /** + * @param searchParameters + */ + public void setSearchParameters(SearchParameters searchParameters) + { + this.searchParameters = searchParameters; + } + + /** + * @param namespacePrefixResolver + */ + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } + + /** + * @param tenantService + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public SearchParameters getSearchParameters() + { + return searchParameters; + } + + @Override + protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException + { + try + { + internalSlop = slop; + Query query; + query = getFieldQuery(field, queryText); + return query; + } + finally + { + internalSlop = 0; + } + + } + + /** + * @param field + * @param queryText + * @param analysisMode + * @param slop + * @param luceneFunction + * @return the query + * @throws ParseException + * @throws IOException + */ + public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, int slop, + LuceneFunction luceneFunction) throws ParseException + { + try + { + internalSlop = slop; + Query query = getFieldQuery(field, queryText, analysisMode, luceneFunction); + return query; + } + finally + { + internalSlop = 0; + } + + } + + /** + * @param field + * @param sqlLikeClause + * @param analysisMode + * @return the query + * @throws ParseException + */ + public Query getLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException + { + String luceneWildCardExpression = translate(sqlLikeClause); + return getWildcardQuery(field, luceneWildCardExpression, AnalysisMode.LIKE); + } + + private String translate(String string) + { + StringBuilder builder = new StringBuilder(string.length()); + + boolean lastWasEscape = false; + + for (int i = 0; i < string.length(); i++) + { + char c = string.charAt(i); + if (lastWasEscape) + { + builder.append(c); + lastWasEscape = false; + } else + { + if (c == '\\') + { + lastWasEscape = true; + } else if (c == '%') + { + builder.append('*'); + } else if (c == '_') + { + builder.append('?'); + } else if (c == '*') + { + builder.append('\\'); + builder.append(c); + } else if (c == '?') + { + builder.append('\\'); + builder.append(c); + } else + { + builder.append(c); + } + } + } + if (lastWasEscape) + { + throw new FTSQueryException("Escape character at end of string " + string); + } + + return builder.toString(); + } + + /** + * @param field + * @param queryText + * @param analysisMode + * @param luceneFunction + * @return the query + * @throws ParseException + * @throws IOException + */ + public Query getDoesNotMatchFieldQuery(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + Query allQuery = new MatchAllDocsQuery(); + Query matchQuery = getFieldQuery(field, queryText, analysisMode, luceneFunction); + if ((matchQuery != null)) + { + query.add(allQuery, Occur.MUST); + query.add(matchQuery, Occur.MUST_NOT); + } else + { + throw new UnsupportedOperationException(); + } + return query.build(); + } + + public Query getFieldQuery(String field, String queryText) throws ParseException + { + return getFieldQuery(field, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); + } + + /** + * @param field + * @param first + * @param last + * @param slop + * @param inOrder + * @return the query + * @throws ParseException + */ + public Query getSpanQuery(String field, String first, String last, int slop, boolean inOrder) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATH); + } + else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + return createDefaultTextQuery(textField -> getSpanQuery(textField, first, last, slop, inOrder)); + } else if (field.equals(FIELD_CLASS)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_CLASS); + } else if (field.equals(FIELD_TYPE)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_TYPE); + } else if (field.equals(FIELD_EXACTTYPE)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTTYPE); + } else if (field.equals(FIELD_ASPECT)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_ASPECT); + } else if (field.equals(FIELD_EXACTASPECT)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTASPECT); + } else if (isPropertyField(field)) + { + return spanQueryBuilder(field, first, last, slop, inOrder); + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); + query.add(part, Occur.SHOULD); + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getSpanQuery(fieldName, first, last, slop, inOrder); + query.add(part, Occur.SHOULD); + } + return query.build(); + } + + } else if (field.equals(FIELD_ISUNSET)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISUNSET); + } else if (field.equals(FIELD_ISNULL)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNULL); + } else if (field.equals(FIELD_ISNOTNULL)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNOTNULL); + } else if (field.equals(FIELD_EXISTS)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXISTS); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); + query.add(part, Occur.SHOULD); + } + return query.build(); + } else if (field.equals(FIELD_FTSSTATUS)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_FTSSTATUS); + } else if (field.equals(FIELD_TAG)) + { + return null; + } else if (isPropertyField(field)) + { + return spanQueryBuilder(field, first, last, slop, inOrder); + } else + { + BytesRef firstBytes = analyzeMultitermTerm(field, first); + BytesRef lastBytes = analyzeMultitermTerm(field, last); + SpanQuery firstTerm = new SpanTermQuery(new Term(field, firstBytes)); + SpanQuery lastTerm = new SpanTermQuery(new Term(field, lastBytes)); + return new SpanNearQuery(new SpanQuery[] + { firstTerm, lastTerm }, slop, inOrder); + } + } + + protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException + { + return getFieldQuery(field, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); + } + + /** + * @param field + * @param queryText + * @param analysisMode + * @param luceneFunction + * @return the query + * @throws ParseException + * @throws IOException + */ + @SuppressWarnings("deprecation") + public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + try + { + if (field.equals(FIELD_PATH)) + { + return createPathQuery(queryText, false); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + return createPathQuery(queryText, true); + } else if (field.equals(FIELD_TEXT)) + { + return createTextQuery(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_ID)) + { + return createIdQuery(queryText); + } else if (field.equals(FIELD_SOLR4_ID)) + { + return createSolr4IdQuery(queryText); + } else if (field.equals(FIELD_DBID)) + { + return createDbidQuery(queryText); + } else if (field.equals(FIELD_ACLID)) + { + return createAclIdQuery(queryText); + } else if (field.equals(FIELD_OWNER)) + { + return createOwnerQuery(queryText); + } else if (field.equals(FIELD_OWNERSET)) + { + return createOwnerSetQuery(queryText); + } else if (field.equals(FIELD_READER)) + { + return createReaderQuery(queryText); + } else if (field.equals(FIELD_READERSET)) + { + return createReaderSetQuery(queryText); + } else if (field.equals(FIELD_AUTHORITY)) + { + /* + * ACL DOCUMENTATION STARTS HERE + * This creates the query that applies the ACL filter + */ + return createAuthorityQuery(queryText); + } else if (field.equals(FIELD_AUTHORITYSET)) + { + /* + * ACL DOCUMENTATION STARTS HERE + * This creates the query that applies the ACL filter + */ + return createAuthoritySetQuery(queryText); + } else if (field.equals(FIELD_DENIED)) + { + return createDeniedQuery(queryText); + } else if (field.equals(FIELD_DENYSET)) + { + return createDenySetQuery(queryText); + } else if (field.equals(FIELD_ISROOT)) + { + return createIsRootQuery(queryText); + } else if (field.equals(FIELD_ISCONTAINER)) + { + return createIsContainerQuery(queryText); + } else if (field.equals(FIELD_ISNODE)) + { + return createIsNodeQuery(queryText); + } else if (field.equals(FIELD_TX)) + { + return createTransactionQuery(queryText); + } else if (field.equals(FIELD_INTXID)) + { + return createInTxIdQuery(queryText); + } else if (field.equals(FIELD_INACLTXID)) + { + return createInAclTxIdQuery(queryText); + } else if (field.equals(FIELD_PARENT)) + { + return createParentQuery(queryText); + } else if (field.equals(FIELD_PRIMARYPARENT)) + { + return createPrimaryParentQuery(queryText); + } else if (field.equals(FIELD_QNAME)) + { + return createQNameQuery(queryText); + } else if (field.equals(FIELD_PRIMARYASSOCQNAME)) + { + return createPrimaryAssocQNameQuery(queryText); + } else if (field.equals(FIELD_PRIMARYASSOCTYPEQNAME)) + { + return createPrimaryAssocTypeQNameQuery(queryText); + } else if (field.equals(FIELD_ASSOCTYPEQNAME)) + { + return createAssocTypeQNameQuery(queryText); + } else if (field.equals(FIELD_CLASS)) + { + ClassDefinition target = QueryParserUtils.matchClassDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (target == null) + { + return new TermQuery(new Term(FIELD_TYPE, "_unknown_")); + } + return getFieldQuery(target.isAspect() ? FIELD_ASPECT : FIELD_TYPE, queryText, analysisMode, + luceneFunction); + } else if (field.equals(FIELD_TYPE)) + { + return createTypeQuery(queryText, false); + } else if (field.equals(FIELD_EXACTTYPE)) + { + return createTypeQuery(queryText, true); + } else if (field.equals(FIELD_ASPECT)) + { + return createAspectQuery(queryText, false); + } else if (field.equals(FIELD_EXACTASPECT)) + { + return createAspectQuery(queryText, true); + } else if (isPropertyField(field)) + { + Query query = attributeQueryBuilder(field, queryText, new FieldQuery(), analysisMode, luceneFunction); + return query; + } else if (field.equals(FIELD_ALL)) + { + return createAllQuery(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_ISUNSET)) + { + return createIsUnsetQuery(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_ISNULL)) + { + return createIsNullQuery(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_ISNOTNULL)) + { + return createIsNotNull(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_EXISTS)) + { + return createExistsQuery(queryText, analysisMode, luceneFunction); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field) != null) + { + return createDataTypeDefinitionQuery(field, queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_TXID)) + { + return createTxIdQuery(queryText); + } else if (field.equals(FIELD_ACLTXID)) + { + return createAclTxIdQuery(queryText); + } else if (field.equals(FIELD_TXCOMMITTIME)) + { + return createTxCommitTimeQuery(queryText); + } else if (field.equals(FIELD_ACLTXCOMMITTIME)) + { + return createAclTxCommitTimeQuery(queryText); + } else if (field.equals(FIELD_TAG)) + { + return createTagQuery(queryText); + } else if (field.equals(FIELD_SITE)) + { + return createSiteQuery(queryText); + } else if (field.equals(FIELD_PNAME)) + { + return createPNameQuery(queryText); + } else if (field.equals(FIELD_NPATH)) + { + return createNPathQuery(queryText); + } else if (field.equals(FIELD_TENANT)) + { + return createTenantQuery(queryText); + } else if (field.equals(FIELD_ANCESTOR)) + { + return createAncestorQuery(queryText); + } else if (field.equals(FIELD_FINGERPRINT)) + { + String[] parts = queryText.split("_"); + Collection values = null; + String nodeId = parts[0]; + + JSONObject json = (JSONObject)request.getContext().get(AbstractQParser.ALFRESCO_JSON); + String fingerPrint = null; + if(json != null) { + //Was the fingerprint passed in + String fingerPrintKey = "fingerprint." + nodeId; + if(json.has(fingerPrintKey)) { + fingerPrint = (String) json.get("fingerprint." + nodeId); + if (fingerPrint != null) { + List l = new ArrayList(); + String[] hashes = fingerPrint.split(" "); + for (String hash : hashes) { + l.add(hash); + } + values = l; + } + } + } else { + json = new JSONObject(); + } + + if (values == null) { + values = fetchMinHash(nodeId); + } + + String shards = this.solrParams.get("shards"); + if(values == null && shards != null) { + //we are in distributed mode + //Fetch the fingerPrint from the shards. + //The UUID and DBID will both work for method call. + values = fetchFingerPrint(shards, nodeId); + } + + //If we're in distributed mode then add the fingerprint to the json + if(values != null && shards != null && fingerPrint == null) { + ModifiableSolrParams newParams = new ModifiableSolrParams(); + newParams.add(solrParams); + solrParams = newParams; + json.put("fingerprint." + nodeId, join(values, " ")); + String jsonString = json.toString(); + newParams.add(AbstractQParser.ALFRESCO_JSON, jsonString); + request.getContext().put(AbstractQParser.ALFRESCO_JSON, json); + request.setParams(newParams); + } + + return createFingerPrintQuery(field, queryText, values, analysisMode, luceneFunction); + + } else + { + return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); + } + + } catch (SAXPathException e) + { + throw new ParseException("Failed to parse XPath...\n" + e.getMessage()); + } catch (IOException e) + { + throw new ParseException("IO: " + e.getMessage()); + } + + } + + private boolean isNumber(String s) { + for(int i=0; i 0) { + builder.append(" "); + } + builder.append(o.toString()); + } + return builder.toString(); + } + + + /** + * fetch MINHASH values from lucene index + * @param id argument of search. It is taken as DBID if is numberic, LID otherwise + * @return List of minhashes + * @throws IOException + */ + private Collection fetchMinHash(String id) throws IOException { + SolrIndexSearcher searcher = request.getSearcher(); + + + Query q; + if(isNumber(id)) + { + long dbid = Long.parseLong(id); + q = LegacyNumericRangeQuery.newLongRange("DBID", dbid, dbid + 1, true, false); + } + else + { + String query = id.startsWith("workspace") ? id : "workspace://SpacesStore/"+id; + q = new TermQuery(new Term(FIELD_LID, query)); + } + + TopDocs docs = searcher.search(q, 1); + Set fields = new HashSet(); + fields.add(FIELD_FINGERPRINT); + + if(docs.totalHits == 1) { + ScoreDoc scoreDoc = docs.scoreDocs[0]; + Document doc = searcher.doc(scoreDoc.doc, fields); + + List values = new ArrayList<>(); + + IndexableField[] minHashes = doc.getFields("MINHASH"); + for (IndexableField minHash : minHashes) + { + SchemaField sf = schema.getFieldOrNull(minHash.name()); + Object value = DocsStreamer.getValue(sf, minHash); + values.add(value); + } + + return values; + } + + return null; + } + + private Collection fetchFingerPrint(String shards, String nodeId) { + shards = shards.replace(",", "|"); + List urls = ((HttpShardHandlerFactory)shardHandlerFactory).buildURLList(shards); + ExecutorService executorService = null; + List futures = new ArrayList(); + Collection fingerPrint = null; + try { + executorService = Executors.newCachedThreadPool(); + for (String url : urls) { + futures.add(executorService.submit(new FingerPrintFetchTask(url, nodeId))); + } + + for (Future future : futures) { + Collection col = future.get(); + if(col != null) { + fingerPrint = col; + } + } + + }catch(Exception e) { + logger.error(e); + } finally { + if (executorService != null) + { + executorService.shutdown(); + } + } + return fingerPrint; + } + + + + + private class FingerPrintFetchTask implements Callable { + private String url; + private String id; + + public FingerPrintFetchTask(String url, String id) { + this.url = url; + this.id = id; + } + + public Collection call() throws Exception { + HttpSolrClient solrClient = null; + CloseableHttpClient closeableHttpClient = null; + try { + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add(FingerPrintComponent.COMPONENT_NAME, "true"); + params.add("id", id); + params.add("qt","/fingerprint"); + closeableHttpClient = HttpClientUtil.createClient(getClientParams()); + solrClient = new HttpSolrClient.Builder(url).withHttpClient(closeableHttpClient).build();; + QueryRequest request = new QueryRequest(params, SolrRequest.METHOD.POST); + QueryResponse response = request.process(solrClient); + NamedList dataResponse = response.getResponse(); + NamedList fingerprint = (NamedList) dataResponse.get("fingerprint"); + return (Collection)fingerprint.get("MINHASH"); + } finally { + if (closeableHttpClient != null) + { + closeableHttpClient.close(); + } + if (solrClient != null) + { + solrClient.close(); + } + } + } + + protected ModifiableSolrParams getClientParams() { + ModifiableSolrParams clientParams = new ModifiableSolrParams(); + clientParams.set(HttpClientUtil.PROP_SO_TIMEOUT, UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATESOTIMEOUT); + clientParams.set(HttpClientUtil.PROP_CONNECTION_TIMEOUT, UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATECONNTIMEOUT); + return clientParams; + } + } + + /** + * @param field + * @param queryText + * @param analysisMode + * + * @param luceneFunction + * @return + * @throws IOException + * @throws ParseException + */ + private Query createFingerPrintQuery(String field, String queryText, Collection values, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws IOException, ParseException + { + String[] parts = queryText.split("_"); + if (parts.length == 0) + { + return createIsNodeQuery("T"); + } + + if (values != null) + { + int bandSize = 1; + float fraction = -1; + float truePositive = 1; + if (parts.length > 1) + { + fraction = Float.parseFloat(parts[1]); + if (fraction > 1) + { + fraction /= 100; + } + } + if (parts.length > 2) + { + truePositive = Float.parseFloat(parts[2]); + if (truePositive > 1) + { + truePositive /= 100; + } + bandSize = computeBandSize(values.size(), fraction, truePositive); + } + BooleanQuery.Builder builder = new BooleanQuery.Builder(); + BooleanQuery.Builder childBuilder = new BooleanQuery.Builder(); + int rowInBand = 0; + for (Object token : values) + { + TermQuery tq = new TermQuery(new Term("MINHASH", token.toString())); + if (bandSize == 1) + { + builder.add(new ConstantScoreQuery(tq), Occur.SHOULD); + } else + { + childBuilder.add(new ConstantScoreQuery(tq), Occur.MUST); + rowInBand++; + if (rowInBand == bandSize) + { + builder.add(new ConstantScoreQuery(childBuilder.setDisableCoord(true).build()), + Occur.SHOULD); + childBuilder = new BooleanQuery.Builder(); + rowInBand = 0; + } + } + } + // Avoid a dubious narrow band .... wrap around and pad with the + // start + if (childBuilder.build().clauses().size() > 0) + { + for (Object token : values) + { + TermQuery tq = new TermQuery(new Term("MINHASH", token.toString())); + childBuilder.add(new ConstantScoreQuery(tq), Occur.MUST); + rowInBand++; + if (rowInBand == bandSize) + { + builder.add(new ConstantScoreQuery(childBuilder.setDisableCoord(true).build()), + Occur.SHOULD); + break; + } + } + } + builder.setDisableCoord(true); + if (parts.length == 2) + { + builder.setMinimumNumberShouldMatch((int) (Math.ceil(values.size() * fraction))); + } + Query q = builder.build(); + return q; + } else + { + return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); + } + } + + private int computeBandSize(int numHash, double sim, double expectedTruePositive) + { + for (int bands = 1; bands <= numHash; bands++) + { + int rowsInBand = numHash / bands; + double truePositive = 1 - Math.pow(1 - Math.pow(sim, rowsInBand), bands); + if (truePositive > expectedTruePositive) + { + return rowsInBand; + } + } + return 1; + } + + private ArrayList getTokens(IndexableField indexableField) throws IOException + { + ArrayList tokens = new ArrayList(); + + TokenStream ts = indexableField.tokenStream(schema.getIndexAnalyzer(), null); + CharTermAttribute termAttribute = ts.getAttribute(CharTermAttribute.class); + ts.reset(); + while (ts.incrementToken()) + { + String token = new String(termAttribute.buffer(), 0, termAttribute.length()); + tokens.add(token); + } + ts.end(); + ts.close(); + + return tokens; + } + + /** + * @param queryText + * @return + */ + private org.apache.lucene.search.Query createNPathQuery(String queryText) + { + return createTermQuery(FIELD_NPATH, queryText); + } + + /** + * @param queryText + * @return + */ + private Query createPNameQuery(String queryText) + { + return createTermQuery(FIELD_PNAME, queryText); + } + + /** + * @param queryText + * @return + */ + private Query createSiteQuery(String queryText) + { + if (queryText.equals("_EVERYTHING_")) + { + return createTermQuery(FIELD_ISNODE, "T"); + } + else if (queryText.equals("_ALL_SITES_")) + { + BooleanQuery.Builder invertedRepositoryQuery = new BooleanQuery.Builder(); + invertedRepositoryQuery.add(createTermQuery(FIELD_ISNODE, "T"), Occur.MUST); + invertedRepositoryQuery.add(createTermQuery(FIELD_SITE, "_REPOSITORY_"), Occur.MUST_NOT); + return invertedRepositoryQuery.build(); + } + else + { + return createTermQuery(FIELD_SITE, queryText); + } + } + + private boolean isPropertyField(String field) + { + if (field.startsWith(PROPERTY_FIELD_PREFIX)) + { + return true; + } + int index = field.lastIndexOf('@'); + if (index > -1) + { + PropertyDefinition pDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field.substring(index + 1)); + if (pDef != null) + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance() + .getIndexedFieldNamesForProperty(pDef.getName()); + for (FieldInstance instance : indexedField.getFields()) + { + if (instance.getField().equals(field)) + { + return true; + } + } + return false; + } else + { + return false; + } + } else + { + return false; + } + + } + + protected Query createTenantQuery(String queryText) throws ParseException + { + + if (queryText.length() > 0) + { + + return getFieldQueryImplWithIOExceptionWrapped(FIELD_TENANT, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + + } + else + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_TENANT, "_DEFAULT_", AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + } + + protected Query createAncestorQuery(String queryText) throws ParseException + { + return createNodeRefQuery(FIELD_ANCESTOR, queryText); + } + + /** + * @param tag + * (which will then be ISO9075 encoded) + * @return + * @throws ParseException + */ + protected Query createTagQuery(String tag) throws ParseException + { + return createTermQuery(FIELD_TAG, tag.toLowerCase()); + } + + private Query getFieldQueryImplWithIOExceptionWrapped(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException + { + try + { + return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); + } + catch (IOException e) + { + throw new ParseException("IO: " + e.getMessage()); + } + } + + /** + * @param queryText + * @return + */ + protected Query createDbidQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_DBID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createTxIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_TXID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createAclTxIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLTXID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createTxCommitTimeQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_TXCOMMITTIME, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createAclTxCommitTimeQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLTXCOMMITTIME, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createDataTypeDefinitionQuery(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc if + // required + Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), queryText, analysisMode, + luceneFunction); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } + + protected Query createIsNotNull(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (pd != null) + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + query.add(createTermQuery(FIELD_PROPERTIES, pd.getName().toString()), Occur.MUST); + query.add(createTermQuery(FIELD_NULLPROPERTIES, pd.getName().toString()), Occur.MUST_NOT); + ; + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + + Query presenceQuery = getWildcardQuery(queryText, "*"); + if (presenceQuery != null) + { + query.add(presenceQuery, Occur.MUST); + } + return query.build(); + } + } + + protected Query createIsNullQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (pd != null) + { + return createTermQuery(FIELD_NULLPROPERTIES, pd.getName().toString()); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + Query presenceQuery = getWildcardQuery(queryText, "*"); + if (presenceQuery != null) + { + query.add(createIsNodeQuery("T"), Occur.MUST); + query.add(presenceQuery, Occur.MUST_NOT); + } + return query.build(); + } + } + + protected Query createIsUnsetQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (pd != null) + { + ClassDefinition containerClass = pd.getContainerClass(); + QName container = containerClass.getName(); + String classType = containerClass.isAspect() ? FIELD_ASPECT : FIELD_TYPE; + Query typeQuery = getFieldQuery(classType, container.toString(), analysisMode, luceneFunction); + + BooleanQuery.Builder query = new BooleanQuery.Builder(); + Query presenceQuery = createTermQuery(FIELD_PROPERTIES, pd.getName().toString()); + if (presenceQuery != null) + { + query.add(typeQuery, Occur.MUST); + query.add(presenceQuery, Occur.MUST_NOT); + } + + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + + Query presenceQuery = getWildcardQuery(queryText, "*"); + if (presenceQuery != null) + { + query.add(presenceQuery, Occur.MUST_NOT); + } + return query.build(); + } + } + + protected Query createExistsQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (pd != null) + { + return createTermQuery(FIELD_PROPERTIES, pd.getName().toString()); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + + Query presenceQuery = getWildcardQuery(queryText, "*"); + if (presenceQuery != null) + { + query.add(createIsNodeQuery("T"), Occur.MUST); + query.add(presenceQuery, Occur.MUST_NOT); + } + return query.build(); + } + } + + protected Query createAllQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + // Set all = searchParameters.getAllAttributes(); + // if ((all == null) || (all.size() == 0)) + // { + // Collection contentAttributes = + // dictionaryService.getAllProperties(null); + // BooleanQuery query = new BooleanQuery(); + // for (QName qname : contentAttributes) + // { + // // The super implementation will create phrase queries etc if + // required + // Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), + // queryText, analysisMode, luceneFunction); + // if (part != null) + // { + // query.add(part, Occur.SHOULD); + // } + // else + // { + // query.add(createNoMatchQuery(), Occur.SHOULD); + // } + // } + // return query; + // } + // else + // { + // BooleanQuery query = new BooleanQuery(); + // for (String fieldName : all) + // { + // Query part = getFieldQuery(fieldName, queryText, analysisMode, + // luceneFunction); + // if (part != null) + // { + // query.add(part, Occur.SHOULD); + // } + // else + // { + // query.add(createNoMatchQuery(), Occur.SHOULD); + // } + // } + // return query; + // } + throw new UnsupportedOperationException(); + } + + protected Query createAspectQuery(String queryText, boolean exactOnly) + { + AspectDefinition target = QueryParserUtils.matchAspectDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (target == null) + { + return new TermQuery(new Term(FIELD_ASPECT, "_unknown_")); + } + + if (exactOnly) + { + QName targetQName = target.getName(); + TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, targetQName.toString())); + + return termQuery; + } + else + { + Collection subclasses = dictionaryService.getSubAspects(target.getName(), true); + + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + for (QName qname : subclasses) + { + AspectDefinition current = dictionaryService.getAspect(qname); + if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) + { + TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, qname.toString())); + if (termQuery != null) + { + booleanQuery.add(termQuery, Occur.SHOULD); + } + } + } + return booleanQuery.build(); + } + + } + + protected Query createTypeQuery(String queryText, boolean exactOnly) throws ParseException + { + TypeDefinition target = QueryParserUtils.matchTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (target == null) + { + return new TermQuery(new Term(FIELD_TYPE, "_unknown_")); + } + if (exactOnly) + { + QName targetQName = target.getName(); + TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, targetQName.toString())); + return termQuery; + } + else + { + Collection subclasses = dictionaryService.getSubTypes(target.getName(), true); + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + for (QName qname : subclasses) + { + TypeDefinition current = dictionaryService.getType(qname); + if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) + { + TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, qname.toString())); + if (termQuery != null) + { + booleanQuery.add(termQuery, Occur.SHOULD); + } + } + } + return booleanQuery.build(); + } + } + + protected Query createInTxIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_INTXID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createInAclTxIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_INACLTXID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + @SuppressWarnings("deprecation") + protected Query createTransactionQuery(String queryText) + { + return createTermQuery(FIELD_TX, queryText); + } + + protected Query createIsNodeQuery(String queryText) + { + return createTermQuery(FIELD_ISNODE, queryText); + } + + protected Query createIsContainerQuery(String queryText) + { + return createTermQuery(FIELD_ISCONTAINER, queryText); + } + + protected Query createIsRootQuery(String queryText) + { + return createTermQuery(FIELD_ISROOT, queryText); + } + + protected Query createTermQuery(String field, String queryText) + { + TermQuery termQuery = new TermQuery(new Term(field, queryText)); + return termQuery; + } + + protected Query createPrimaryParentQuery(String queryText) + { + return createNodeRefQuery(FIELD_PRIMARYPARENT, queryText); + } + + protected Query createParentQuery(String queryText) + { + return createNodeRefQuery(FIELD_PARENT, queryText); + } + + protected Query createNodeRefQuery(String field, String queryText) + { + if (tenantService.isTenantUser() && (queryText.contains(StoreRef.URI_FILLER))) + { + // assume NodeRef, since it contains StorRef URI filler + queryText = tenantService.getName(new NodeRef(queryText)).toString(); + } + return createTermQuery(field, queryText); + } + + + + /** + * + * Get generic text query + * + * + */ + protected Query createDefaultTextQuery(ThrowingFunction getQuery) throws ParseException + { + + Set text = searchParameters.getTextAttributes(); + if (text == null || text.isEmpty()) + { + text = Stream.of(AlfrescoDefaultTextFields.values()) + .map(t -> PROPERTY_FIELD_PREFIX + t.getFieldName()) + .collect(Collectors.toSet()); + } + + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : text) + { + Query part; + try { + part = getQuery.apply(fieldName); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } catch (UnsupportedOperationException e) { + logger.warn(e); + } + } + return query.build(); + + } + + + protected Query createTextQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return createDefaultTextQuery(textField -> getFieldQuery(textField, queryText, analysisMode, luceneFunction)); + } + + @SuppressWarnings("unchecked") + protected Query getFieldQueryImpl(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException, IOException + { + // make sure the field exists or return a dummy query so we have no + // error ....ACE-3231 + SchemaField schemaField = schema.getFieldOrNull(field); + boolean isNumeric = false; + if (schemaField == null) + { + return new TermQuery(new Term("_dummy_", "_miss_")); + } + else + { + isNumeric = (schemaField.getType().getNumericType() != null); + if (isNumeric) + { + //Check to see if queryText is numeric or else it will fail. + try + { + Double.valueOf(queryText); + } + catch (NumberFormatException e) + { + return new TermQuery(new Term("_dummy_", "_miss_")); + } + } + } + + // Use the analyzer to get all the tokens, and then build a TermQuery, + // PhraseQuery, or noth + + // TODO: Untokenised columns with functions require special handling + + if (luceneFunction != LuceneFunction.FIELD) + { + throw new UnsupportedOperationException( + "Field queries are not supported on lucene functions (UPPER, LOWER, etc)"); + } + + // if the incoming string already has a language identifier we strip it + // iff and addit back on again + + String localePrefix = ""; + + String toTokenise = queryText; + + if (queryText.startsWith("{")) + { + int position = queryText.indexOf("}"); + if (position > 0) + { + String language = queryText.substring(0, position + 1); + Locale locale = new Locale(queryText.substring(1, position)); + String token = queryText.substring(position + 1); + boolean found = false; + for (Locale current : Locale.getAvailableLocales()) + { + if (current.toString().equalsIgnoreCase(locale.toString())) + { + found = true; + break; + } + } + if (found) + { + localePrefix = language; + toTokenise = token; + } else + { + // toTokenise = token; + } + } + } + + String testText = toTokenise; + boolean requiresMLTokenDuplication = false; + String localeString = null; + if (isPropertyField(field) && (localePrefix.length() == 0)) + { + if ((queryText.length() > 0) && (queryText.charAt(0) == '\u0000')) + { + int position = queryText.indexOf("\u0000", 1); + testText = queryText.substring(position + 1); + requiresMLTokenDuplication = true; + localeString = queryText.substring(1, position); + + } + } + + // find the positions of any escaped * and ? and ignore them + + Set wildcardPoistions = getWildcardPositions(testText); + + TokenStream source = null; + ArrayList list = new ArrayList(); + boolean severalTokensAtSamePosition = false; + PackedTokenAttributeImpl nextToken; + int positionCount = 0; + + try + { + source = getAnalyzer().tokenStream(field, new StringReader(toTokenise)); + source.reset(); + while (source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if (source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if (source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + nextToken = new PackedTokenAttributeImpl(); + nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); + nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if (typeAtt != null) + { + nextToken.setType(typeAtt.type()); + } + if (posIncAtt != null) + { + nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + + list.add(nextToken); + if (nextToken.getPositionIncrement() != 0) + positionCount += nextToken.getPositionIncrement(); + else + severalTokensAtSamePosition = true; + } + } + finally + { + try + { + if (source != null) + { + source.close(); + } + } catch (IOException e) + { + // ignore + } + } + + // add any alpha numeric wildcards that have been missed + // Fixes most stop word and wild card issues + + for (int index = 0; index < testText.length(); index++) + { + char current = testText.charAt(index); + if (((current == '*') || (current == '?')) && wildcardPoistions.contains(index)) + { + StringBuilder pre = new StringBuilder(10); + if (index == 0) + { + // "*" and "?" at the start + + boolean found = false; + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl test = list.get(j); + if ((test.startOffset() <= 0) && (0 < test.endOffset())) + { + found = true; + break; + } + } + if (!found && (list.size() == 0)) + { + // Add new token followed by * not given by the + // tokeniser + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append("", 0, 0); + newToken.setType("ALPHANUM"); + if (requiresMLTokenDuplication) + { + Locale locale = I18NUtil.parseLocale(localeString); + @SuppressWarnings("resource") + MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, MLAnalysisMode.EXACT_LANGUAGE); + Iterator it = duplicator.buildIterator(newToken); + if (it != null) + { + int count = 0; + while (it.hasNext()) + { + list.add(it.next()); + count++; + if (count > 1) + { + severalTokensAtSamePosition = true; + } + } + } + } + // content + else + { + list.add(newToken); + } + } + } + else if (index > 0) + { + // Add * and ? back into any tokens from which it has been + // removed + + boolean tokenFound = false; + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl test = list.get(j); + if ((test.startOffset() <= index) && (index < test.endOffset())) + { + if (requiresMLTokenDuplication) + { + String termText = test.toString(); + int position = termText.indexOf("}"); + String language = termText.substring(0, position + 1); + String token = termText.substring(position + 1); + if (index >= test.startOffset() + token.length()) + { + test.setEmpty(); + test.append(language + token + current); + } + } + else + { + if (index >= test.startOffset() + test.length()) + { + test.setEmpty(); + test.append(test.toString() + current); + } + } + tokenFound = true; + break; + } + } + + if (!tokenFound) + { + for (int i = index - 1; i >= 0; i--) + { + char c = testText.charAt(i); + if (Character.isLetterOrDigit(c)) + { + boolean found = false; + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl test = list.get(j); + if ((test.startOffset() <= i) && (i < test.endOffset())) + { + found = true; + break; + } + } + if (found) + { + break; + } + else + { + pre.insert(0, c); + } + } + else + { + break; + } + } + if (pre.length() > 0) + { + // Add new token followed by * not given by the + // tokeniser + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(pre.toString()); + newToken.setOffset(index - pre.length(), index); + newToken.setType("ALPHANUM"); + if (requiresMLTokenDuplication) + { + Locale locale = I18NUtil.parseLocale(localeString); + @SuppressWarnings("resource") + MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, + MLAnalysisMode.EXACT_LANGUAGE); + Iterator it = duplicator.buildIterator(newToken); + if (it != null) + { + int count = 0; + while (it.hasNext()) + { + list.add(it.next()); + count++; + if (count > 1) + { + severalTokensAtSamePosition = true; + } + } + } + } + // content + else + { + list.add(newToken); + } + } + } + } + + StringBuilder post = new StringBuilder(10); + if (index > 0) + { + for (int i = index + 1; i < testText.length(); i++) + { + char c = testText.charAt(i); + if (Character.isLetterOrDigit(c)) + { + boolean found = false; + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl test = list.get(j); + if ((test.startOffset() <= i) && (i < test.endOffset())) + { + found = true; + break; + } + } + if (found) + { + break; + } + else + { + post.append(c); + } + } + else + { + break; + } + } + if (post.length() > 0) + { + // Add new token followed by * not given by the + // tokeniser + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(post.toString()); + newToken.setOffset(index + 1, index + 1 + post.length()); + newToken.setType("ALPHANUM"); + if (requiresMLTokenDuplication) + { + Locale locale = I18NUtil.parseLocale(localeString); + @SuppressWarnings("resource") + MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, MLAnalysisMode.EXACT_LANGUAGE); + Iterator it = duplicator.buildIterator(newToken); + if (it != null) + { + int count = 0; + while (it.hasNext()) + { + list.add(it.next()); + count++; + if (count > 1) + { + severalTokensAtSamePosition = true; + } + } + } + } + // content + else + { + list.add(newToken); + } + } + } + + } + } + + // Put in real position increments as we treat them correctly + + int curentIncrement = -1; + for (PackedTokenAttributeImpl c : list) + { + if (curentIncrement == -1) + { + curentIncrement = c.getPositionIncrement(); + } else if (c.getPositionIncrement() > 0) + { + curentIncrement = c.getPositionIncrement(); + } else + { + c.setPositionIncrement(curentIncrement); + } + } + + // Fix up position increments for in phrase isolated wildcards + + boolean lastWasWild = false; + for(int i = 0; i < list.size() -1; i++) + { + for(int j = list.get(i).endOffset() + 1; j < list.get(i + 1).startOffset() - 1; j++) + { + if(wildcardPoistions.contains(j)) + { + if(!lastWasWild) + { + list.get(i+1).setPositionIncrement(list.get(i+1).getPositionIncrement() + 1); + } + lastWasWild = true; + } + else + { + lastWasWild = false; + } + } + } + + Collections.sort(list, new Comparator() + { + + public int compare(PackedTokenAttributeImpl o1, PackedTokenAttributeImpl o2) + { + int dif = o1.startOffset() - o2.startOffset(); + return dif; + + } + }); + + // Combined * and ? based strings - should redo the tokeniser + + // Build tokens by position + + LinkedList> tokensByPosition = new LinkedList>(); + LinkedList currentList = null; + int lastStart = 0; + for (PackedTokenAttributeImpl c : list) + { + if (c.startOffset() == lastStart) + { + if (currentList == null) + { + currentList = new LinkedList(); + tokensByPosition.add(currentList); + } + currentList.add(c); + } else + { + currentList = new LinkedList(); + tokensByPosition.add(currentList); + currentList.add(c); + } + lastStart = c.startOffset(); + } + + // Build all the token sequences and see which ones get strung together + + OrderedHashSet> allTokenSequencesSet = new OrderedHashSet>(); + for (LinkedList tokensAtPosition : tokensByPosition) + { + OrderedHashSet> positionalSynonymSequencesSet = new OrderedHashSet>(); + + OrderedHashSet> newAllTokenSequencesSet = new OrderedHashSet>(); + + FOR_FIRST_TOKEN_AT_POSITION_ONLY: for (PackedTokenAttributeImpl t : tokensAtPosition) + { + PackedTokenAttributeImpl replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(t); + replace.setOffset(t.startOffset(), t.endOffset()); + replace.setType(t.type()); + replace.setPositionIncrement(t.getPositionIncrement()); + + boolean tokenFoundSequence = false; + for (LinkedList tokenSequence : allTokenSequencesSet) + { + LinkedList newEntry = new LinkedList(); + newEntry.addAll(tokenSequence); + if ((newEntry.getLast().endOffset() == replace.endOffset()) + && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) + { + if ((newEntry.getLast().startOffset() == replace.startOffset()) + && newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM)) + { + positionalSynonymSequencesSet.add(tokenSequence); + newEntry.add(replace); + tokenFoundSequence = true; + } + else if (newEntry.getLast().type().equals(CommonGramsFilter.GRAM_TYPE)) + { + if (newEntry.toString().endsWith(replace.toString())) + { + // already in the gram + positionalSynonymSequencesSet.add(tokenSequence); + tokenFoundSequence = true; + } + else + { + // need to replace the synonym in the current + // gram + tokenFoundSequence = true; + StringBuffer old = new StringBuffer(newEntry.getLast().toString()); + old.replace(replace.startOffset() - newEntry.getLast().startOffset(), + replace.endOffset() - newEntry.getLast().startOffset(), replace.toString()); + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(old.toString()); + newToken.setOffset(newEntry.getLast().startOffset(), newEntry.getLast().endOffset()); + newEntry.removeLast(); + newEntry.add(newToken); + } + } + } + else if ((newEntry.getLast().startOffset() < replace.startOffset()) + && (newEntry.getLast().endOffset() < replace.endOffset())) + { + if (newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM) + && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) + { + positionalSynonymSequencesSet.add(tokenSequence); + } + newEntry.add(replace); + tokenFoundSequence = true; + } + newAllTokenSequencesSet.add(newEntry); + } + if (false == tokenFoundSequence) + { + for (LinkedList tokenSequence : newAllTokenSequencesSet) + { + LinkedList newEntry = new LinkedList(); + newEntry.addAll(tokenSequence); + if ((newEntry.getLast().endOffset() == replace.endOffset()) + && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) + { + if ((newEntry.getLast().startOffset() == replace.startOffset()) + && newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM)) + { + positionalSynonymSequencesSet.add(tokenSequence); + newEntry.add(replace); + tokenFoundSequence = true; + } else if (newEntry.getLast().type().equals(CommonGramsFilter.GRAM_TYPE)) + { + if (newEntry.toString().endsWith(replace.toString())) + { + // already in the gram + positionalSynonymSequencesSet.add(tokenSequence); + tokenFoundSequence = true; + } else + { + // need to replace the synonym in the + // current gram + tokenFoundSequence = true; + StringBuffer old = new StringBuffer(newEntry.getLast().toString()); + old.replace(replace.startOffset() - newEntry.getLast().startOffset(), + replace.endOffset() - newEntry.getLast().startOffset(), replace.toString()); + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(old.toString()); + newToken.setOffset(newEntry.getLast().startOffset(), + newEntry.getLast().endOffset()); + newEntry.removeLast(); + newEntry.add(newToken); + positionalSynonymSequencesSet.add(newEntry); + } + } + } else if ((newEntry.getLast().startOffset() < replace.startOffset()) + && (newEntry.getLast().endOffset() < replace.endOffset())) + { + if (newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM) + && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) + { + positionalSynonymSequencesSet.add(tokenSequence); + newEntry.add(replace); + tokenFoundSequence = true; + } + } + } + } + if (false == tokenFoundSequence) + { + LinkedList newEntry = new LinkedList(); + newEntry.add(replace); + newAllTokenSequencesSet.add(newEntry); + } + // Limit the max number of permutations we consider + if (newAllTokenSequencesSet.size() > 64) + { + break FOR_FIRST_TOKEN_AT_POSITION_ONLY; + } + } + allTokenSequencesSet = newAllTokenSequencesSet; + allTokenSequencesSet.addAll(positionalSynonymSequencesSet); + + } + + LinkedList> allTokenSequences = new LinkedList>( + allTokenSequencesSet); + + // build the unique + + LinkedList> fixedTokenSequences = new LinkedList>(); + for (LinkedList tokenSequence : allTokenSequences) + { + LinkedList fixedTokenSequence = new LinkedList(); + fixedTokenSequences.add(fixedTokenSequence); + PackedTokenAttributeImpl replace = null; + for (PackedTokenAttributeImpl c : tokenSequence) + { + if (replace == null) + { + StringBuilder prefix = new StringBuilder(); + for (int i = c.startOffset() - 1; i >= 0; i--) + { + char test = testText.charAt(i); + if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) + { + prefix.insert(0, test); + } else + { + break; + } + } + String pre = prefix.toString(); + if (requiresMLTokenDuplication) + { + String termText = c.toString(); + int position = termText.indexOf("}"); + String language = termText.substring(0, position + 1); + String token = termText.substring(position + 1); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(language + pre + token); + replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); + replace.setType(c.type()); + replace.setPositionIncrement(c.getPositionIncrement()); + } else + { + String termText = c.toString(); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(pre + termText); + replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); + replace.setType(c.type()); + replace.setPositionIncrement(c.getPositionIncrement()); + } + } else + { + StringBuilder prefix = new StringBuilder(); + StringBuilder postfix = new StringBuilder(); + StringBuilder builder = prefix; + for (int i = c.startOffset() - 1; i >= replace.endOffset(); i--) + { + char test = testText.charAt(i); + if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) + { + builder.insert(0, test); + } else + { + builder = postfix; + postfix.setLength(0); + } + } + String pre = prefix.toString(); + String post = postfix.toString(); + + // Does it bridge? + if ((pre.length() > 0) && (replace.endOffset() + pre.length()) == c.startOffset()) + { + String termText = c.toString(); + if (requiresMLTokenDuplication) + { + int position = termText.indexOf("}"); + @SuppressWarnings("unused") + String language = termText.substring(0, position + 1); + String token = termText.substring(position + 1); + int oldPositionIncrement = replace.getPositionIncrement(); + String replaceTermText = replace.toString(); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(replaceTermText + pre + token); + replace.setOffset(replace.startOffset(), c.endOffset()); + replace.setType(replace.type()); + replace.setPositionIncrement(oldPositionIncrement); + } else + { + int oldPositionIncrement = replace.getPositionIncrement(); + String replaceTermText = replace.toString(); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(replaceTermText + pre + termText); + replace.setOffset(replace.startOffset(), c.endOffset()); + replace.setType(replace.type()); + replace.setPositionIncrement(oldPositionIncrement); + } + } else + { + String termText = c.toString(); + if (requiresMLTokenDuplication) + { + int position = termText.indexOf("}"); + String language = termText.substring(0, position + 1); + String token = termText.substring(position + 1); + String replaceTermText = replace.toString(); + PackedTokenAttributeImpl last = new PackedTokenAttributeImpl(); + last.setEmpty().append(replaceTermText + post); + last.setOffset(replace.startOffset(), replace.endOffset() + post.length()); + last.setType(replace.type()); + last.setPositionIncrement(replace.getPositionIncrement()); + fixedTokenSequence.add(last); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(language + pre + token); + replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); + replace.setType(c.type()); + replace.setPositionIncrement(c.getPositionIncrement()); + } else + { + String replaceTermText = replace.toString(); + PackedTokenAttributeImpl last = new PackedTokenAttributeImpl(); + last.setEmpty().append(replaceTermText + post); + last.setOffset(replace.startOffset(), replace.endOffset() + post.length()); + last.setType(replace.type()); + last.setPositionIncrement(replace.getPositionIncrement()); + fixedTokenSequence.add(last); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(pre + termText); + replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); + replace.setType(c.type()); + replace.setPositionIncrement(c.getPositionIncrement()); + } + } + } + } + // finish last + if (replace != null) + { + StringBuilder postfix = new StringBuilder(); + if ((replace.endOffset() >= 0) && (replace.endOffset() < testText.length())) + { + for (int i = replace.endOffset(); i < testText.length(); i++) + { + char test = testText.charAt(i); + if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) + { + postfix.append(test); + } else + { + break; + } + } + } + String post = postfix.toString(); + int oldPositionIncrement = replace.getPositionIncrement(); + String replaceTermText = replace.toString(); + PackedTokenAttributeImpl terminal = new PackedTokenAttributeImpl(); + terminal.setEmpty().append(replaceTermText + post); + terminal.setOffset(replace.startOffset(), replace.endOffset() + post.length()); + terminal.setType(replace.type()); + terminal.setPositionIncrement(oldPositionIncrement); + fixedTokenSequence.add(terminal); + } + } + + // rebuild fixed list + + ArrayList fixed = new ArrayList(); + for (LinkedList tokenSequence : fixedTokenSequences) + { + for (PackedTokenAttributeImpl token : tokenSequence) + { + fixed.add(token); + } + } + + // reorder by start position and increment + + Collections.sort(fixed, new Comparator() + { + + public int compare(PackedTokenAttributeImpl o1, PackedTokenAttributeImpl o2) + { + int dif = o1.startOffset() - o2.startOffset(); + if (dif != 0) + { + return dif; + } else + { + return o1.getPositionIncrement() - o2.getPositionIncrement(); + } + } + }); + + // make sure we remove any tokens we have duplicated + + @SuppressWarnings("rawtypes") + OrderedHashSet unique = new OrderedHashSet(); + unique.addAll(fixed); + fixed = new ArrayList(unique); + + list = fixed; + + // add any missing locales back to the tokens + + if (localePrefix.length() > 0) + { + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl currentToken = list.get(j); + String termText = currentToken.toString(); + currentToken.setEmpty(); + currentToken.append(localePrefix + termText); + } + } + + SchemaField sf = schema.getField(field); + + boolean isShingled = false; + @SuppressWarnings("resource") + TokenizerChain tokenizerChain = (sf.getType().getQueryAnalyzer() instanceof TokenizerChain) + ? ((TokenizerChain) sf.getType().getQueryAnalyzer()) : null; + if (tokenizerChain != null) + { + for (TokenFilterFactory factory : tokenizerChain.getTokenFilterFactories()) + { + if (factory instanceof ShingleFilterFactory) + { + isShingled = true; + break; + } + } + } + @SuppressWarnings("resource") + AlfrescoAnalyzerWrapper analyzerWrapper = (sf.getType().getQueryAnalyzer() instanceof AlfrescoAnalyzerWrapper) + ? ((AlfrescoAnalyzerWrapper) sf.getType().getQueryAnalyzer()) : null; + if (analyzerWrapper != null) + { + // assume if there are no term positions it is shingled .... + isShingled = true; + } + + boolean forceConjuncion = (rerankPhase == RerankPhase.QUERY_PHASE && !isExactTermSearch(analysisMode)); + if (list.size() == 0) + { + return null; + } + else if (list.size() == 1) + { + nextToken = list.get(0); + String termText = nextToken.toString(); + if (!isNumeric && (termText.contains("*") || termText.contains("?"))) + { + return newWildcardQuery(new Term(field, termText)); + } + else + { + return newTermQuery(new Term(field, termText)); + } + } + else + { + if (severalTokensAtSamePosition) + { + if (positionCount == 1) + { + // no phrase query: + Builder q = newBooleanQuery(); + for (int i = 0; i < list.size(); i++) + { + Query currentQuery; + nextToken = list.get(i); + String termText = nextToken.toString(); + if (termText.contains("*") || termText.contains("?")) + { + currentQuery = newWildcardQuery(new Term(field, termText)); + } else + { + currentQuery = newTermQuery(new Term(field, termText)); + } + q.add(currentQuery, BooleanClause.Occur.SHOULD); + } + return q.build(); + } + else if (forceConjuncion) + { + BooleanQuery.Builder or = new BooleanQuery.Builder(); + + for (LinkedList tokenSequence : fixedTokenSequences) + { + BooleanQuery.Builder and = new BooleanQuery.Builder(); + for (int i = 0; i < tokenSequence.size(); i++) + { + nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); + String termText = nextToken.toString(); + + Term term = new Term(field, termText); + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + and.add(wildQuery, Occur.MUST); + } else + { + TermQuery termQuery = new TermQuery(term); + and.add(termQuery, Occur.MUST); + } + } + if (and.build().clauses().size() > 0) + { + or.add(and.build(), Occur.SHOULD); + } + } + return or.build(); + } + // shingle + else if (sf.omitPositions() && isShingled) + { + + ArrayList nonContained = getNonContained(list); + Query currentQuery; + + BooleanQuery.Builder weakPhrase = new BooleanQuery.Builder(); + for (PackedTokenAttributeImpl shingleToken : nonContained) + { + String termText = shingleToken.toString(); + Term term = new Term(field, termText); + + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + currentQuery = new org.apache.lucene.search.WildcardQuery(term); + } else + { + currentQuery = new TermQuery(term); + } + weakPhrase.add(currentQuery, Occur.MUST); + } + + return weakPhrase.build(); + + } + // Word delimiter factory and other odd things generate complex + // token patterns + // Smart skip token sequences with small tokens that generate + // toomany wildcards + // Fall back to the larger pattern + // e.g Site1* will not do (S ite 1*) or (Site 1*) if 1* matches + // too much (S ite1*) and (Site1*) will still be OK + // If we skip all (for just 1* in the input) this is still an + // issue. + else + { + + return generateSpanOrQuery(field, fixedTokenSequences); + + } + } + else + { + if (forceConjuncion) + { + BooleanQuery.Builder or = new BooleanQuery.Builder(); + + for (LinkedList tokenSequence : fixedTokenSequences) + { + BooleanQuery.Builder and = new BooleanQuery.Builder(); + for (int i = 0; i < tokenSequence.size(); i++) + { + nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); + String termText = nextToken.toString(); + + Term term = new Term(field, termText); + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + and.add(wildQuery, Occur.MUST); + } + else + { + TermQuery termQuery = new TermQuery(term); + and.add(termQuery, Occur.MUST); + } + } + if (and.build().clauses().size() > 0) + { + or.add(and.build(), Occur.SHOULD); + } + } + return or.build(); + } + else + { + SpanQuery spanQuery = null; + ArrayList atSamePositionSpanOrQueryParts = new ArrayList(); + int gap = 0; + for (int i = 0; i < list.size(); i++) + { + nextToken = list.get(i); + String termText = nextToken.toString(); + Term term = new Term(field, termText); + if (getEnablePositionIncrements()) + { + SpanQuery nextSpanQuery; + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( + wildQuery); + wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); + nextSpanQuery = wrapper; + } + else + { + nextSpanQuery = new SpanTermQuery(term); + } + if (gap == 0) + { + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } + else + { + if (atSamePositionSpanOrQueryParts.size() == 0) + { + if (spanQuery == null) + { + spanQuery = nextSpanQuery; + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, nextSpanQuery }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + } + else if (atSamePositionSpanOrQueryParts.size() == 1) + { + if (spanQuery == null) + { + spanQuery = atSamePositionSpanOrQueryParts.get(0); + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, + internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } + else + { + if (spanQuery == null) + { + spanQuery = new SpanOrQuery( + atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})); + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, + new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})) }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } + } + gap = nextToken.getPositionIncrement(); + } + else + { + SpanQuery nextSpanQuery; + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( + wildQuery); + wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); + nextSpanQuery = wrapper; + } + else + { + nextSpanQuery = new SpanTermQuery(term); + } + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(nextSpanQuery); + } + else + { + spanQuery = new SpanOrQuery(spanQuery, nextSpanQuery); + } + } + } + if (atSamePositionSpanOrQueryParts.size() == 0) + { + return spanQuery; + } + else if (atSamePositionSpanOrQueryParts.size() == 1) + { + if (spanQuery == null) + { + spanQuery = atSamePositionSpanOrQueryParts.get(0); + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, + internalSlop < 2); + } + return spanQuery; + } + else + { + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})); + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})) }, (gap - 1) + internalSlop, internalSlop < 2); + } + return spanQuery; + } + } + } + } + } + + /** + * @param list + * @return + */ + private ArrayList getNonContained(ArrayList list) + { + ArrayList nonContained = new ArrayList(); + NEXT_CANDIDATE: for (PackedTokenAttributeImpl candidate : list) + { + NEXT_TEST: for (PackedTokenAttributeImpl test : list) + { + if (candidate == test) + { + continue NEXT_TEST; + } else if ((test.startOffset() == candidate.startOffset()) + && (candidate.endOffset() == test.endOffset()) + && (test.toString().equals(candidate.toString()))) + { + continue NEXT_TEST; + } else if ((test.startOffset() <= candidate.startOffset()) + && (candidate.endOffset() <= test.endOffset()) + && (test.toString().contains(candidate.toString()))) + { + continue NEXT_CANDIDATE; + } + } + nonContained.add(candidate); + } + return nonContained; + } + + /** + * @param field + * @param fixedTokenSequences + * LinkedList> + * @return Query + */ + protected SpanQuery generateSpanOrQuery(String field, + LinkedList> fixedTokenSequences) + { + PackedTokenAttributeImpl nextToken; + ArrayList spanOrQueryParts = new ArrayList(); + + for (LinkedList tokenSequence : fixedTokenSequences) + { + int gap = 1; + SpanQuery spanQuery = null; + ArrayList atSamePositionSpanOrQueryParts = new ArrayList(); + + // MNT-13239: if all tokens's positions are incremented by one then + // create flat nearQuery + if (getEnablePositionIncrements() && isAllTokensSequentiallyShifted(tokenSequence)) + { + // there will be no tokens at same position + List wildWrappedList = new ArrayList(tokenSequence.size()); + for (PackedTokenAttributeImpl token : tokenSequence) + { + String termText = token.toString(); + Term term = new Term(field, termText); + SpanQuery nextSpanQuery = wrapWildcardTerms(term); + wildWrappedList.add(nextSpanQuery); + } + if (wildWrappedList.size() == 1) + { + spanQuery = wildWrappedList.get(0); + } else + { + spanQuery = new SpanNearQuery(wildWrappedList.toArray(new SpanQuery[wildWrappedList.size()]), 0, + true); + } + } else + { + for (int i = 0; i < tokenSequence.size(); i++) + { + nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); + String termText = nextToken.toString(); + + Term term = new Term(field, termText); + + if (getEnablePositionIncrements()) + { + SpanQuery nextSpanQuery = wrapWildcardTerms(term); + if (gap == 0) + { + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } else + { + if (atSamePositionSpanOrQueryParts.size() == 0) + { + if (spanQuery == null) + { + spanQuery = nextSpanQuery; + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, nextSpanQuery }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + } else if (atSamePositionSpanOrQueryParts.size() == 1) + { + if (spanQuery == null) + { + spanQuery = atSamePositionSpanOrQueryParts.get(0); + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, + internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } else + { + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})); + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, spanQuery = new SpanOrQuery( + atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})) }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } + } + gap = nextToken.getPositionIncrement(); + + } else + { + SpanQuery nextSpanQuery; + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( + wildQuery); + wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); + nextSpanQuery = wrapper; + } else + { + nextSpanQuery = new SpanTermQuery(term); + } + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(nextSpanQuery); + } else + { + spanQuery = new SpanOrQuery(spanQuery, nextSpanQuery); + } + } + } + } + + if (atSamePositionSpanOrQueryParts.size() == 0) + { + spanOrQueryParts.add(spanQuery); + } else if (atSamePositionSpanOrQueryParts.size() == 1) + { + if (spanQuery == null) + { + spanQuery = atSamePositionSpanOrQueryParts.get(0); + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + spanOrQueryParts.add(spanQuery); + } else + { + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})); + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})) }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + spanOrQueryParts.add(spanQuery); + } + } + if (spanOrQueryParts.size() == 1) + { + return spanOrQueryParts.get(0); + } else + { + return new SpanOrQuery(spanOrQueryParts.toArray(new SpanQuery[] + {})); + } + } + + private SpanQuery wrapWildcardTerms(org.apache.lucene.index.Term term) + { + String termText = term.text(); + SpanQuery nextSpanQuery; + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery(term); + SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( + wildQuery); + wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); + nextSpanQuery = wrapper; + } else + { + nextSpanQuery = new SpanTermQuery(term); + } + return nextSpanQuery; + } + + protected boolean isAllTokensSequentiallyShifted(List tokenSequence) + { + if (0 == internalSlop) + { + // check if all tokens have gap = 1 + for (PackedTokenAttributeImpl tokenToCheck : tokenSequence) + { + if (1 != tokenToCheck.getPositionIncrement()) + { + return false; + } + } + return true; + } + return false; + } + + private Set getWildcardPositions(String string) + { + HashSet wildcardPositions = new HashSet(); + + boolean lastWasEscape = false; + + for (int i = 0; i < string.length(); i++) + { + char c = string.charAt(i); + if (lastWasEscape) + { + lastWasEscape = false; + } else + { + if (c == '\\') + { + lastWasEscape = true; + } else if (c == '*') + { + wildcardPositions.add(i); + } else if (c == '?') + { + wildcardPositions.add(i); + } + } + } + + return wildcardPositions; + } + + /** + * @exception ParseException + * throw in overridden method to disallow + */ + + protected Query getRangeQuery(String field, String part1, String part2, boolean startInclusive, + boolean endInclusive) throws ParseException + { + return getRangeQuery(field, part1, part2, startInclusive, endInclusive, AnalysisMode.IDENTIFIER, + LuceneFunction.FIELD); + } + + protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException + { + return getRangeQuery(field, part1, part2, inclusive, inclusive, AnalysisMode.IDENTIFIER, LuceneFunction.FIELD); + } + + /** + * @param field + * @param part1 + * @param part2 + * @param includeLower + * @param includeUpper + * @param analysisMode + * @param luceneFunction + * @return the query + * @exception ParseException + * throw in overridden method to disallow + */ + public Query getRangeQuery(String field, String part1, String part2, boolean includeLower, boolean includeUpper, + AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATH); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + + return createDefaultTextQuery(textField -> getRangeQuery(textField, part1, part2, includeLower, + includeUpper, analysisMode, luceneFunction)); + + + } else if (field.equals(FIELD_CASCADETX)) + { + SchemaField sf = schema.getField(FIELD_CASCADETX); + if (sf != null) + { + String start = null; + try + { + analyzeMultitermTerm(FIELD_CASCADETX, part1); + start = part1; + } catch (Exception e) + { + + } + String end = null; + try + { + analyzeMultitermTerm(FIELD_CASCADETX, part2); + end = part2; + } catch (Exception e) + { + + } + return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); + } else + { + throw new UnsupportedOperationException(); + } + } + // FIELD_ID uses the default + // FIELD_DBID uses the default + // FIELD_ISROOT uses the default + // FIELD_ISCONTAINER uses the default + // FIELD_ISNODE uses the default + // FIELD_TX uses the default + // FIELD_PARENT uses the default + // FIELD_PRIMARYPARENT uses the default + // FIELD_QNAME uses the default + // FIELD_PRIMARYASSOCTYPEQNAME uses the default + // FIELD_ASSOCTYPEQNAME uses the default + // FIELD_CLASS uses the default + // FIELD_TYPE uses the default + // FIELD_EXACTTYPE uses the default + // FIELD_ASPECT uses the default + // FIELD_EXACTASPECT uses the default + // FIELD_TYPE uses the default + // FIELD_TYPE uses the default + if (isPropertyField(field)) + { + Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(field); + + String expandedFieldName = null; + PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, fieldNameAndEnding.getFirst()); + IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; + if (propertyDef != null) + { + tokenisationMode = propertyDef.getIndexTokenisationMode(); + if (tokenisationMode == null) + { + tokenisationMode = IndexTokenisationMode.TRUE; + } + } else + { + expandedFieldName = expandAttributeFieldName(field); + } + + if (propertyDef != null) + { + // LOWER AND UPPER + if (luceneFunction != LuceneFunction.FIELD) + { + if (luceneFunction == LuceneFunction.LOWER) + { + if ((false == part1.toLowerCase().equals(part1)) + || (false == part2.toLowerCase().equals(part2))) + { + return createNoMatchQuery(); + } + } + + if (luceneFunction == LuceneFunction.UPPER) + { + if ((false == part1.toUpperCase().equals(part1)) + || (false == part2.toUpperCase().equals(part2))) + { + return createNoMatchQuery(); + } + } + + if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + addLocaleSpecificUntokenisedTextRangeFunction(expandedFieldName, propertyDef, part1, part2, + includeLower, includeUpper, luceneFunction, booleanQuery, locale, tokenisationMode); + } + return booleanQuery.build(); + } else + { + throw new UnsupportedOperationException("Lucene Function"); + } + } + + if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) + { + return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, analysisMode, + expandedFieldName, propertyDef, tokenisationMode); + } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) + { + String solrField = null; + switch (fieldNameAndEnding.getSecond()) + { + case FIELD_SIZE_SUFFIX: + solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_SIZE, FieldUse.ID) + .getFields().get(0).getField(); + break; + case FIELD_MIMETYPE_SUFFIX: + solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_MIMETYPE, FieldUse.ID) + .getFields().get(0).getField(); + break; + case FIELD_ENCODING_SUFFIX: + solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_ENCODING, FieldUse.ID) + .getFields().get(0).getField(); + break; + case FIELD_LOCALE_SUFFIX: + solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_LOCALE, FieldUse.ID) + .getFields().get(0).getField(); + break; + } + if (solrField != null) + { + String start = null; + try + { + analyzeMultitermTerm(solrField, part1); + start = part1; + } catch (Exception e) + { + + } + String end = null; + try + { + analyzeMultitermTerm(solrField, part2); + end = part2; + } catch (Exception e) + { + + } + + SchemaField sf = schema.getField(solrField); + return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); + } else + { + return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, + analysisMode, expandedFieldName, propertyDef, tokenisationMode); + } + } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) + { + return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, analysisMode, + expandedFieldName, propertyDef, tokenisationMode); + } else if ((propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) + || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE)) && + !AlfrescoSolrDataModel.getInstance().isDerivedDateField(fieldNameAndEnding.getSecond())) + { + Pair dateAndResolution1 = parseDateString(part1); + Pair dateAndResolution2 = parseDateString(part2); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + IndexedField indexedField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), null, FieldUse.ID); + for (FieldInstance instance : indexedField.getFields()) + { + String start = dateAndResolution1 == null ? part1 + : (includeLower ? getDateStart(dateAndResolution1) : getDateEnd(dateAndResolution1)); + String end = dateAndResolution2 == null ? part2 + : (includeUpper ? getDateEnd(dateAndResolution2) : getDateStart(dateAndResolution2)); + if (start.equals("*")) + { + start = null; + } + if (end.equals("*")) + { + end = null; + } + + SchemaField sf = schema.getField(instance.getField()); + + Query query = sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); + if (query != null) + { + bQuery.add(query, Occur.SHOULD); + } + } + return bQuery.build(); + } + else + { + String solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), + AlfrescoSolrDataModel.getInstance().getTextField(fieldNameAndEnding.getSecond()), + FieldUse.ID) + .getFields().get(0).getField(); + + String start = null; + try + { + analyzeMultitermTerm(solrField, part1); + start = part1; + } catch (Exception e) + { + + } + String end = null; + try + { + analyzeMultitermTerm(solrField, part2); + end = part2; + } catch (Exception e) + { + + } + + SchemaField sf = schema.getField(solrField); + return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); + } + } else + { + throw new UnsupportedOperationException(); + } + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, + includeUpper, analysisMode, luceneFunction); + query.add(part, Occur.SHOULD); + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getRangeQuery(fieldName, part1, part2, includeLower, includeUpper, analysisMode, + luceneFunction); + query.add(part, Occur.SHOULD); + } + return query.build(); + } + + } + // FIELD_ISUNSET uses the default + // FIELD_ISNULL uses the default + // FIELD_ISNOTNULL uses the default + // FIELD_EXISTS uses the default + else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, + includeUpper, analysisMode, luceneFunction); + query.add(part, Occur.SHOULD); + } + return query.build(); + } + // FIELD_FTSSTATUS uses the default + if (field.equals(FIELD_TAG)) + { + throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_TAG); + } else + { + // None property - leave alone + throw new UnsupportedOperationException(); + } + } + + /** + * @param field + * @param part1 + * @param part2 + * @param includeLower + * @param includeUpper + * @param analysisMode + * @param expandedFieldName + * @param propertyDef + * @param tokenisationMode + * @return + * @throws ParseException + */ + private Query buildTextMLTextOrContentRange(String field, String part1, String part2, boolean includeLower, + boolean includeUpper, AnalysisMode analysisMode, String expandedFieldName, PropertyDefinition propertyDef, + IndexTokenisationMode tokenisationMode) throws ParseException + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + + try + { + addTextRange(field, propertyDef, part1, part2, includeLower, includeUpper, analysisMode, + expandedFieldName, propertyDef, tokenisationMode, booleanQuery, locale); + } catch (IOException e) + { + throw new ParseException( + "Failed to tokenise: <" + part1 + "> or <" + part2 + "> for " + propertyDef.getName()); + } + + } + return booleanQuery.build(); + } + + private String expandAttributeFieldName(String field) + { + return PROPERTY_FIELD_PREFIX + QueryParserUtils.expandQName(searchParameters.getNamespace(), + namespacePrefixResolver, field.substring(1)); + } + + // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) + @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") + protected String getToken(String field, String value, AnalysisMode analysisMode) throws ParseException + { + try (TokenStream source = getAnalyzer().tokenStream(field, new StringReader(value))) + { + String tokenised = null; + + while (source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if (source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if (source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + PackedTokenAttributeImpl token = new PackedTokenAttributeImpl(); + token.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); + token.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if (typeAtt != null) + { + token.setType(typeAtt.type()); + } + if (posIncAtt != null) + { + token.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + + tokenised = token.toString(); + } + return tokenised; + } catch (IOException e) + { + throw new ParseException("IO" + e.getMessage()); + } + + } + + @Override + public Query getPrefixQuery(String field, String termStr) throws ParseException + { + return getPrefixQuery(field, termStr, AnalysisMode.PREFIX); + } + + @SuppressWarnings("deprecation") + public Query getPrefixQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATH); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + + return createDefaultTextQuery(textField -> getPrefixQuery(textField, termStr, analysisMode)); + + } else if (field.equals(FIELD_ID)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getPrefixQuery(FIELD_LID, termStr); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) + || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) + || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) + || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getPrefixQuery(field, termStr); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_CLASS)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_CLASS); + } else if (field.equals(FIELD_TYPE)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_TYPE); + } else if (field.equals(FIELD_EXACTTYPE)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_EXACTTYPE); + } else if (field.equals(FIELD_ASPECT)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_ASPECT); + } else if (field.equals(FIELD_EXACTASPECT)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_EXACTASPECT); + } else if (isPropertyField(field)) + { + return attributeQueryBuilder(field, termStr, new PrefixQuery(), analysisMode, LuceneFunction.FIELD); + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc + // if required + Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getPrefixQuery(fieldName, termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } + } else if (field.equals(FIELD_ISUNSET)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISUNSET); + } else if (field.equals(FIELD_ISNULL)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNULL); + } else if (field.equals(FIELD_ISNOTNULL)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNOTNULL); + } else if (field.equals(FIELD_EXISTS)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_EXISTS); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc if + // required + Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else if (field.equals(FIELD_FTSSTATUS)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_FTSSTATUS); + } else if (field.equals(FIELD_TAG)) + { + return super.getPrefixQuery(field, termStr); + } else if (field.equals(FIELD_SITE)) + { + return super.getPrefixQuery(field, termStr); + } else if (field.equals(FIELD_NPATH)) + { + return super.getPrefixQuery(field, termStr); + } else if (field.equals(FIELD_PNAME)) + { + return super.getPrefixQuery(field, termStr); + } else + { + return super.getPrefixQuery(field, termStr); + } + } + + @Override + public Query getWildcardQuery(String field, String termStr) throws ParseException + { + return getWildcardQuery(field, termStr, AnalysisMode.WILD); + } + + @SuppressWarnings("deprecation") + public Query getWildcardQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_PATH); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + + return createDefaultTextQuery(textField -> getWildcardQuery(textField, termStr, analysisMode)); + + } else if (field.equals(FIELD_ID)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getWildcardQuery(FIELD_LID, termStr); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) + || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) + || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) + || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getWildcardQuery(field, termStr); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_CLASS)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_CLASS); + } else if (field.equals(FIELD_TYPE)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_TYPE); + } else if (field.equals(FIELD_EXACTTYPE)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_EXACTTYPE); + } else if (field.equals(FIELD_ASPECT)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_ASPECT); + } else if (field.equals(FIELD_EXACTASPECT)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_EXACTASPECT); + } else if (isPropertyField(field)) + { + return attributeQueryBuilder(field, termStr, new WildcardQuery(), analysisMode, LuceneFunction.FIELD); + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc + // if required + Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getWildcardQuery(fieldName, termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } + } else if (field.equals(FIELD_ISUNSET)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISUNSET); + } else if (field.equals(FIELD_ISNULL)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNULL); + } else if (field.equals(FIELD_ISNOTNULL)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNOTNULL); + } else if (field.equals(FIELD_EXISTS)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_EXISTS); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc if + // required + Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else if (field.equals(FIELD_FTSSTATUS)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_FTSSTATUS); + } else if (field.equals(FIELD_TAG)) + { + return super.getWildcardQuery(field, termStr); + } else if (field.equals(FIELD_SITE)) + { + return super.getWildcardQuery(field, termStr); + } else if (field.equals(FIELD_PNAME)) + { + return super.getWildcardQuery(field, termStr); + } else if (field.equals(FIELD_NPATH)) + { + return super.getWildcardQuery(field, termStr); + } else + { + return super.getWildcardQuery(field, termStr); + } + } + + @SuppressWarnings("deprecation") + @Override + public Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATH); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + + return createDefaultTextQuery(textField -> getFuzzyQuery(textField, termStr, minSimilarity)); + + } else if (field.equals(FIELD_ID) || field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) + || field.equals(FIELD_ISCONTAINER) || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) + || field.equals(FIELD_PARENT) || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) + || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getFuzzyQuery(field, termStr, minSimilarity); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_CLASS)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_CLASS); + } else if (field.equals(FIELD_TYPE)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_TYPE); + } else if (field.equals(FIELD_EXACTTYPE)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTTYPE); + } else if (field.equals(FIELD_ASPECT)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ASPECT); + } else if (field.equals(FIELD_EXACTASPECT)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTASPECT); + } else if (isPropertyField(field)) + { + return attributeQueryBuilder(field, termStr, new FuzzyQuery(minSimilarity), AnalysisMode.FUZZY, + LuceneFunction.FIELD); + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc + // if required + Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getFuzzyQuery(fieldName, termStr, minSimilarity); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } + } else if (field.equals(FIELD_ISUNSET)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISUNSET); + } else if (field.equals(FIELD_ISNULL)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNULL); + } else if (field.equals(FIELD_ISNOTNULL)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNOTNULL); + } else if (field.equals(FIELD_EXISTS)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXISTS); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc if + // required + Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else if (field.equals(FIELD_FTSSTATUS)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_FTSSTATUS); + } else if (field.equals(FIELD_TAG)) + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } else if (field.equals(FIELD_SITE)) + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } else if (field.equals(FIELD_PNAME)) + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } else if (field.equals(FIELD_NPATH)) + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } else + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } + } + + /** + * @param dictionaryService + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param field + * @param queryText + * @param analysisMode + * @param luceneFunction + * @return the query + * @throws ParseException + */ + public Query getSuperFieldQuery(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(field, queryText, analysisMode, luceneFunction); + } + + /** + * @param field + * @param termStr + * @param minSimilarity + * @return the query + * @throws ParseException + */ + public Query getSuperFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } + + /** + * @param field + * @param termStr + * @return the query + * @throws ParseException + */ + public Query getSuperPrefixQuery(String field, String termStr) throws ParseException + { + return super.getPrefixQuery(field, termStr); + } + + /** + * @param field + * @param termStr + * @return the query + * @throws ParseException + */ + public Query getSuperWildcardQuery(String field, String termStr) throws ParseException + { + return super.getWildcardQuery(field, termStr); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.lucene.queryParser.QueryParser#newWildcardQuery(org.apache. + * lucene.index.Term) + */ + @Override + protected Query newWildcardQuery(Term t) + { + if (t.text().equals("*")) + { + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + bQuery.add(createTermQuery(FIELD_FIELDS, t.field()), Occur.SHOULD); + bQuery.add(createTermQuery(FIELD_PROPERTIES, t.field()), Occur.SHOULD); + return bQuery.build(); + } else if (t.text().contains("\\")) + { + String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, + SearchLanguageConversion.DEF_REGEX, t.text()); + return new RegexpQuery(new Term(t.field(), regexp)); + } else + { + org.apache.lucene.search.WildcardQuery query = new org.apache.lucene.search.WildcardQuery(t); + query.setRewriteMethod(new MultiTermQuery.TopTermsScoringBooleanQueryRewrite(topTermSpanRewriteLimit)); + return query; + } + } + + /* + * (non-Javadoc) + * + * @see org.apache.lucene.queryParser.QueryParser#newPrefixQuery(org.apache. + * lucene.index.Term) + */ + @Override + protected Query newPrefixQuery(Term prefix) + { + if (prefix.text().contains("\\")) + { + String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, + SearchLanguageConversion.DEF_REGEX, prefix.text()); + return new RegexpQuery(new Term(prefix.field(), regexp)); + } else + { + return super.newPrefixQuery(prefix); + } + + } + + public interface SubQuery + { + /** + * @param field + * @param queryText + * @param analysisMode + * @param luceneFunction + * @return the query + * @throws ParseException + */ + Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException; + } + + class FieldQuery implements SubQuery + { + public Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return getSuperFieldQuery(field, queryText, analysisMode, luceneFunction); + } + } + + class FuzzyQuery implements SubQuery + { + float minSimilarity; + + FuzzyQuery(float minSimilarity) + { + this.minSimilarity = minSimilarity; + } + + public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return getSuperFuzzyQuery(field, translateLocale(termStr), minSimilarity); + } + } + + private String translateLocale(String localised) + { + if (localised.startsWith("\u0000")) + { + if (localised.startsWith("\u0000\u0000")) + { + if (localised.length() < 3) + { + return ""; + } else + { + return localised.substring(2); + } + } else + { + int end = localised.indexOf('\u0000', 1); + if (end == -1) + { + return localised; + } else + { + StringBuilder buffer = new StringBuilder(); + buffer.append("{"); + buffer.append(localised.substring(1, end)); + buffer.append("}"); + buffer.append(localised.substring(end + 1)); + return buffer.toString(); + } + } + } else + { + return localised; + } + } + + class PrefixQuery implements SubQuery + { + public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + StringBuilder builder = new StringBuilder(termStr.length() + 1); + builder.append(termStr); + builder.append("*"); + return getSuperFieldQuery(field, builder.toString(), analysisMode, luceneFunction); + // return getSuperPrefixQuery(field, termStr); + } + } + + class WildcardQuery implements SubQuery + { + public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return getSuperFieldQuery(field, termStr, analysisMode, luceneFunction); + // return getSuperWildcardQuery(field, termStr); + } + } + + private Query spanQueryBuilder(String field, String first, String last, int slop, boolean inOrder) + throws ParseException + { + String propertyFieldName = field.substring(1); + String expandedFieldName = null; + + PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, propertyFieldName); + IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; + if (propertyDef != null) + { + tokenisationMode = propertyDef.getIndexTokenisationMode(); + if (tokenisationMode == null) + { + tokenisationMode = IndexTokenisationMode.TRUE; + } + } else + { + expandedFieldName = expandAttributeFieldName(field); + } + + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) + { + // Build a sub query for each locale and or the results together - + // the analysis will take care of + // cross language matching for each entry + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + addMLTextSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, propertyDef, + tokenisationMode, booleanQuery, locale); + } + return booleanQuery.build(); + } + // Content + else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales + .addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); + } + + return addContentSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, + expandedLocales); + + } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + + addTextSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, + booleanQuery, locale); + + } + return booleanQuery.build(); + } else + { + throw new UnsupportedOperationException( + "Span queries are only supported for d:text, d:mltext and d:content data types"); + } + } + + private Query attributeQueryBuilder(String field, String queryText, SubQuery subQueryBuilder, + AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException + { + // TODO: Fix duplicate token generation for mltext, content and text. + // -locale expansion here and in tokeisation -> duplicates + + // Get type info etc + + // TODO: additional suffixes + + Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(field); + + String expandedFieldName = null; + QName propertyQName; + PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, fieldNameAndEnding.getFirst()); + IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; + if (propertyDef != null) + { + tokenisationMode = propertyDef.getIndexTokenisationMode(); + if (tokenisationMode == null) + { + tokenisationMode = IndexTokenisationMode.TRUE; + } + propertyQName = propertyDef.getName(); + } else + { + expandedFieldName = expandAttributeFieldName(field); + propertyQName = QName.createQName(fieldNameAndEnding.getFirst()); + } + + if (isAllStar(queryText)) + { + return createTermQuery(FIELD_PROPERTIES, propertyQName.toString()); + } + + if (luceneFunction != LuceneFunction.FIELD) + { + if ((tokenisationMode == IndexTokenisationMode.FALSE) || (tokenisationMode == IndexTokenisationMode.BOTH)) + { + if (luceneFunction == LuceneFunction.LOWER) + { + if (false == queryText.toLowerCase().equals(queryText)) + { + return createNoMatchQuery(); + } + } + if (luceneFunction == LuceneFunction.UPPER) + { + if (false == queryText.toUpperCase().equals(queryText)) + { + return createNoMatchQuery(); + } + } + + return functionQueryBuilder(expandedFieldName, fieldNameAndEnding.getSecond(), propertyQName, + propertyDef, tokenisationMode, queryText, luceneFunction); + } + } + + // Mime type + if (fieldNameAndEnding.getSecond().equals(FIELD_MIMETYPE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_MIMETYPE, FieldUse.ID).getFields().get(0) + .getField(), queryText, analysisMode, luceneFunction); + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_SIZE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_SIZE, FieldUse.ID).getFields().get(0) + .getField(), queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_LOCALE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_LOCALE, FieldUse.ID).getFields().get(0) + .getField(), queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_ENCODING_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_ENCODING, FieldUse.ID).getFields().get(0) + .getField(), queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_STATUS_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder + .getQuery( + AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_STATUS, + FieldUse.ID) + .getFields().get(0).getField(), + queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_TIME_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder + .getQuery( + AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_TIME, + FieldUse.ID) + .getFields().get(0).getField(), + queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery( + AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_EXCEPTION, + FieldUse.ID) + .getFields().get(0).getField(), + queryText, analysisMode, luceneFunction); + + } + + } + else if (AlfrescoSolrDataModel.getInstance().isDerivedDateField(fieldNameAndEnding.getSecond())) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE) + || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME ))) + { + return subQueryBuilder.getQuery( + AlfrescoSolrDataModel.getInstance() + .getDateDerivedField(propertyQName, fieldNameAndEnding.getSecond()), + queryText, analysisMode, luceneFunction); + } + } + + // Already in expanded form + + // ML + + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) + { + // Build a sub query for each locale and or the results together - + // the analysis will take care of + // cross language matching for each entry + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + addMLTextAttributeQuery(field, propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, propertyDef, tokenisationMode, booleanQuery, locale); + } + return getNonEmptyBooleanQuery(booleanQuery.build()); + } + // Content + else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + // Identifier request are ignored for content + + // Build a sub query for each locale and or the results together - + // - add an explicit condition for the locale + + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales + .addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); + } + + return addContentAttributeQuery(propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, expandedLocales); + + } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) + { + // if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) || + // propertyQName.equals(ContentModel.PROP_USERNAME) || + // propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) + // { + // // nasty work around for solr support for user and group look up + // as we can not support lowercased identifiers in the model + // if(isLucene()) + // { + // return subQueryBuilder.getQuery(expandedFieldName, queryText, + // analysisMode, luceneFunction); + // } + // } + + boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) + || propertyQName.equals(ContentModel.PROP_USERNAME) + || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); + + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + Locale fixedLocale = locale; + if (fixedLocale.getLanguage().equals("*")) + { + fixedLocale = new Locale("??"); + } + addTextAttributeQuery(field, propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, tokenisationMode, booleanQuery, fixedLocale); + + } + return getNonEmptyBooleanQuery(booleanQuery.build()); + } else + { + // Date does not support like + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME))) + { + if (analysisMode == AnalysisMode.LIKE) + { + throw new UnsupportedOperationException("Wild cards are not supported for the datetime type"); + } + } + + // expand date for loose date parsing + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) + || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE))) + { + Pair dateAndResolution = parseDateString(queryText); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + IndexedField indexedField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), null, FieldUse.FTS); + for (FieldInstance instance : indexedField.getFields()) + { + if (dateAndResolution != null) + { + Query query = newRangeQuery(instance.getField(), getDateStart(dateAndResolution), + getDateEnd(dateAndResolution), true, true); + if (query != null) + { + bQuery.add(query, Occur.SHOULD); + } + } else + { + Query query = subQueryBuilder.getQuery(instance.getField(), queryText, AnalysisMode.DEFAULT, + luceneFunction); + if (query != null) + { + bQuery.add(query, Occur.SHOULD); + } + } + } + if (bQuery.build().clauses().size() > 0) + { + return bQuery.build(); + } else + { + return createNoMatchQuery(); + } + } + + if ((propertyDef != null) && (tenantService.isTenantUser()) + && (propertyDef.getDataType().getName().equals(DataTypeDefinition.NODE_REF)) + && (queryText.contains(StoreRef.URI_FILLER))) + { + // ALF-6202 + queryText = tenantService.getName(new NodeRef(queryText)).toString(); + } + + // Sort and id is only special for MLText, text, and content + // Dates are not special in this case + if (propertyDef != null) + { + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + IndexedField indexedField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), null, FieldUse.FTS); + for (FieldInstance instance : indexedField.getFields()) + { + Query query = subQueryBuilder.getQuery(instance.getField(), queryText, AnalysisMode.DEFAULT, + luceneFunction); + if (query != null) + { + bQuery.add(query, Occur.SHOULD); + } + } + if (bQuery.build().clauses().size() > 0) + { + return bQuery.build(); + } else + { + return createNoMatchQuery(); + } + } else + { + Query query = subQueryBuilder.getQuery(expandedFieldName, queryText, AnalysisMode.DEFAULT, + luceneFunction); + if (query != null) + { + return query; + } else + { + return createNoMatchQuery(); + } + } + } + } + + /** + * @param queryText + * @return + */ + private boolean isAllStar(String queryText) + { + if ((queryText == null) || (queryText.length() == 0)) + { + return false; + } + for (char c : queryText.toCharArray()) + { + if (c != '*') + { + return false; + } + } + return true; + + } + + /** + * @param dateAndResolution + * @return + */ + private String getDateEnd(Pair dateAndResolution) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.setTime(dateAndResolution.getFirst()); + switch (dateAndResolution.getSecond()) + { + case Calendar.YEAR: + cal.set(Calendar.MONTH, cal.getActualMaximum(Calendar.MONTH)); + case Calendar.MONTH: + cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); + case Calendar.DAY_OF_MONTH: + cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY)); + case Calendar.HOUR_OF_DAY: + cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE)); + case Calendar.MINUTE: + cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND)); + case Calendar.SECOND: + cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND)); + case Calendar.MILLISECOND: + default: + } + SimpleDateFormat formatter = CachingDateFormat.getSolrDatetimeFormat(); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + return formatter.format(cal.getTime()); + } + + /** + * Returns the formatted start (i.e. lower bound) of a range query. + */ + private String getDateStart(Pair dateAndResolution) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.setTime(dateAndResolution.getFirst()); + switch (dateAndResolution.getSecond()) + { + case Calendar.YEAR: + cal.set(Calendar.MONTH, cal.getActualMinimum(Calendar.MONTH)); + case Calendar.MONTH: + cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH)); + case Calendar.DAY_OF_MONTH: + cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); + case Calendar.HOUR_OF_DAY: + cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE)); + case Calendar.MINUTE: + cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND)); + case Calendar.SECOND: + cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND)); + case Calendar.MILLISECOND: + default: + } + return CachingDateFormat.getSolrDatetimeFormat().format(cal.getTime()); + } + + private Pair parseDateString(String dateString) + { + try + { + return CachingDateFormat.lenientParse(dateString, Calendar.YEAR); + } catch (java.text.ParseException e) + { + SimpleDateFormat oldDf = CachingDateFormat.getDateFormat(); + oldDf.setTimeZone(TimeZone.getTimeZone("UTC")); + try + { + Date date = oldDf.parse(dateString); + return new Pair<>(date, Calendar.SECOND); + } catch (java.text.ParseException ee) + { + if (dateString.equalsIgnoreCase("min")) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.set(Calendar.YEAR, cal.getMinimum(Calendar.YEAR)); + cal.set(Calendar.DAY_OF_YEAR, cal.getMinimum(Calendar.DAY_OF_YEAR)); + cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY)); + cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE)); + cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND)); + cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND)); + return new Pair(cal.getTime(), Calendar.MILLISECOND); + } else if (dateString.equalsIgnoreCase("now")) + { + return new Pair(new Date(), Calendar.MILLISECOND); + } else if (dateString.equalsIgnoreCase("today")) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.setTime(new Date()); + cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY)); + cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE)); + cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND)); + cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND)); + return new Pair(cal.getTime(), Calendar.DAY_OF_MONTH); + } else if (dateString.equalsIgnoreCase("max")) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.set(Calendar.YEAR, cal.getMaximum(Calendar.YEAR)); + cal.set(Calendar.DAY_OF_YEAR, cal.getMaximum(Calendar.DAY_OF_YEAR)); + cal.set(Calendar.HOUR_OF_DAY, cal.getMaximum(Calendar.HOUR_OF_DAY)); + cal.set(Calendar.MINUTE, cal.getMaximum(Calendar.MINUTE)); + cal.set(Calendar.SECOND, cal.getMaximum(Calendar.SECOND)); + cal.set(Calendar.MILLISECOND, cal.getMaximum(Calendar.MILLISECOND)); + return new Pair(cal.getTime(), Calendar.MILLISECOND); + } else + { + return null; // delegate to SOLR date parsing + } + } + } + } + + protected Query functionQueryBuilder(String expandedFieldName, String ending, QName propertyQName, + PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, String queryText, + LuceneFunction luceneFunction) throws ParseException + { + + // Mime type + if (ending.equals(FIELD_MIMETYPE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_SIZE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_LOCALE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_ENCODING_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_CONTENT_DOC_ID_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_TRANSFORMATION_TIME_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_TRANSFORMATION_STATUS_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } + + // Already in expanded form + + // ML + + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) + { + // Build a sub query for each locale and or the results together - + // the analysis will take care of + // cross language matching for each entry + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + + addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, propertyDef, queryText, luceneFunction, + booleanQuery, locale, tokenisationMode); + + } + return booleanQuery.build(); + } + // Content + else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene functions not supported for content"); + } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) + { + if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) + || propertyQName.equals(ContentModel.PROP_USERNAME) + || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) + { + throw new UnsupportedOperationException("Functions are not supported agaisnt special text fields"); + } + + boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) + || propertyQName.equals(ContentModel.PROP_USERNAME) + || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); + + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, propertyDef, queryText, luceneFunction, + booleanQuery, locale, tokenisationMode); + + } + return booleanQuery.build(); + } else + { + throw new UnsupportedOperationException("Lucene Function"); + } + } + + protected TermQuery createNoMatchQuery() + { + return new TermQuery(new Term("NO_TOKENS", "__")); + } + + /** + * Returns null if all clause words were filtered away by the analyzer + * + * @param booleanQuery + * - initial BooleanQuery + * @return BooleanQuery or null if booleanQuery has no clauses + */ + protected BooleanQuery getNonEmptyBooleanQuery(BooleanQuery booleanQuery) + { + if (booleanQuery.clauses().size() > 0) + { + return booleanQuery; + } else + { + return null; + } + } + + protected Query createSolr4IdQuery(String queryText) + { + return createTermQuery(FIELD_SOLR4_ID, queryText); + } + + // Previous SOLR + + protected Query createIdQuery(String queryText) + { + if (NodeRef.isNodeRef(queryText)) + { + return createNodeRefQuery(FIELD_LID, queryText); + } else + { + return createNodeRefQuery(FIELD_ID, queryText); + } + } + + protected Query createPathQuery(String queryText, boolean withRepeats) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse(queryText); + SolrPathQuery pathQuery = handler.getQuery(); + pathQuery.setRepeats(withRepeats); + return new SolrCachingPathQuery(pathQuery); + } + + protected Query createQNameQuery(String queryText) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse("//" + queryText); + SolrPathQuery pathQuery = handler.getQuery(); + return new SolrCachingPathQuery(pathQuery); + } + + protected Query createPrimaryAssocQNameQuery(String queryText) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse("//" + queryText); + SolrPathQuery pathQuery = handler.getQuery(); + pathQuery.setPathField(FIELD_PRIMARYASSOCQNAME); + return new SolrCachingPathQuery(pathQuery); + } + + protected Query createPrimaryAssocTypeQNameQuery(String queryText) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse("//" + queryText); + SolrPathQuery pathQuery = handler.getQuery(); + pathQuery.setPathField(FIELD_PRIMARYASSOCTYPEQNAME); + return new SolrCachingPathQuery(pathQuery); + } + + protected Query createAssocTypeQNameQuery(String queryText) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse("//" + queryText); + SolrPathQuery pathQuery = handler.getQuery(); + pathQuery.setPathField(FIELD_ASSOCTYPEQNAME); + return new SolrCachingPathQuery(pathQuery); + } + + /** + * @param queryText + * @return + */ + protected Query createAclIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + /** + * @param queryText + * @return + */ + protected Query createOwnerQuery(String queryText) throws ParseException + { + return new SolrOwnerQuery(queryText); + } + + /** + * @param queryText + * @return + */ + protected Query createReaderQuery(String queryText) throws ParseException + { + return new SolrReaderQuery(queryText); + } + + /** + * @param queryText + * @return + */ + protected Query createAuthorityQuery(String queryText) throws ParseException + { + //return new SolrAuthorityQuery(queryText); + return new SolrAuthoritySetQuery(","+queryText); + } + + // TODO: correct field names + protected Query addContentAttributeQuery(PropertyDefinition pDef, String queryText, SubQuery subQueryBuilder, + AnalysisMode analysisMode, LuceneFunction luceneFunction, String expandedFieldName, + List expandedLocales) throws ParseException + { + BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder(); + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, + FieldUse.FTS); + if (isExactTermSearch(analysisMode)) + { + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + Query subQuery = subQueryBuilder.getQuery(field.getField(), queryText, analysisMode, + luceneFunction); + if (subQuery != null) + { + booleanQueryBuilder.add(subQuery, Occur.SHOULD); + } + } + } + if (booleanQueryBuilder.build().clauses().size() == 0) + { + throw new UnsupportedOperationException("Exact Term search is not supported unless you configure the field <"+pDef.getName()+"> for cross locale search"); + } + } else + { + for (Locale locale : expandedLocales) + { + boolean localisedSearch = locale.toString().length() != 0; + if (localisedSearch) + { + StringBuilder builder = new StringBuilder(queryText.length() + 10); + builder.append("\u0000").append(locale.toString()).append("\u0000").append(queryText); + } + for (FieldInstance field : indexedField.getFields()) + { + if ((!localisedSearch && !field.isLocalised()) || (localisedSearch && field.isLocalised())) + { + Query subQuery = subQueryBuilder.getQuery(field.getField(), queryText, analysisMode, + luceneFunction); + if (subQuery != null) + { + booleanQueryBuilder.add(subQuery, Occur.SHOULD); + } + } + } + } + } + return getNonEmptyBooleanQuery(booleanQueryBuilder.build()); + } + + protected void addLocaleSpecificUntokenisedMLOrTextFunction(String expandedFieldName, PropertyDefinition pDef, + String queryText, LuceneFunction luceneFunction, Builder booleanQuery, Locale locale, + IndexTokenisationMode tokenisationMode) + { + // Query subQuery = new CaseInsensitiveFieldQuery(new + // Term(getFieldName(expandedFieldName, locale, tokenisationMode, + // IndexTokenisationMode.FALSE), getFixedFunctionQueryText( + // queryText, locale, tokenisationMode, IndexTokenisationMode.FALSE))); + // booleanQuery.add(subQuery, Occur.SHOULD); + // + // if (booleanQuery.getClauses().length == 0) + // { + // booleanQuery.add(createNoMatchQuery(), Occur.SHOULD); + // } + throw new UnsupportedOperationException(); + } + + private FieldInstance getFieldInstance(String baseFieldName, PropertyDefinition pDef, Locale locale, + IndexTokenisationMode preferredIndexTokenisationMode, AnalysisMode analysisMode) + { + if (pDef != null) + { + switch (preferredIndexTokenisationMode) + { + case BOTH: + throw new IllegalStateException("Preferred mode can not be BOTH"); + case FALSE: + if (locale.toString().length() == 0) + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.ID); + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + return field; + } + } + } else + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.ID); + for (FieldInstance field : indexedField.getFields()) + { + if (field.isLocalised()) + { + return field; + } + } + } + break; + case TRUE: + { + IndexedField indexedField; + if (isExactTermSearch(analysisMode)) + { + indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.EXACT); + } else + { + indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.FTS); + } + if (locale.toString().length() == 0) + { + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + return field; + } + } + } else + { + for (FieldInstance field : indexedField.getFields()) + { + if (field.isLocalised()) + { + return field; + } + } + } + break; + } + } + return null; + + } + + return new FieldInstance(baseFieldName, false, false); + + } + + private boolean isExactTermSearch(AnalysisMode analysisMode) { + return analysisMode==AnalysisMode.IDENTIFIER && !this.solrParams.get(DEFTYPE).equals(CMIS_REQUEST_HANDLER); + } + + protected void addLocaleSpecificUntokenisedTextRangeFunction(String expandedFieldName, PropertyDefinition pDef, + String lower, String upper, boolean includeLower, boolean includeUpper, LuceneFunction luceneFunction, + Builder booleanQuery, Locale locale, IndexTokenisationMode tokenisationMode) throws ParseException + { + // String field = getFieldName(expandedFieldName, locale, + // tokenisationMode, IndexTokenisationMode.FALSE); + // + // StringBuilder builder = new StringBuilder(); + // builder.append("\u0000").append(locale.toString()).append("\u0000").append(lower); + // String first = getToken(field, builder.toString(), + // AnalysisMode.IDENTIFIER); + // + // builder = new StringBuilder(); + // builder.append("\u0000").append(locale.toString()).append("\u0000").append(upper); + // String last = getToken(field, builder.toString(), + // AnalysisMode.IDENTIFIER); + // + // Query query = new CaseInsensitiveFieldRangeQuery(field, first, last, + // includeLower, includeUpper); + // booleanQuery.add(query, Occur.SHOULD); + + throw new UnsupportedOperationException(); + + } + + protected void addMLTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, + SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, + String expandedFieldName, PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, + Builder booleanQuery, Locale locale) throws ParseException + { + + addMLTextOrTextAttributeQuery(field, pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, tokenisationMode, booleanQuery, locale); + } + + private void addMLTextOrTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, + SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, + String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) + throws ParseException + { + + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + switch (tokenisationMode) + { + case BOTH: + switch (analysisMode) + { + default: + case DEFAULT: + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); + + if (ContentModel.PROP_NAME.equals(pDef.getName())) + { + setLowercaseExpandedTerms(false); + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); + } + + break; + case TOKENISE: + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); + break; + case IDENTIFIER: + setLowercaseExpandedTerms(false); + if (isExactTermSearch(analysisMode)) + {//with exact search we favour tokenization, specifically cross locale tokenization + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); + } else + { + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); + } + break; + case FUZZY: + case PREFIX: + case WILD: + case LIKE: + setLowercaseExpandedTerms(false); + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); + + break; + } + break; + case FALSE: + setLowercaseExpandedTerms(false); + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); + break; + case TRUE: + default: + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); + break; + } + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + + } + + protected void addTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, + SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, + String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) + throws ParseException + { + + addMLTextOrTextAttributeQuery(field, pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, tokenisationMode, booleanQuery, locale); + } + + private void addLocaleSpecificMLOrTextAttribute(PropertyDefinition pDef, String queryText, SubQuery subQueryBuilder, + AnalysisMode analysisMode, LuceneFunction luceneFunction, Builder booleanQuery, Locale locale, + String textFieldName, IndexTokenisationMode tokenisationMode, + IndexTokenisationMode preferredTokenisationMode) throws ParseException + { + + FieldInstance fieldInstance = getFieldInstance(textFieldName, pDef, locale, preferredTokenisationMode, analysisMode); + if (fieldInstance != null) + { + StringBuilder builder = new StringBuilder(queryText.length() + 10); + if (fieldInstance.isLocalised()) + { + builder.append("\u0000").append(locale.toString()).append("\u0000"); + } + builder.append(queryText); + Query subQuery = subQueryBuilder.getQuery(fieldInstance.getField(), builder.toString(), analysisMode, + luceneFunction); + if (subQuery != null) + { + booleanQuery.add(subQuery, Occur.SHOULD); + } + } + } + + protected void addTextRange(String field, PropertyDefinition pDef, String part1, String part2, boolean includeLower, + boolean includeUpper, AnalysisMode analysisMode, String fieldName, PropertyDefinition propertyDef, + IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) + throws ParseException, IOException + { + switch (tokenisationMode) + { + case BOTH: + switch (analysisMode) + { + case DEFAULT: + case TOKENISE: + addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, + locale, analysisMode, tokenisationMode, IndexTokenisationMode.TRUE); + break; + case IDENTIFIER: + addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, + locale, analysisMode, tokenisationMode, IndexTokenisationMode.FALSE); + break; + case WILD: + case LIKE: + case PREFIX: + case FUZZY: + default: + throw new UnsupportedOperationException(); + } + break; + case FALSE: + addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, locale, + analysisMode, tokenisationMode, IndexTokenisationMode.FALSE); + break; + case TRUE: + addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, locale, + analysisMode, tokenisationMode, IndexTokenisationMode.TRUE); + break; + default: + } + + } + + private void addLocaleSpecificTextRange(String expandedFieldName, PropertyDefinition pDef, String part1, + String part2, boolean includeLower, boolean includeUpper, Builder booleanQuery, Locale locale, + AnalysisMode analysisMode, IndexTokenisationMode tokenisationMode, + IndexTokenisationMode preferredTokenisationMode) throws ParseException, IOException + { + FieldInstance fieldInstance = getFieldInstance(expandedFieldName, pDef, locale, preferredTokenisationMode, analysisMode); + if (fieldInstance != null) + { + String firstString = null; + if ((part1 != null) && !part1.equals("\u0000")) + { + if (fieldInstance.isLocalised()) + { + firstString = getFirstTokenForRange(getLocalePrefixedText(part1, locale), fieldInstance); + if (firstString == null) + { + firstString = "{" + locale.getLanguage() + "}"; + } + } else + { + firstString = getFirstTokenForRange(part1, fieldInstance); + } + } else + { + if (fieldInstance.isLocalised()) + { + firstString = "{" + locale.getLanguage() + "}"; + } else + { + firstString = null; + } + } + + String lastString = null; + if ((part2 != null) && !part2.equals("\uffff")) + { + if (fieldInstance.isLocalised()) + { + lastString = getFirstTokenForRange(getLocalePrefixedText(part2, locale), fieldInstance); + if (lastString == null) + { + lastString = "{" + locale.getLanguage() + "}\uffff"; + } + } else + { + lastString = getFirstTokenForRange(part2, fieldInstance); + } + } else + { + if (fieldInstance.isLocalised()) + { + lastString = "{" + locale.getLanguage() + "}\uffff"; + } else + { + lastString = null; + } + } + + + // * is returned by stringField but it is not correct in case of range queries. + if (firstString != null && firstString.equals("*")) + { + firstString = null; + } + + if (lastString != null && lastString.equals("*")) + { + lastString = null; + } + + TermRangeQuery query = new TermRangeQuery(fieldInstance.getField(), + firstString == null ? null : new BytesRef(firstString), + lastString == null ? null : new BytesRef(lastString), includeLower, includeUpper); + booleanQuery.add(query, Occur.SHOULD); + } + } + + private String getFirstTokenForRange(String string, FieldInstance field) throws IOException + { + PackedTokenAttributeImpl nextToken; + TokenStream source = null; + ; + + try + { + source = getAnalyzer().tokenStream(field.getField(), new StringReader(string)); + source.reset(); + while (source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if (source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if (source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + nextToken = new PackedTokenAttributeImpl(); + nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); + nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if (typeAtt != null) + { + nextToken.setType(typeAtt.type()); + } + if (posIncAtt != null) + { + nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + + return nextToken.toString(); + } + } finally + { + try + { + if (source != null) + { + source.close(); + } + } catch (IOException e) + { + // ignore + } + } + return null; + } + + protected void addTextSpanQuery(String field, PropertyDefinition pDef, String first, String last, int slop, + boolean inOrder, String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, + Locale locale) { + addMLTextOrTextSpanQuery(field, pDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, + booleanQuery, locale); + } + + protected org.apache.lucene.search.Query addContentSpanQuery(String afield, PropertyDefinition pDef, String first, + String last, int slop, boolean inOrder, String expandedFieldName, List expandedLocales) + { + try + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + for (Locale locale : expandedLocales) + { + if (locale.toString().length() == 0) + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.FTS); + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + SpanQuery firstQuery = buildSpanOrQuery(first, field); + SpanQuery lastQuery = buildSpanOrQuery(last, field); + SpanNearQuery result = new SpanNearQuery(new SpanQuery[] + { firstQuery, lastQuery }, slop, inOrder); + booleanQuery.add(result, Occur.SHOULD); + } + } + } else + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.FTS); + for (FieldInstance field : indexedField.getFields()) + { + if (field.isLocalised()) + { + SpanQuery firstQuery = buildSpanOrQuery(getLocalePrefixedText(first, locale), field); + SpanQuery lastQuery = buildSpanOrQuery(getLocalePrefixedText(last, locale), field); + SpanNearQuery result = new SpanNearQuery(new SpanQuery[] + { firstQuery, lastQuery }, slop, inOrder); + booleanQuery.add(result, Occur.SHOULD); + } + } + } + } + return getNonEmptyBooleanQuery(booleanQuery.build()); + } catch (IOException ioe) + { + return createNoMatchQuery(); + } + + } + + private String getLocalePrefixedText(String text, Locale locale) + { + StringBuilder builder = new StringBuilder(text.length() + 10); + builder.append("\u0000").append(locale.toString()).append("\u0000").append(text); + return builder.toString(); + } + + /** + * @param first + * @param field + * @return SpanOrQuery + * @throws IOException + */ + private SpanQuery buildSpanOrQuery(String first, FieldInstance field) throws IOException + { + ArrayList spanOrQueryParts = new ArrayList(); + + PackedTokenAttributeImpl nextToken; + TokenStream source = null; + + try + { + source = getAnalyzer().tokenStream(field.getField(), new StringReader(first)); + source.reset(); + while (source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if (source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if (source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + nextToken = new PackedTokenAttributeImpl(); + nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); + nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if (typeAtt != null) + { + nextToken.setType(typeAtt.type()); + } + if (posIncAtt != null) + { + nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + + SpanQuery termQuery = new SpanTermQuery(new Term(field.getField(), nextToken.toString())); + spanOrQueryParts.add(termQuery); + } + } finally + { + try + { + if (source != null) + { + source.close(); + } + } catch (IOException e) + { + // ignore + } + } + + if (spanOrQueryParts.size() == 1) + { + return spanOrQueryParts.get(0); + } else + { + return new SpanOrQuery(spanOrQueryParts.toArray(new SpanQuery[] + {})); + } + } + + protected void addMLTextSpanQuery(String field, PropertyDefinition pDef, String first, String last, int slop, + boolean inOrder, String expandedFieldName, PropertyDefinition propertyDef, + IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) { + addMLTextOrTextSpanQuery(field, pDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, + booleanQuery, locale); + } + + private void addMLTextOrTextSpanQuery(String afield, PropertyDefinition pDef, String first, String last, int slop, + boolean inOrder, String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, + Locale locale) + { + try + { + + if (locale.toString().length() == 0) + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, + FieldUse.FTS); + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + SpanQuery firstQuery = buildSpanOrQuery(first, field); + SpanQuery lastQuery = buildSpanOrQuery(last, field); + SpanNearQuery result = new SpanNearQuery(new SpanQuery[] + { firstQuery, lastQuery }, slop, inOrder); + booleanQuery.add(result, Occur.SHOULD); + } + } + } else + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, + FieldUse.FTS); + for (FieldInstance field : indexedField.getFields()) + { + if (field.isLocalised()) + { + SpanQuery firstQuery = buildSpanOrQuery(getLocalePrefixedText(first, locale), field); + SpanQuery lastQuery = buildSpanOrQuery(getLocalePrefixedText(last, locale), field); + SpanNearQuery result = new SpanNearQuery(new SpanQuery[] + { firstQuery, lastQuery }, slop, inOrder); + booleanQuery.add(result, Occur.SHOULD); + } + } + } + + } catch (IOException ioe) + { + + } + } + + public boolean addContentCrossLocaleWildcards() + { + return false; + } + + protected Query createOwnerSetQuery(String queryText) throws ParseException + { + return new SolrOwnerSetQuery(queryText); + } + + protected Query createReaderSetQuery(String queryText) throws ParseException + { + return new SolrReaderSetQuery(queryText); + } + + protected Query createAuthoritySetQuery(String queryText) throws ParseException + { + return new SolrAuthoritySetQuery(queryText); + } + + protected Query createDeniedQuery(String queryText) throws ParseException + { + return new SolrDeniedQuery(queryText); + } + + protected Query createDenySetQuery(String queryText) throws ParseException + { + return new SolrDenySetQuery(queryText); + } + + private BytesRef analyzeMultitermTerm(String field, String part) { + return analyzeMultitermTerm(field, part, getAnalyzer()); + } + + // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) + @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") + protected BytesRef analyzeMultitermTerm(String field, String part, Analyzer analyzerIn) { + if (analyzerIn == null) analyzerIn = getAnalyzer(); + + try (TokenStream source = analyzerIn.tokenStream(field, part)) { + source.reset(); + + TermToBytesRefAttribute termAtt = source.getAttribute(TermToBytesRefAttribute.class); + + if (!source.incrementToken()) + throw new IllegalArgumentException("analyzer returned no terms for multiTerm term: " + part); + BytesRef bytes = BytesRef.deepCopyOf(termAtt.getBytesRef()); + if (source.incrementToken()) + throw new IllegalArgumentException("analyzer returned too many terms for multiTerm term: " + part); + source.end(); + return bytes; + } catch (IOException e) { + throw new RuntimeException("Error analyzing multiTerm term: " + part, e); + } + } + + protected Query newRangeQuery(String field, String part1, String part2, boolean startInclusive, boolean endInclusive) { + final BytesRef start; + final BytesRef end; + + if (part1 == null) { + start = null; + } else { + start = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part1) : new BytesRef(part1); + } + + if (part2 == null) { + end = null; + } else { + end = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part2) : new BytesRef(part2); + } + + final TermRangeQuery query = new TermRangeQuery(field, start, end, startInclusive, endInclusive); + + query.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE); + return query; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityQuery.java index 5d96000fa..d77d845f2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityQuery.java @@ -1,91 +1,91 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Query for docs the supplied authority is able to read. - * - * @author Matt Ward - */ -public class SolrAuthorityQuery extends AbstractAuthorityQuery -{ - public SolrAuthorityQuery(String authority) - { - super(authority); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_AUTHORITY).append(':'); - stringBuilder.append(authority); - return stringBuilder.toString(); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrAuthorityQueryWeight((SolrIndexSearcher)searcher, needsScores, this, authority); - } - - private class SolrAuthorityQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrAuthorityQueryWeight(SolrIndexSearcher searcher, boolean needsScores, Query query, String authority) throws IOException - { - super(searcher, needsScores, query, QueryConstants.FIELD_AUTHORITY, authority); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrAuthorityScorer.createAuthorityScorer(this, context, searcher, authority); - } - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_AUTHORITY, authority)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Query for docs the supplied authority is able to read. + * + * @author Matt Ward + */ +public class SolrAuthorityQuery extends AbstractAuthorityQuery +{ + public SolrAuthorityQuery(String authority) + { + super(authority); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_AUTHORITY).append(':'); + stringBuilder.append(authority); + return stringBuilder.toString(); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrAuthorityQueryWeight((SolrIndexSearcher)searcher, needsScores, this, authority); + } + + private class SolrAuthorityQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrAuthorityQueryWeight(SolrIndexSearcher searcher, boolean needsScores, Query query, String authority) throws IOException + { + super(searcher, needsScores, query, QueryConstants.FIELD_AUTHORITY, authority); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrAuthorityScorer.createAuthorityScorer(this, context, searcher, authority); + } + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_AUTHORITY, authority)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityScorer.java index 909cf8ae7..da9cff399 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityScorer.java @@ -1,109 +1,109 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Properties; - -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.solr.cache.CacheConstants; -import org.alfresco.solr.data.GlobalReaders; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Find the set of docs that the supplied authority can read. - *

- * Note that anyDenyDenies is not directly supported by this class, see {@link AbstractQParser}. - * - * @see AbstractQParser - * @author Matt Ward - */ -public class SolrAuthorityScorer extends AbstractSolrCachingScorer -{ - SolrAuthorityScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrAuthorityScorer createAuthorityScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException - { - Properties p = searcher.getSchema().getResourceLoader().getCoreProperties(); - boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); - - Query key = new SolrAuthorityQuery(authority); - - DocSet answer = (DocSet)searcher.cacheLookup(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key); - if(answer != null) - { - // Answer was in the cache, so return it. - return new SolrAuthorityScorer(weight, answer, context, searcher); - } - - // Answer was not in cache, so build the results, cache and return. - final HashSet globalReaders = GlobalReaders.getReaders(); - - if (globalReaders.contains(authority) || (doPermissionChecks == false)) - { - // can read all - DocSet allDocs = searcher.getDocSet(new MatchAllDocsQuery()); - return new SolrAuthorityScorer(weight, allDocs, context, searcher); - } - - // Docs for which the authority has explicit read access. - DocSet readableDocSet = searcher.getDocSet(new SolrReaderQuery(authority)); - - // Are all doc owners granted read permissions at a global level? - if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) - { - // Get the set of docs owned by the authority (which they can therefore read). - DocSet authorityOwnedDocs = searcher.getDocSet(new SolrOwnerQuery(authority)); - // Final set of docs that the authority can read. - DocSet toCache = readableDocSet.union(authorityOwnedDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); - return new SolrAuthorityScorer(weight, toCache, context, searcher); - } - else - { - // for that docs I own that have owner Read rights - DocSet ownerReadableDocSet = searcher.getDocSet(new SolrReaderQuery(PermissionService.OWNER_AUTHORITY)); - DocSet authorityOwnedDocs = searcher.getDocSet(new SolrOwnerQuery(authority)); - - // Docs where the authority is an owner and where owners have read rights. - DocSet docsAuthorityOwnsAndCanRead = ownerReadableDocSet.intersection(authorityOwnedDocs); - // Final set of docs that the authority can read. - DocSet toCache = readableDocSet.union(docsAuthorityOwnsAndCanRead); - searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); - return new SolrAuthorityScorer(weight, toCache, context, searcher); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Properties; + +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.solr.cache.CacheConstants; +import org.alfresco.solr.data.GlobalReaders; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Find the set of docs that the supplied authority can read. + *

+ * Note that anyDenyDenies is not directly supported by this class, see {@link AbstractQParser}. + * + * @see AbstractQParser + * @author Matt Ward + */ +public class SolrAuthorityScorer extends AbstractSolrCachingScorer +{ + SolrAuthorityScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrAuthorityScorer createAuthorityScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException + { + Properties p = searcher.getSchema().getResourceLoader().getCoreProperties(); + boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); + + Query key = new SolrAuthorityQuery(authority); + + DocSet answer = (DocSet)searcher.cacheLookup(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key); + if(answer != null) + { + // Answer was in the cache, so return it. + return new SolrAuthorityScorer(weight, answer, context, searcher); + } + + // Answer was not in cache, so build the results, cache and return. + final HashSet globalReaders = GlobalReaders.getReaders(); + + if (globalReaders.contains(authority) || (doPermissionChecks == false)) + { + // can read all + DocSet allDocs = searcher.getDocSet(new MatchAllDocsQuery()); + return new SolrAuthorityScorer(weight, allDocs, context, searcher); + } + + // Docs for which the authority has explicit read access. + DocSet readableDocSet = searcher.getDocSet(new SolrReaderQuery(authority)); + + // Are all doc owners granted read permissions at a global level? + if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) + { + // Get the set of docs owned by the authority (which they can therefore read). + DocSet authorityOwnedDocs = searcher.getDocSet(new SolrOwnerQuery(authority)); + // Final set of docs that the authority can read. + DocSet toCache = readableDocSet.union(authorityOwnedDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); + return new SolrAuthorityScorer(weight, toCache, context, searcher); + } + else + { + // for that docs I own that have owner Read rights + DocSet ownerReadableDocSet = searcher.getDocSet(new SolrReaderQuery(PermissionService.OWNER_AUTHORITY)); + DocSet authorityOwnedDocs = searcher.getDocSet(new SolrOwnerQuery(authority)); + + // Docs where the authority is an owner and where owners have read rights. + DocSet docsAuthorityOwnsAndCanRead = ownerReadableDocSet.intersection(authorityOwnedDocs); + // Final set of docs that the authority can read. + DocSet toCache = readableDocSet.union(docsAuthorityOwnsAndCanRead); + searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); + return new SolrAuthorityScorer(weight, toCache, context, searcher); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetQuery.java index f3e53bb75..194cdc973 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetQuery.java @@ -1,372 +1,372 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Properties; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.solr.data.GlobalReaders; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BooleanQuery.Builder; -import org.apache.lucene.search.Collector; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.LeafCollector; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.DelegatingCollector; -import org.apache.solr.search.PostFilter; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * @author Andy - * - */ -public class SolrAuthoritySetQuery extends AbstractAuthoritySetQuery implements PostFilter -{ - public SolrAuthoritySetQuery(String authorities) - { - super(authorities); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - SolrIndexSearcher solrIndexSearcher = (SolrIndexSearcher)searcher; - Properties p = solrIndexSearcher.getSchema().getResourceLoader().getCoreProperties(); - boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); - - boolean hasGlobalRead = false; - - final HashSet globalReaders = GlobalReaders.getReaders(); - - for(String auth : auths) - { - if(globalReaders.contains(auth)) - { - hasGlobalRead = true; - break; - } - } - - if (hasGlobalRead || (doPermissionChecks == false)) - { - return new MatchAllDocsQuery().createWeight(searcher, needsScores); - } - - BitsFilter readFilter = getACLFilter(auths, QueryConstants.FIELD_READER, solrIndexSearcher); - BitsFilter ownerFilter = getOwnerFilter(auths, solrIndexSearcher); - - if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) - { - readFilter.or(ownerFilter); - return new ConstantScoreQuery(readFilter).createWeight(searcher, needsScores); - } - else - { - String[] ownerAuth = {PermissionService.OWNER_AUTHORITY}; - BitsFilter ownerReadFilter = getACLFilter(ownerAuth, QueryConstants.FIELD_READER, solrIndexSearcher); - ownerReadFilter.and(ownerFilter); - readFilter.or(ownerReadFilter); - return new ConstantScoreQuery(readFilter).createWeight(searcher, needsScores); - } - } - - /* - * ACL PostFilter - * - * The getFilterCollector function returns a DelegatingCollector - * which is used to filter the documents that match the query. - * - * A delegating collector wraps the TopDocs Collector which gathers the top documents that - * match a query. A delegating can filter the documents before "delegating" to the TopDocs - * collector. This filtering process is where the ACL logic is applied. - * - * The getFilterCollector method sets up the data structures needed to apply the acl rules. - * These data structures are then passed to the access control collectors. - * - */ - - public DelegatingCollector getFilterCollector(IndexSearcher searcher) - { - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - SolrIndexSearcher solrIndexSearcher = (SolrIndexSearcher)searcher; - Properties p = solrIndexSearcher.getSchema().getResourceLoader().getCoreProperties(); - boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); - boolean hasGlobalRead = false; - - final HashSet globalReaders = GlobalReaders.getReaders(); - - for(String auth : auths) - { - if(globalReaders.contains(auth)) - { - hasGlobalRead = true; - break; - } - } - - if (hasGlobalRead || (doPermissionChecks == false)) - { - return new AllAccessCollector(); - } - - try - { - - /* - * Collect the ACLID's that match the authorities. - * This is done by querying the ACL records in the index. See the method for more - * documentation on this query. - */ - - HybridBitSet aclSet = getACLSet(auths, QueryConstants.FIELD_READER, solrIndexSearcher); - - /* - * Collect the documents that the user owns. - */ - - BitsFilter ownerFilter = getOwnerFilter(auths, solrIndexSearcher); - - if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) - { - return new AccessControlCollector(aclSet, ownerFilter); - } - else - { - String[] ownerAuth = {PermissionService.OWNER_AUTHORITY}; - HybridBitSet ownerAclSet = getACLSet(ownerAuth, QueryConstants.FIELD_READER, solrIndexSearcher); - return new AccessControlCollectorWithoutOwnerRead(aclSet, ownerAclSet, ownerFilter); - } - } - catch(Exception e) - { - throw new RuntimeException(e); - } - } - - public int getCost() - { - return 202; - } - - public void setCost(int cost) - { - - } - - public boolean getCache() { - return true; - } - - public void setCache(boolean cache) - { - - } - - public boolean getCacheSep() - { - return false; - } - - public void setCacheSep(boolean sep) - { - - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_AUTHORITYSET).append(':'); - stringBuilder.append(authorities); - return stringBuilder.toString(); - } - - private BitsFilter getOwnerFilter(String[] auths, SolrIndexSearcher searcher) throws IOException - { - Builder builder = new BooleanQuery.Builder(); - for(String current : auths) - { - if (AuthorityType.getAuthorityType(current) == AuthorityType.USER) - { - builder.add(new TermQuery(new Term(QueryConstants.FIELD_OWNER, current)), BooleanClause.Occur.SHOULD); - } - } - - BitsFilterCollector collector = new BitsFilterCollector(searcher.getTopReaderContext().leaves().size()); - searcher.search(builder.build(), collector); - return collector.getBitsFilter(); - } - - class BitsFilterCollector implements Collector, LeafCollector - { - private List sets; - private FixedBitSet set; - - public BitsFilterCollector(int leafCount) - { - this.sets = new ArrayList(leafCount); - } - - public BitsFilter getBitsFilter() { - return new BitsFilter(sets); - } - - public boolean acceptsDocsOutOfOrder() { - return false; - } - - public void setScorer(Scorer scorer) { - - } - - public void collect(int doc) { - set.set(doc); - } - - @Override - public LeafCollector getLeafCollector(LeafReaderContext context) - throws IOException { - set = new FixedBitSet(context.reader().maxDoc()); - sets.add(set); - return this; - } - - @Override - public boolean needsScores() { - // TODO Auto-generated method stub - return false; - } - } - - - /* - * The AccessControlCollector applies that ACL logic given aclIds and ownerFilter - */ - - class AccessControlCollector extends DelegatingCollector - { - private HybridBitSet aclIds; - private NumericDocValues fieldValues; - private BitsFilter ownerFilter; - private FixedBitSet ownerDocs; - - public AccessControlCollector(HybridBitSet aclIds, BitsFilter ownerFilter) - { - this.aclIds=aclIds; - this.ownerFilter = ownerFilter; - } - - public boolean acceptsDocsOutOfOrder() - { - return false; - } - - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); - this.ownerDocs = ownerFilter.getBitSets().get(context.ord); - } - - /* - * The collect method is applied to each document that matches the - * query. The document's aclId must be in the set of aclId's passed into the collector, - * or the documents id must be in the ownerDocs. - */ - - public void collect(int doc) throws IOException - { - long aclId = this.fieldValues.get(doc); - - if(aclIds.get(aclId) || ownerDocs.get(doc)) - { - super.collect(doc); - } - } - } - - class AccessControlCollectorWithoutOwnerRead extends DelegatingCollector - { - private HybridBitSet aclIds; - private HybridBitSet ownerAclIds; - private NumericDocValues fieldValues; - private BitsFilter ownerFilter; - private FixedBitSet ownerDocs; - public AccessControlCollectorWithoutOwnerRead(HybridBitSet aclIds, HybridBitSet ownerAclIds, BitsFilter ownerFilter) - { - this.aclIds=aclIds; - this.ownerAclIds = ownerAclIds; - this.ownerFilter = ownerFilter; - } - - public boolean acceptsDocsOutOfOrder() - { - return false; - } - - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); - this.ownerDocs = ownerFilter.getBitSets().get(context.ord); - } - - public void collect(int doc) throws IOException - { - long aclId = this.fieldValues.get(doc); - if(aclIds.get(aclId) || (ownerDocs.get(doc) && ownerAclIds.get(aclId))) - { - super.collect(doc); - } - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.solr.data.GlobalReaders; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BooleanQuery.Builder; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.LeafCollector; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.DelegatingCollector; +import org.apache.solr.search.PostFilter; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * @author Andy + * + */ +public class SolrAuthoritySetQuery extends AbstractAuthoritySetQuery implements PostFilter +{ + public SolrAuthoritySetQuery(String authorities) + { + super(authorities); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + SolrIndexSearcher solrIndexSearcher = (SolrIndexSearcher)searcher; + Properties p = solrIndexSearcher.getSchema().getResourceLoader().getCoreProperties(); + boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); + + boolean hasGlobalRead = false; + + final HashSet globalReaders = GlobalReaders.getReaders(); + + for(String auth : auths) + { + if(globalReaders.contains(auth)) + { + hasGlobalRead = true; + break; + } + } + + if (hasGlobalRead || (doPermissionChecks == false)) + { + return new MatchAllDocsQuery().createWeight(searcher, needsScores); + } + + BitsFilter readFilter = getACLFilter(auths, QueryConstants.FIELD_READER, solrIndexSearcher); + BitsFilter ownerFilter = getOwnerFilter(auths, solrIndexSearcher); + + if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) + { + readFilter.or(ownerFilter); + return new ConstantScoreQuery(readFilter).createWeight(searcher, needsScores); + } + else + { + String[] ownerAuth = {PermissionService.OWNER_AUTHORITY}; + BitsFilter ownerReadFilter = getACLFilter(ownerAuth, QueryConstants.FIELD_READER, solrIndexSearcher); + ownerReadFilter.and(ownerFilter); + readFilter.or(ownerReadFilter); + return new ConstantScoreQuery(readFilter).createWeight(searcher, needsScores); + } + } + + /* + * ACL PostFilter + * + * The getFilterCollector function returns a DelegatingCollector + * which is used to filter the documents that match the query. + * + * A delegating collector wraps the TopDocs Collector which gathers the top documents that + * match a query. A delegating can filter the documents before "delegating" to the TopDocs + * collector. This filtering process is where the ACL logic is applied. + * + * The getFilterCollector method sets up the data structures needed to apply the acl rules. + * These data structures are then passed to the access control collectors. + * + */ + + public DelegatingCollector getFilterCollector(IndexSearcher searcher) + { + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + SolrIndexSearcher solrIndexSearcher = (SolrIndexSearcher)searcher; + Properties p = solrIndexSearcher.getSchema().getResourceLoader().getCoreProperties(); + boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); + boolean hasGlobalRead = false; + + final HashSet globalReaders = GlobalReaders.getReaders(); + + for(String auth : auths) + { + if(globalReaders.contains(auth)) + { + hasGlobalRead = true; + break; + } + } + + if (hasGlobalRead || (doPermissionChecks == false)) + { + return new AllAccessCollector(); + } + + try + { + + /* + * Collect the ACLID's that match the authorities. + * This is done by querying the ACL records in the index. See the method for more + * documentation on this query. + */ + + HybridBitSet aclSet = getACLSet(auths, QueryConstants.FIELD_READER, solrIndexSearcher); + + /* + * Collect the documents that the user owns. + */ + + BitsFilter ownerFilter = getOwnerFilter(auths, solrIndexSearcher); + + if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) + { + return new AccessControlCollector(aclSet, ownerFilter); + } + else + { + String[] ownerAuth = {PermissionService.OWNER_AUTHORITY}; + HybridBitSet ownerAclSet = getACLSet(ownerAuth, QueryConstants.FIELD_READER, solrIndexSearcher); + return new AccessControlCollectorWithoutOwnerRead(aclSet, ownerAclSet, ownerFilter); + } + } + catch(Exception e) + { + throw new RuntimeException(e); + } + } + + public int getCost() + { + return 202; + } + + public void setCost(int cost) + { + + } + + public boolean getCache() { + return true; + } + + public void setCache(boolean cache) + { + + } + + public boolean getCacheSep() + { + return false; + } + + public void setCacheSep(boolean sep) + { + + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_AUTHORITYSET).append(':'); + stringBuilder.append(authorities); + return stringBuilder.toString(); + } + + private BitsFilter getOwnerFilter(String[] auths, SolrIndexSearcher searcher) throws IOException + { + Builder builder = new BooleanQuery.Builder(); + for(String current : auths) + { + if (AuthorityType.getAuthorityType(current) == AuthorityType.USER) + { + builder.add(new TermQuery(new Term(QueryConstants.FIELD_OWNER, current)), BooleanClause.Occur.SHOULD); + } + } + + BitsFilterCollector collector = new BitsFilterCollector(searcher.getTopReaderContext().leaves().size()); + searcher.search(builder.build(), collector); + return collector.getBitsFilter(); + } + + class BitsFilterCollector implements Collector, LeafCollector + { + private List sets; + private FixedBitSet set; + + public BitsFilterCollector(int leafCount) + { + this.sets = new ArrayList(leafCount); + } + + public BitsFilter getBitsFilter() { + return new BitsFilter(sets); + } + + public boolean acceptsDocsOutOfOrder() { + return false; + } + + public void setScorer(Scorer scorer) { + + } + + public void collect(int doc) { + set.set(doc); + } + + @Override + public LeafCollector getLeafCollector(LeafReaderContext context) + throws IOException { + set = new FixedBitSet(context.reader().maxDoc()); + sets.add(set); + return this; + } + + @Override + public boolean needsScores() { + // TODO Auto-generated method stub + return false; + } + } + + + /* + * The AccessControlCollector applies that ACL logic given aclIds and ownerFilter + */ + + class AccessControlCollector extends DelegatingCollector + { + private HybridBitSet aclIds; + private NumericDocValues fieldValues; + private BitsFilter ownerFilter; + private FixedBitSet ownerDocs; + + public AccessControlCollector(HybridBitSet aclIds, BitsFilter ownerFilter) + { + this.aclIds=aclIds; + this.ownerFilter = ownerFilter; + } + + public boolean acceptsDocsOutOfOrder() + { + return false; + } + + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); + this.ownerDocs = ownerFilter.getBitSets().get(context.ord); + } + + /* + * The collect method is applied to each document that matches the + * query. The document's aclId must be in the set of aclId's passed into the collector, + * or the documents id must be in the ownerDocs. + */ + + public void collect(int doc) throws IOException + { + long aclId = this.fieldValues.get(doc); + + if(aclIds.get(aclId) || ownerDocs.get(doc)) + { + super.collect(doc); + } + } + } + + class AccessControlCollectorWithoutOwnerRead extends DelegatingCollector + { + private HybridBitSet aclIds; + private HybridBitSet ownerAclIds; + private NumericDocValues fieldValues; + private BitsFilter ownerFilter; + private FixedBitSet ownerDocs; + public AccessControlCollectorWithoutOwnerRead(HybridBitSet aclIds, HybridBitSet ownerAclIds, BitsFilter ownerFilter) + { + this.aclIds=aclIds; + this.ownerAclIds = ownerAclIds; + this.ownerFilter = ownerFilter; + } + + public boolean acceptsDocsOutOfOrder() + { + return false; + } + + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); + this.ownerDocs = ownerFilter.getBitSets().get(context.ord); + } + + public void collect(int doc) throws IOException + { + long aclId = this.fieldValues.get(doc); + if(aclIds.get(aclId) || (ownerDocs.get(doc) && ownerAclIds.get(aclId))) + { + super.collect(doc); + } + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetScorer.java index 29202fdd4..c88879e3c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetScorer.java @@ -1,136 +1,136 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Properties; - -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.solr.cache.CacheConstants; -import org.alfresco.solr.data.GlobalReaders; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -/** - * Find the set of docs that the specified authority set can read. - *

- * Note that anyDenyDenies is not directly supported by this class, see {@link AbstractQParser}. - * - * @see AbstractQParser - * @author Matt Ward - */ -public class SolrAuthoritySetScorer extends AbstractSolrCachingScorer -{ - SolrAuthoritySetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrAuthoritySetScorer createAuthoritySetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities) throws IOException - { - Properties p = searcher.getSchema().getResourceLoader().getCoreProperties(); - boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); - - Query key = new SolrAuthoritySetQuery(authorities); - - DocSet answer = (DocSet)searcher.cacheLookup(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key); - if(answer != null) - { - // Answer was in the cache, so return it. - return new SolrAuthoritySetScorer(weight, answer, context, searcher); - } - - // Answer was not in cache, so build the results, cache and return. - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - boolean hasGlobalRead = false; - - final HashSet globalReaders = GlobalReaders.getReaders(); - - for(String auth : auths) - { - if(globalReaders.contains(auth)) - { - hasGlobalRead = true; - break; - } - - } - - if (hasGlobalRead || (doPermissionChecks == false)) - { - // can read all - WrappedQuery wrapped = new WrappedQuery(new MatchAllDocsQuery()); - wrapped.setCache(false); - DocSet allDocs = searcher.getDocSet(wrapped); - return new SolrAuthoritySetScorer(weight, allDocs, context, searcher); - } - - // Docs for which the authorities have explicit read access. - WrappedQuery wrapped; - wrapped = new WrappedQuery(new SolrReaderSetQuery(authorities)); - wrapped.setCache(false); - DocSet readableDocSet = searcher.getDocSet(wrapped); - - // Are all doc owners granted read permissions at a global level? - if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) - { - // Get the set of docs owned by the authorities (which they can therefore read). - wrapped = new WrappedQuery(new SolrOwnerSetQuery(authorities)); - wrapped.setCache(false); - DocSet authorityOwnedDocs = searcher.getDocSet(wrapped); - // Final set of docs that the authorities can read. - DocSet toCache = readableDocSet.union(authorityOwnedDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); - return new SolrAuthoritySetScorer(weight, toCache, context, searcher); - } - else - { - // for that docs I own that have owner Read rights - wrapped = new WrappedQuery(new SolrReaderSetQuery("|"+PermissionService.OWNER_AUTHORITY)); - wrapped.setCache(false); - DocSet ownerReadableDocSet = searcher.getDocSet(wrapped); - wrapped = new WrappedQuery(new SolrOwnerSetQuery(authorities)); - wrapped.setCache(false); - DocSet authorityOwnedDocs = searcher.getDocSet(wrapped); - - // Docs where the authority is an owner and where owners have read rights. - DocSet docsAuthorityOwnsAndCanRead = ownerReadableDocSet.intersection(authorityOwnedDocs); - // Final set of docs that the authorities can read. - DocSet toCache = readableDocSet.union(docsAuthorityOwnsAndCanRead); - searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); - return new SolrAuthoritySetScorer(weight, toCache, context, searcher); - } - } -} - +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Properties; + +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.solr.cache.CacheConstants; +import org.alfresco.solr.data.GlobalReaders; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +/** + * Find the set of docs that the specified authority set can read. + *

+ * Note that anyDenyDenies is not directly supported by this class, see {@link AbstractQParser}. + * + * @see AbstractQParser + * @author Matt Ward + */ +public class SolrAuthoritySetScorer extends AbstractSolrCachingScorer +{ + SolrAuthoritySetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrAuthoritySetScorer createAuthoritySetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities) throws IOException + { + Properties p = searcher.getSchema().getResourceLoader().getCoreProperties(); + boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); + + Query key = new SolrAuthoritySetQuery(authorities); + + DocSet answer = (DocSet)searcher.cacheLookup(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key); + if(answer != null) + { + // Answer was in the cache, so return it. + return new SolrAuthoritySetScorer(weight, answer, context, searcher); + } + + // Answer was not in cache, so build the results, cache and return. + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + boolean hasGlobalRead = false; + + final HashSet globalReaders = GlobalReaders.getReaders(); + + for(String auth : auths) + { + if(globalReaders.contains(auth)) + { + hasGlobalRead = true; + break; + } + + } + + if (hasGlobalRead || (doPermissionChecks == false)) + { + // can read all + WrappedQuery wrapped = new WrappedQuery(new MatchAllDocsQuery()); + wrapped.setCache(false); + DocSet allDocs = searcher.getDocSet(wrapped); + return new SolrAuthoritySetScorer(weight, allDocs, context, searcher); + } + + // Docs for which the authorities have explicit read access. + WrappedQuery wrapped; + wrapped = new WrappedQuery(new SolrReaderSetQuery(authorities)); + wrapped.setCache(false); + DocSet readableDocSet = searcher.getDocSet(wrapped); + + // Are all doc owners granted read permissions at a global level? + if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) + { + // Get the set of docs owned by the authorities (which they can therefore read). + wrapped = new WrappedQuery(new SolrOwnerSetQuery(authorities)); + wrapped.setCache(false); + DocSet authorityOwnedDocs = searcher.getDocSet(wrapped); + // Final set of docs that the authorities can read. + DocSet toCache = readableDocSet.union(authorityOwnedDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); + return new SolrAuthoritySetScorer(weight, toCache, context, searcher); + } + else + { + // for that docs I own that have owner Read rights + wrapped = new WrappedQuery(new SolrReaderSetQuery("|"+PermissionService.OWNER_AUTHORITY)); + wrapped.setCache(false); + DocSet ownerReadableDocSet = searcher.getDocSet(wrapped); + wrapped = new WrappedQuery(new SolrOwnerSetQuery(authorities)); + wrapped.setCache(false); + DocSet authorityOwnedDocs = searcher.getDocSet(wrapped); + + // Docs where the authority is an owner and where owners have read rights. + DocSet docsAuthorityOwnsAndCanRead = ownerReadableDocSet.intersection(authorityOwnedDocs); + // Final set of docs that the authorities can read. + DocSet toCache = readableDocSet.union(docsAuthorityOwnsAndCanRead); + searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); + return new SolrAuthoritySetScorer(weight, toCache, context, searcher); + } + } +} + diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathQuery.java index 62146dc3a..07ef57c14 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathQuery.java @@ -1,108 +1,108 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -/** - * Decorator that executes a SolrPathQuery and returns cached results where possible. - * - * @author Andy - */ -public class SolrCachingPathQuery extends Query -{ - final SolrPathQuery pathQuery; - - public SolrCachingPathQuery(SolrPathQuery pathQuery) - { - if (pathQuery == null) throw new IllegalStateException("pathQuery cannot be null"); - this.pathQuery = pathQuery; - } - - /* - * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) - */ - public Weight createWeight(IndexSearcher indexSearcher, boolean requiresScore) throws IOException - { - SolrIndexSearcher searcher = null; - if(!(indexSearcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - else - { - searcher = (SolrIndexSearcher)indexSearcher; - } - - DocSet results = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_PATH_CACHE, pathQuery); - if (results == null) - { - // Cache miss: get path query results and cache them - WrappedQuery wrapped = new WrappedQuery(pathQuery); - wrapped.setCache(false); - results = searcher.getDocSet(wrapped); - searcher.cacheInsert(CacheConstants.ALFRESCO_PATH_CACHE, pathQuery, results); - } - - return new ConstantScoreQuery(results.getTopFilter()).createWeight(searcher, false); - } - - /* - * @see org.apache.lucene.search.Query#toString(java.lang.String) - */ - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("CACHED -> :"); - stringBuilder.append(pathQuery.toString()); - return stringBuilder.toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof SolrCachingPathQuery)) return false; - - SolrCachingPathQuery that = (SolrCachingPathQuery) o; - return pathQuery.equals(that.pathQuery); - - } - - @Override - public int hashCode() { - return pathQuery.hashCode(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +/** + * Decorator that executes a SolrPathQuery and returns cached results where possible. + * + * @author Andy + */ +public class SolrCachingPathQuery extends Query +{ + final SolrPathQuery pathQuery; + + public SolrCachingPathQuery(SolrPathQuery pathQuery) + { + if (pathQuery == null) throw new IllegalStateException("pathQuery cannot be null"); + this.pathQuery = pathQuery; + } + + /* + * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) + */ + public Weight createWeight(IndexSearcher indexSearcher, boolean requiresScore) throws IOException + { + SolrIndexSearcher searcher = null; + if(!(indexSearcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + else + { + searcher = (SolrIndexSearcher)indexSearcher; + } + + DocSet results = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_PATH_CACHE, pathQuery); + if (results == null) + { + // Cache miss: get path query results and cache them + WrappedQuery wrapped = new WrappedQuery(pathQuery); + wrapped.setCache(false); + results = searcher.getDocSet(wrapped); + searcher.cacheInsert(CacheConstants.ALFRESCO_PATH_CACHE, pathQuery, results); + } + + return new ConstantScoreQuery(results.getTopFilter()).createWeight(searcher, false); + } + + /* + * @see org.apache.lucene.search.Query#toString(java.lang.String) + */ + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("CACHED -> :"); + stringBuilder.append(pathQuery.toString()); + return stringBuilder.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SolrCachingPathQuery)) return false; + + SolrCachingPathQuery that = (SolrCachingPathQuery) o; + return pathQuery.equals(that.pathQuery); + + } + + @Override + public int hashCode() { + return pathQuery.hashCode(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathScorer.java index 5f03dd69e..e56c4b2d5 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathScorer.java @@ -1,72 +1,72 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -/** - * Caching wrapper for {@link SolrPathQuery}. - * - * @author Matt Ward - */ -public class SolrCachingPathScorer extends AbstractSolrCachingScorer -{ - SolrCachingPathScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - - /** - * Factory method used to create {@link SolrCachingPathScorer} instances. - * @param acceptDocs - */ - public static SolrCachingPathScorer create(SolrCachingPathWeight weight, - LeafReaderContext context, - SolrIndexSearcher searcher, - SolrPathQuery wrappedPathQuery) throws IOException - { - DocSet results = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_PATH_CACHE, wrappedPathQuery); - if (results == null) - { - // Cache miss: get path query results and cache them - WrappedQuery wrapped = new WrappedQuery(wrappedPathQuery); - wrapped.setCache(false); - results = searcher.getDocSet(wrapped); - searcher.cacheInsert(CacheConstants.ALFRESCO_PATH_CACHE, wrappedPathQuery, results); - } - - return new SolrCachingPathScorer(weight, results, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +/** + * Caching wrapper for {@link SolrPathQuery}. + * + * @author Matt Ward + */ +public class SolrCachingPathScorer extends AbstractSolrCachingScorer +{ + SolrCachingPathScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + + /** + * Factory method used to create {@link SolrCachingPathScorer} instances. + * @param acceptDocs + */ + public static SolrCachingPathScorer create(SolrCachingPathWeight weight, + LeafReaderContext context, + SolrIndexSearcher searcher, + SolrPathQuery wrappedPathQuery) throws IOException + { + DocSet results = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_PATH_CACHE, wrappedPathQuery); + if (results == null) + { + // Cache miss: get path query results and cache them + WrappedQuery wrapped = new WrappedQuery(wrappedPathQuery); + wrapped.setCache(false); + results = searcher.getDocSet(wrapped); + searcher.cacheInsert(CacheConstants.ALFRESCO_PATH_CACHE, wrappedPathQuery, results); + } + + return new SolrCachingPathScorer(weight, results, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathWeight.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathWeight.java index b5448bab3..ffc0f88a8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathWeight.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathWeight.java @@ -1,84 +1,84 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * {@link Weight} implementation for the {@link SolrCachingPathQuery}. - * - * @author Matt Ward - */ -public class SolrCachingPathWeight extends Weight -{ - private SolrIndexSearcher searcher; - private Weight queryWeight; - - public SolrCachingPathWeight(SolrCachingPathQuery cachingPathQuery, SolrIndexSearcher searcher) throws IOException - { - super(cachingPathQuery); - this.searcher = searcher; - queryWeight = cachingPathQuery.pathQuery.createWeight(searcher, false); - } - - @Override - public Explanation explain(LeafReaderContext context, int doc) throws IOException - { - throw new UnsupportedOperationException(); - } - - @Override - public float getValueForNormalization() throws IOException - { - return 1.0f; - } - - @Override - public void normalize(float norm, float topLevelBoost) - { - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrCachingPathScorer.create(this, context, searcher, ((SolrCachingPathQuery)getQuery()).pathQuery); - } - - @Override - public void extractTerms(Set terms) - { - queryWeight.extractTerms(terms); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * {@link Weight} implementation for the {@link SolrCachingPathQuery}. + * + * @author Matt Ward + */ +public class SolrCachingPathWeight extends Weight +{ + private SolrIndexSearcher searcher; + private Weight queryWeight; + + public SolrCachingPathWeight(SolrCachingPathQuery cachingPathQuery, SolrIndexSearcher searcher) throws IOException + { + super(cachingPathQuery); + this.searcher = searcher; + queryWeight = cachingPathQuery.pathQuery.createWeight(searcher, false); + } + + @Override + public Explanation explain(LeafReaderContext context, int doc) throws IOException + { + throw new UnsupportedOperationException(); + } + + @Override + public float getValueForNormalization() throws IOException + { + return 1.0f; + } + + @Override + public void normalize(float norm, float topLevelBoost) + { + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrCachingPathScorer.create(this, context, searcher, ((SolrCachingPathQuery)getQuery()).pathQuery); + } + + @Override + public void extractTerms(Set terms) + { + queryWeight.extractTerms(terms); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrContainerScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrContainerScorer.java index db53d2153..641d5f505 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrContainerScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrContainerScorer.java @@ -1,581 +1,581 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; - -/** - * The scorer for structured field queries. - * - * A document either matches or it does not, there for the frequency is reported - * as 0.0f or 1.0. - * - * - * - * @author andyh - */ -public class SolrContainerScorer extends Scorer -{ - SolrContainerScorerDocIdSetIterator iterator; - - - /** - * The arguments here follow the same pattern as used by the PhraseQuery. - * (It has the same unused arguments) - * - * @param weight - - * curently unsued - * @param root - - * the term positions for documents with multiple entries - this - * may be null, or contain no matches - it specifies those things - * that appear under multiple categories etc. - * @param positions - - * the structured field positions - where terms should appear - */ - public SolrContainerScorer(Weight weight, PostingsEnum root, StructuredFieldPosition[] positions) - { - super(weight); - iterator = new SolrContainerScorerDocIdSetIterator(root, positions); - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.Scorer#score() - */ - @Override - public float score() throws IOException - { - return 1.0f; - } - - /* (non-Javadoc) - * @see org.apache.lucene.index.DocsEnum#freq() - */ - @Override - public int freq() throws IOException - { - // Could loop through positions and return min freq?? - return 1; - } - - @Override - public int docID() - { - return iterator.docID(); - } - - @Override - public DocIdSetIterator iterator() - { - return iterator; - } - - - - private static class SolrContainerScorerDocIdSetIterator extends DocIdSetIterator - { - - // Positions of documents with multiple structure elements - // e.g have mutiple paths, multiple categories or multiples entries in the - // same category - PostingsEnum root; - - // The Field positions that describe the structure we are trying to match - StructuredFieldPosition[] positions; - - // The minium document found so far - int min = -1; - - // The max document found so far - int max = -1; - - // The next root doc - // Integer.MAX_VALUE and it has gone off the end - int rootDoc = -1; - - // Are there potentially more documents - boolean more = true; - - SolrContainerScorerDocIdSetIterator( PostingsEnum root, StructuredFieldPosition[] positions) - { - this.positions = positions; - this.root = root; - } - - /* - * (non-Javadoc) - * - * @see org.apache.lucene.search.Scorer#next() - */ - private boolean next() throws IOException - { - if(root == null) - { - return false; - } - if (!more) - { - // One of the search terms has no more docuements - return false; - } - - if (max == -1) - { - // We need to initialise - // Just do a next on all terms and check if the first doc matches - doNextOnAll(); - if (found()) - { - return true; - } - // drop through to the normal find sequence - } - - return findNext(); - } - - - /** - * @return - * @throws IOException - */ - private boolean findNext() throws IOException - { - if(root == null) - { - more = false; - max= NO_MORE_DOCS; - return false; - } - - // Move to the next document - - while (more) - { - move(); // may set more to false - if (found()) - { - return true; - } - } - - // If we get here we must have no more documents - return false; - } - - /** - * Check if we have found a match - * - * @return - * @throws IOException - */ - - private boolean found() throws IOException - { - // No predicate test if there are no positions - if (positions.length == 0) - { - return true; - } - - // no more documents - no match - if (!more) - { - return false; - } - - // min and max must point to the same document - if (min != max) - { - return false; - } - - if (rootDoc != max) - { - return false; - } - - // We have duplicate entries - support should be improved but it is not used at the moment - // This should work akin to the leaf scorer - // It would compact the index - // The match must be in a known term range - int count = root.freq(); - int start = 0; - int end = -1; - for (int i = 0; i < count; i++) - { - if (i == 0) - { - // First starts at zero - start = 0; - end = root.nextPosition() ; - } - else - { - start = end + 1; - end = root.nextPosition() ; - } - - if (check(start, end)) - { - return true; - } - } - - // We had checks to do and they all failed. - return false; - } - - /* - * We have all documents at the same state. Now we check the positions of - * the terms. - */ - - private boolean check(int start, int end) throws IOException - { - int offset = checkTail(start, end, 0, 0, true); - // Last match may fail - if (offset == -1) - { - return false; - } - else - { - return true; - } - } - - /** - * For // type pattern matches we need to test patterns of variable greedyness. - * - * - * @param start - first term position - * @param end - end term position marker - * @param currentPosition - current path match index - * @param currentOffset - current path position starting at zero (position being considered is start+offset) - * @return - * @throws IOException - */ - private int checkTail(int start, int end, int currentPosition, int currentOffset, boolean checkLastMatch) throws IOException - { - // pre check last can match some combination or we are done - if(checkLastMatch) - { - if(end-start > 1) - { - StructuredFieldPosition last = getLastPositionNotSelfCheck(); - if(last != null) - { - if(last.matches(start, end, end-start-2) == -1) - { - return -1; - } - } - } - } - - int offset = currentOffset; - for (int i = currentPosition, l = positions.length; i < l; i++) - { - offset = positions[i].matches(start, end, offset); - if (offset == -1) - { - return -1; - } - if (positions[i].isDescendant()) - { - // go up in twos as there are two parts to a QName - for (int j = offset; j < (end - start); j+=2) - { - int newOffset = checkTail(start, end, i + 1, j, false); - if (newOffset != -1) - { - return newOffset; - } - } - return -1; - } - } - - if((start+offset+1) == end) - { - return offset; - } - else - { - return -1; - } - } - - private StructuredFieldPosition getLastPositionNotSelfCheck() - { - for(int i = positions.length-1; i >= 0; i--) - { - if(positions[i].linkSelf()) - { - continue; - } - return positions[i]; - } - return null; - } - - /* - * Move to the next position to consider for a match test - */ - - private void move() throws IOException - { - if (min == max) - { - // If we were at a match just do next on all terms - // They all must move on - doNextOnAll(); - } - else - { - // We are in a range - try and skip to the max position on all terms - // Only some need to move on - some may move past the current max and set a new target - skipToMax(); - } - } - - /* - * Go through all the term positions and try and move to next document. Any - * failure measn we have no more. - * - * This can be used at initialisation and when moving away from an existing - * match. - * - * This will set min, max, more and rootDoc - * - */ - private void doNextOnAll() throws IOException - { - if(root == null) - { - more = false; - max = NO_MORE_DOCS; - return; - } - // Do the terms - int current; - boolean first = true; - for (int i = 0, l = positions.length; i < l; i++) - { - if (positions[i].getCachingTermPositions() != null) - { - if (positions[i].getCachingTermPositions().nextDoc() != NO_MORE_DOCS) - - { - current = positions[i].getCachingTermPositions().docID(); - adjustMinMax(current, first); - first = false; - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - } - } - - // Do the root term - it must always exists as the path could well have multiple entries - // If an entry in the index does not have a root terminal it is broken - if (root.nextDoc() != NO_MORE_DOCS) - { - rootDoc = root.docID(); - if(first) - { - adjustMinMax(rootDoc, first); - } - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - if (root.docID() < max) - { - if (root.advance(max) != NO_MORE_DOCS) - { - rootDoc = root.docID(); - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - } - } - - /* - * Try and skip all those term positions at documents less than the current - * max up to value. This is quite likely to fail and leave us with (min != - * max) but that is OK, we try again. - * - * It is possible that max increases as we process terms, this is OK. We - * just failed to skip to a given value of max and start doing the next. - */ - private void skipToMax() throws IOException - { - // Do the terms - int current; - for (int i = 0, l = positions.length; i < l; i++) - { - if (i == 0) - { - min = max; - } - if (positions[i].getCachingTermPositions() != null) - { - if (positions[i].getCachingTermPositions().docID() < max) - { - if (positions[i].getCachingTermPositions().advance(max) != NO_MORE_DOCS) - { - current = positions[i].getCachingTermPositions().docID(); - adjustMinMax(current, false); - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - } - } - } - - // Do the root - if (root.docID() < max) - { - if (root.advance(max) != NO_MORE_DOCS) - { - rootDoc = root.docID(); - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - } - } - - /* - * Adjust the min and max values Convenience boolean to set or adjust the - * minimum. - */ - private void adjustMinMax(int doc, boolean setMin) - { - - if (max < doc) - { - max = doc; - } - - if (setMin) - { - min = doc; - } - else if (min > doc) - { - min = doc; - } - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.search.DocIdSetIterator#docID() - */ - @Override - public int docID() - { - return max; - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.DocIdSetIterator#nextDoc() - */ - @Override - public int nextDoc() throws IOException - { - boolean found = next(); - if(found) - { - return docID(); - } - else - { - return NO_MORE_DOCS; - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.DocIdSetIterator#advance(int) - */ - @Override - public int advance(int target) throws IOException - { - if(target == NO_MORE_DOCS) - { - more = false; - max = NO_MORE_DOCS; - return NO_MORE_DOCS; - } - - max = target; - boolean found = findNext(); - if(found) - { - return docID(); - } - else - { - return NO_MORE_DOCS; - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.DocIdSetIterator#cost() - */ - @Override - public long cost() - { - return 1; - } - } - -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.apache.lucene.index.PostingsEnum; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; + +/** + * The scorer for structured field queries. + * + * A document either matches or it does not, there for the frequency is reported + * as 0.0f or 1.0. + * + * + * + * @author andyh + */ +public class SolrContainerScorer extends Scorer +{ + SolrContainerScorerDocIdSetIterator iterator; + + + /** + * The arguments here follow the same pattern as used by the PhraseQuery. + * (It has the same unused arguments) + * + * @param weight - + * curently unsued + * @param root - + * the term positions for documents with multiple entries - this + * may be null, or contain no matches - it specifies those things + * that appear under multiple categories etc. + * @param positions - + * the structured field positions - where terms should appear + */ + public SolrContainerScorer(Weight weight, PostingsEnum root, StructuredFieldPosition[] positions) + { + super(weight); + iterator = new SolrContainerScorerDocIdSetIterator(root, positions); + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.Scorer#score() + */ + @Override + public float score() throws IOException + { + return 1.0f; + } + + /* (non-Javadoc) + * @see org.apache.lucene.index.DocsEnum#freq() + */ + @Override + public int freq() throws IOException + { + // Could loop through positions and return min freq?? + return 1; + } + + @Override + public int docID() + { + return iterator.docID(); + } + + @Override + public DocIdSetIterator iterator() + { + return iterator; + } + + + + private static class SolrContainerScorerDocIdSetIterator extends DocIdSetIterator + { + + // Positions of documents with multiple structure elements + // e.g have mutiple paths, multiple categories or multiples entries in the + // same category + PostingsEnum root; + + // The Field positions that describe the structure we are trying to match + StructuredFieldPosition[] positions; + + // The minium document found so far + int min = -1; + + // The max document found so far + int max = -1; + + // The next root doc + // Integer.MAX_VALUE and it has gone off the end + int rootDoc = -1; + + // Are there potentially more documents + boolean more = true; + + SolrContainerScorerDocIdSetIterator( PostingsEnum root, StructuredFieldPosition[] positions) + { + this.positions = positions; + this.root = root; + } + + /* + * (non-Javadoc) + * + * @see org.apache.lucene.search.Scorer#next() + */ + private boolean next() throws IOException + { + if(root == null) + { + return false; + } + if (!more) + { + // One of the search terms has no more docuements + return false; + } + + if (max == -1) + { + // We need to initialise + // Just do a next on all terms and check if the first doc matches + doNextOnAll(); + if (found()) + { + return true; + } + // drop through to the normal find sequence + } + + return findNext(); + } + + + /** + * @return + * @throws IOException + */ + private boolean findNext() throws IOException + { + if(root == null) + { + more = false; + max= NO_MORE_DOCS; + return false; + } + + // Move to the next document + + while (more) + { + move(); // may set more to false + if (found()) + { + return true; + } + } + + // If we get here we must have no more documents + return false; + } + + /** + * Check if we have found a match + * + * @return + * @throws IOException + */ + + private boolean found() throws IOException + { + // No predicate test if there are no positions + if (positions.length == 0) + { + return true; + } + + // no more documents - no match + if (!more) + { + return false; + } + + // min and max must point to the same document + if (min != max) + { + return false; + } + + if (rootDoc != max) + { + return false; + } + + // We have duplicate entries - support should be improved but it is not used at the moment + // This should work akin to the leaf scorer + // It would compact the index + // The match must be in a known term range + int count = root.freq(); + int start = 0; + int end = -1; + for (int i = 0; i < count; i++) + { + if (i == 0) + { + // First starts at zero + start = 0; + end = root.nextPosition() ; + } + else + { + start = end + 1; + end = root.nextPosition() ; + } + + if (check(start, end)) + { + return true; + } + } + + // We had checks to do and they all failed. + return false; + } + + /* + * We have all documents at the same state. Now we check the positions of + * the terms. + */ + + private boolean check(int start, int end) throws IOException + { + int offset = checkTail(start, end, 0, 0, true); + // Last match may fail + if (offset == -1) + { + return false; + } + else + { + return true; + } + } + + /** + * For // type pattern matches we need to test patterns of variable greedyness. + * + * + * @param start - first term position + * @param end - end term position marker + * @param currentPosition - current path match index + * @param currentOffset - current path position starting at zero (position being considered is start+offset) + * @return + * @throws IOException + */ + private int checkTail(int start, int end, int currentPosition, int currentOffset, boolean checkLastMatch) throws IOException + { + // pre check last can match some combination or we are done + if(checkLastMatch) + { + if(end-start > 1) + { + StructuredFieldPosition last = getLastPositionNotSelfCheck(); + if(last != null) + { + if(last.matches(start, end, end-start-2) == -1) + { + return -1; + } + } + } + } + + int offset = currentOffset; + for (int i = currentPosition, l = positions.length; i < l; i++) + { + offset = positions[i].matches(start, end, offset); + if (offset == -1) + { + return -1; + } + if (positions[i].isDescendant()) + { + // go up in twos as there are two parts to a QName + for (int j = offset; j < (end - start); j+=2) + { + int newOffset = checkTail(start, end, i + 1, j, false); + if (newOffset != -1) + { + return newOffset; + } + } + return -1; + } + } + + if((start+offset+1) == end) + { + return offset; + } + else + { + return -1; + } + } + + private StructuredFieldPosition getLastPositionNotSelfCheck() + { + for(int i = positions.length-1; i >= 0; i--) + { + if(positions[i].linkSelf()) + { + continue; + } + return positions[i]; + } + return null; + } + + /* + * Move to the next position to consider for a match test + */ + + private void move() throws IOException + { + if (min == max) + { + // If we were at a match just do next on all terms + // They all must move on + doNextOnAll(); + } + else + { + // We are in a range - try and skip to the max position on all terms + // Only some need to move on - some may move past the current max and set a new target + skipToMax(); + } + } + + /* + * Go through all the term positions and try and move to next document. Any + * failure measn we have no more. + * + * This can be used at initialisation and when moving away from an existing + * match. + * + * This will set min, max, more and rootDoc + * + */ + private void doNextOnAll() throws IOException + { + if(root == null) + { + more = false; + max = NO_MORE_DOCS; + return; + } + // Do the terms + int current; + boolean first = true; + for (int i = 0, l = positions.length; i < l; i++) + { + if (positions[i].getCachingTermPositions() != null) + { + if (positions[i].getCachingTermPositions().nextDoc() != NO_MORE_DOCS) + + { + current = positions[i].getCachingTermPositions().docID(); + adjustMinMax(current, first); + first = false; + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + } + } + + // Do the root term - it must always exists as the path could well have multiple entries + // If an entry in the index does not have a root terminal it is broken + if (root.nextDoc() != NO_MORE_DOCS) + { + rootDoc = root.docID(); + if(first) + { + adjustMinMax(rootDoc, first); + } + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + if (root.docID() < max) + { + if (root.advance(max) != NO_MORE_DOCS) + { + rootDoc = root.docID(); + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + } + } + + /* + * Try and skip all those term positions at documents less than the current + * max up to value. This is quite likely to fail and leave us with (min != + * max) but that is OK, we try again. + * + * It is possible that max increases as we process terms, this is OK. We + * just failed to skip to a given value of max and start doing the next. + */ + private void skipToMax() throws IOException + { + // Do the terms + int current; + for (int i = 0, l = positions.length; i < l; i++) + { + if (i == 0) + { + min = max; + } + if (positions[i].getCachingTermPositions() != null) + { + if (positions[i].getCachingTermPositions().docID() < max) + { + if (positions[i].getCachingTermPositions().advance(max) != NO_MORE_DOCS) + { + current = positions[i].getCachingTermPositions().docID(); + adjustMinMax(current, false); + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + } + } + } + + // Do the root + if (root.docID() < max) + { + if (root.advance(max) != NO_MORE_DOCS) + { + rootDoc = root.docID(); + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + } + } + + /* + * Adjust the min and max values Convenience boolean to set or adjust the + * minimum. + */ + private void adjustMinMax(int doc, boolean setMin) + { + + if (max < doc) + { + max = doc; + } + + if (setMin) + { + min = doc; + } + else if (min > doc) + { + min = doc; + } + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.search.DocIdSetIterator#docID() + */ + @Override + public int docID() + { + return max; + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.DocIdSetIterator#nextDoc() + */ + @Override + public int nextDoc() throws IOException + { + boolean found = next(); + if(found) + { + return docID(); + } + else + { + return NO_MORE_DOCS; + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.DocIdSetIterator#advance(int) + */ + @Override + public int advance(int target) throws IOException + { + if(target == NO_MORE_DOCS) + { + more = false; + max = NO_MORE_DOCS; + return NO_MORE_DOCS; + } + + max = target; + boolean found = findNext(); + if(found) + { + return docID(); + } + else + { + return NO_MORE_DOCS; + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.DocIdSetIterator#cost() + */ + @Override + public long cost() + { + return 1; + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedQuery.java index 8c2fd2523..3f2899f13 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedQuery.java @@ -1,90 +1,90 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * - * @author Matt Ward - */ -public class SolrDeniedQuery extends AbstractAuthorityQuery -{ - public SolrDeniedQuery(String authority) - { - super(authority); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_DENIED).append(':'); - stringBuilder.append(authority); - return stringBuilder.toString(); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean requiresScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrDenyQueryWeight((SolrIndexSearcher)searcher, this, authority); - } - - private class SolrDenyQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrDenyQueryWeight(SolrIndexSearcher searcher, Query query, String reader) throws IOException - { - super(searcher, false, query, QueryConstants.FIELD_DENIED, reader); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrDeniedScorer.createDenyScorer(this, context, searcher, authority); - } - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_DENIED, authority)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * + * @author Matt Ward + */ +public class SolrDeniedQuery extends AbstractAuthorityQuery +{ + public SolrDeniedQuery(String authority) + { + super(authority); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_DENIED).append(':'); + stringBuilder.append(authority); + return stringBuilder.toString(); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean requiresScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrDenyQueryWeight((SolrIndexSearcher)searcher, this, authority); + } + + private class SolrDenyQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrDenyQueryWeight(SolrIndexSearcher searcher, Query query, String reader) throws IOException + { + super(searcher, false, query, QueryConstants.FIELD_DENIED, reader); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrDeniedScorer.createDenyScorer(this, context, searcher, authority); + } + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_DENIED, authority)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedScorer.java index 4b5d29682..6a86623fc 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedScorer.java @@ -1,89 +1,89 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Find the set of docs denied to an authority. - * - * @author Matt Ward - */ -public class SolrDeniedScorer extends AbstractSolrCachingScorer -{ - SolrDeniedScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrDeniedScorer createDenyScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException - { - DocSet deniedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_DENIED_CACHE, authority); - - if (deniedDocs == null) - { - // Cache miss: query the index for ACL docs where the denial matches the authority. - DocSet aclDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_DENIED, authority))); - - // Allocate a bitset to store the results. - deniedDocs = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - // Translate from ACL docs to real docs - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); - SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); - Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); - // Find real docs that match the ACL ID - DocSet docsForAclId = searcher.getDocSet(query); - deniedDocs = deniedDocs.union(docsForAclId); - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - deniedDocs = deniedDocs.andNot(aclDocs); - } - - searcher.cacheInsert(CacheConstants.ALFRESCO_DENIED_CACHE, authority, deniedDocs); - } - return new SolrDeniedScorer(weight, deniedDocs, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Find the set of docs denied to an authority. + * + * @author Matt Ward + */ +public class SolrDeniedScorer extends AbstractSolrCachingScorer +{ + SolrDeniedScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrDeniedScorer createDenyScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException + { + DocSet deniedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_DENIED_CACHE, authority); + + if (deniedDocs == null) + { + // Cache miss: query the index for ACL docs where the denial matches the authority. + DocSet aclDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_DENIED, authority))); + + // Allocate a bitset to store the results. + deniedDocs = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + // Translate from ACL docs to real docs + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); + SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); + Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); + // Find real docs that match the ACL ID + DocSet docsForAclId = searcher.getDocSet(query); + deniedDocs = deniedDocs.union(docsForAclId); + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + deniedDocs = deniedDocs.andNot(aclDocs); + } + + searcher.cacheInsert(CacheConstants.ALFRESCO_DENIED_CACHE, authority, deniedDocs); + } + return new SolrDeniedScorer(weight, deniedDocs, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetQuery.java index e7f6329b3..be6999a80 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetQuery.java @@ -1,156 +1,156 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DelegatingCollector; -import org.apache.solr.search.PostFilter; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Query for a set of denied authorities. - * - * @author Joel Bernstein - */ -public class SolrDenySetQuery extends AbstractAuthoritySetQuery implements PostFilter -{ - public SolrDenySetQuery(String authorities) - { - super(authorities); - } - - public int getCost() - { - return 201; - } - - public void setCost(int cost) - { - - } - - public void setCache(boolean cache) - { - - } - - public boolean getCache() { - return true; - } - - public boolean getCacheSep() - { - return false; - } - - public void setCacheSep(boolean cacheSep) - { - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean requiresScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - BitsFilter denyFilter = getACLFilter(auths, QueryConstants.FIELD_DENIED, (SolrIndexSearcher) searcher); - return new ConstantScoreQuery(denyFilter).createWeight(searcher, false); - } - - public DelegatingCollector getFilterCollector(IndexSearcher searcher) - { - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - try - { - HybridBitSet denySet = getACLSet(auths, QueryConstants.FIELD_DENIED, (SolrIndexSearcher) searcher); - if(denySet instanceof EmptyHybridBitSet) - { - return new AllAccessCollector(); - } - else - { - return new AccessControlCollector(denySet); - } - } - catch(Exception e) - { - throw new RuntimeException(e); - } - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_DENYSET).append(':'); - stringBuilder.append(authorities); - return stringBuilder.toString(); - } - - class AccessControlCollector extends DelegatingCollector - { - private HybridBitSet aclIds; - private NumericDocValues fieldValues; - - public AccessControlCollector(HybridBitSet aclIds) - { - this.aclIds=aclIds; - } - - public boolean acceptsDocsOutOfOrder() - { - return false; - } - - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); - } - - public void collect(int doc) throws IOException{ - - long aclId = this.fieldValues.get(doc); - - if(!aclIds.get(aclId)) - { - super.collect(doc); - } - - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DelegatingCollector; +import org.apache.solr.search.PostFilter; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Query for a set of denied authorities. + * + * @author Joel Bernstein + */ +public class SolrDenySetQuery extends AbstractAuthoritySetQuery implements PostFilter +{ + public SolrDenySetQuery(String authorities) + { + super(authorities); + } + + public int getCost() + { + return 201; + } + + public void setCost(int cost) + { + + } + + public void setCache(boolean cache) + { + + } + + public boolean getCache() { + return true; + } + + public boolean getCacheSep() + { + return false; + } + + public void setCacheSep(boolean cacheSep) + { + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean requiresScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + BitsFilter denyFilter = getACLFilter(auths, QueryConstants.FIELD_DENIED, (SolrIndexSearcher) searcher); + return new ConstantScoreQuery(denyFilter).createWeight(searcher, false); + } + + public DelegatingCollector getFilterCollector(IndexSearcher searcher) + { + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + try + { + HybridBitSet denySet = getACLSet(auths, QueryConstants.FIELD_DENIED, (SolrIndexSearcher) searcher); + if(denySet instanceof EmptyHybridBitSet) + { + return new AllAccessCollector(); + } + else + { + return new AccessControlCollector(denySet); + } + } + catch(Exception e) + { + throw new RuntimeException(e); + } + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_DENYSET).append(':'); + stringBuilder.append(authorities); + return stringBuilder.toString(); + } + + class AccessControlCollector extends DelegatingCollector + { + private HybridBitSet aclIds; + private NumericDocValues fieldValues; + + public AccessControlCollector(HybridBitSet aclIds) + { + this.aclIds=aclIds; + } + + public boolean acceptsDocsOutOfOrder() + { + return false; + } + + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); + } + + public void collect(int doc) throws IOException{ + + long aclId = this.fieldValues.get(doc); + + if(!aclIds.get(aclId)) + { + super.collect(doc); + } + + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer.java index 398480b2a..6d21b23d9 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer2.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer2.java index fe951a5c7..62f991b78 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer2.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer2.java @@ -1,125 +1,125 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.HashSet; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -public class SolrDenySetScorer2 extends AbstractSolrCachingScorer -{ - - SolrDenySetScorer2(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrDenySetScorer2 createDenySetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException - { - DocSet deniedDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_DENIED_CACHE, authorities); - - if (deniedDocSet == null) - { - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - deniedDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - for(String current : auths) - { - bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_DENIED, current)), Occur.SHOULD); - } - WrappedQuery wrapped = new WrappedQuery(bQuery.build()); - wrapped.setCache(false); - - DocSet aclDocs = searcher.getDocSet(wrapped); - - HashSet aclsFound = new HashSet(aclDocs.size()); - NumericDocValues aclDocValues = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID); - - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = aclDocValues.get(docID); - aclsFound.add(getLong(aclID)); - } - - if(aclsFound.size() > 0) - { - for(LeafReaderContext readerContext : searcher.getSlowAtomicReader().leaves() ) - { - int maxDoc = readerContext.reader().maxDoc(); - NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, readerContext.reader()); - if(fieldValues != null) - { - for(int i = 0; i < maxDoc; i++) - { - long aclID = fieldValues.get(i); - Long key = getLong(aclID); - if(aclsFound.contains(key)) - { - deniedDocSet.add(readerContext.docBase + i); - } - } - } - - } - } - - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - deniedDocSet = deniedDocSet.andNot(aclDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_DENIED_CACHE, authorities, deniedDocSet); - } - - // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) - // plus check of course, for presence in cache at start of method. - return new SolrDenySetScorer2(weight, deniedDocSet, context, searcher); - - - - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.HashSet; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +public class SolrDenySetScorer2 extends AbstractSolrCachingScorer +{ + + SolrDenySetScorer2(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrDenySetScorer2 createDenySetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException + { + DocSet deniedDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_DENIED_CACHE, authorities); + + if (deniedDocSet == null) + { + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + deniedDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + for(String current : auths) + { + bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_DENIED, current)), Occur.SHOULD); + } + WrappedQuery wrapped = new WrappedQuery(bQuery.build()); + wrapped.setCache(false); + + DocSet aclDocs = searcher.getDocSet(wrapped); + + HashSet aclsFound = new HashSet(aclDocs.size()); + NumericDocValues aclDocValues = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID); + + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = aclDocValues.get(docID); + aclsFound.add(getLong(aclID)); + } + + if(aclsFound.size() > 0) + { + for(LeafReaderContext readerContext : searcher.getSlowAtomicReader().leaves() ) + { + int maxDoc = readerContext.reader().maxDoc(); + NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, readerContext.reader()); + if(fieldValues != null) + { + for(int i = 0; i < maxDoc; i++) + { + long aclID = fieldValues.get(i); + Long key = getLong(aclID); + if(aclsFound.contains(key)) + { + deniedDocSet.add(readerContext.docBase + i); + } + } + } + + } + } + + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + deniedDocSet = deniedDocSet.andNot(aclDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_DENIED_CACHE, authorities, deniedDocSet); + } + + // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) + // plus check of course, for presence in cache at start of method. + return new SolrDenySetScorer2(weight, deniedDocSet, context, searcher); + + + + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerQuery.java index 153b8c824..5884ba683 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerQuery.java @@ -1,127 +1,127 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Collector; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.LeafCollector; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * @author Andy - * - */ -public class SolrOwnerQuery extends AbstractAuthorityQuery -{ - public SolrOwnerQuery(String authority) - { - super(authority); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - - BitsFilter ownerFilter = getOwnerFilter(authority, (SolrIndexSearcher)searcher); - return new ConstantScoreQuery(ownerFilter).createWeight(searcher, false); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_OWNER).append(':'); - stringBuilder.append(authority); - return stringBuilder.toString(); - } - - private BitsFilter getOwnerFilter(String owner, SolrIndexSearcher searcher) throws IOException - { - Query query = new TermQuery(new Term(QueryConstants.FIELD_OWNER, owner)); - BitsFilterCollector collector = new BitsFilterCollector(searcher.getTopReaderContext().leaves().size()); - searcher.search(query, collector); - return collector.getBitsFilter(); - } - - class BitsFilterCollector implements Collector, LeafCollector - { - private List sets; - private FixedBitSet set; - - public BitsFilterCollector(int leafCount) - { - this.sets = new ArrayList(leafCount); - } - - public BitsFilter getBitsFilter() { - return new BitsFilter(sets); - } - - public boolean acceptsDocsOutOfOrder() { - return false; - } - - public void setScorer(Scorer scorer) { - - } - - public void collect(int doc) { - set.set(doc); - } - - @Override - public LeafCollector getLeafCollector(LeafReaderContext context) - throws IOException { - set = new FixedBitSet(context.reader().maxDoc()); - sets.add(set); - return this; - } - - @Override - public boolean needsScores() { - // TODO Auto-generated method stub - return false; - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.LeafCollector; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * @author Andy + * + */ +public class SolrOwnerQuery extends AbstractAuthorityQuery +{ + public SolrOwnerQuery(String authority) + { + super(authority); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + + BitsFilter ownerFilter = getOwnerFilter(authority, (SolrIndexSearcher)searcher); + return new ConstantScoreQuery(ownerFilter).createWeight(searcher, false); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_OWNER).append(':'); + stringBuilder.append(authority); + return stringBuilder.toString(); + } + + private BitsFilter getOwnerFilter(String owner, SolrIndexSearcher searcher) throws IOException + { + Query query = new TermQuery(new Term(QueryConstants.FIELD_OWNER, owner)); + BitsFilterCollector collector = new BitsFilterCollector(searcher.getTopReaderContext().leaves().size()); + searcher.search(query, collector); + return collector.getBitsFilter(); + } + + class BitsFilterCollector implements Collector, LeafCollector + { + private List sets; + private FixedBitSet set; + + public BitsFilterCollector(int leafCount) + { + this.sets = new ArrayList(leafCount); + } + + public BitsFilter getBitsFilter() { + return new BitsFilter(sets); + } + + public boolean acceptsDocsOutOfOrder() { + return false; + } + + public void setScorer(Scorer scorer) { + + } + + public void collect(int doc) { + set.set(doc); + } + + @Override + public LeafCollector getLeafCollector(LeafReaderContext context) + throws IOException { + set = new FixedBitSet(context.reader().maxDoc()); + sets.add(set); + return this; + } + + @Override + public boolean needsScores() { + // TODO Auto-generated method stub + return false; + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerScorer.java index 7138b1e47..d175adddb 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerScorer.java @@ -1,75 +1,75 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Find the set of documents for which the specified authority is the owner. - * If the authority is not a user then an empty set is returned. - * - * @author Matt Ward - */ -public class SolrOwnerScorer extends AbstractSolrCachingScorer -{ - SolrOwnerScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrOwnerScorer createOwnerScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException - { - if (AuthorityType.getAuthorityType(authority) == AuthorityType.USER) - { - DocSet ownedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authority); - - if (ownedDocs == null) - { - // Cache miss: query the index for docs where the owner matches the authority. - ownedDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_OWNER, authority))); - searcher.cacheInsert(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authority, ownedDocs); - } - return new SolrOwnerScorer(weight, ownedDocs, context, searcher); - } - - // Return an empty doc set, as the authority isn't a user. - return new SolrOwnerScorer(weight, new BitDocSet(new FixedBitSet(0)), context, searcher); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Find the set of documents for which the specified authority is the owner. + * If the authority is not a user then an empty set is returned. + * + * @author Matt Ward + */ +public class SolrOwnerScorer extends AbstractSolrCachingScorer +{ + SolrOwnerScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrOwnerScorer createOwnerScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException + { + if (AuthorityType.getAuthorityType(authority) == AuthorityType.USER) + { + DocSet ownedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authority); + + if (ownedDocs == null) + { + // Cache miss: query the index for docs where the owner matches the authority. + ownedDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_OWNER, authority))); + searcher.cacheInsert(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authority, ownedDocs); + } + return new SolrOwnerScorer(weight, ownedDocs, context, searcher); + } + + // Return an empty doc set, as the authority isn't a user. + return new SolrOwnerScorer(weight, new BitDocSet(new FixedBitSet(0)), context, searcher); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetQuery.java index dcb31e3d5..b7be3b8f6 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetQuery.java @@ -1,92 +1,92 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * @author Andy - * - */ -public class SolrOwnerSetQuery extends AbstractAuthoritySetQuery -{ - public SolrOwnerSetQuery(String authorities) - { - super(authorities); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrOwnerSetQueryWeight((SolrIndexSearcher)searcher, this, authorities); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_OWNERSET).append(':'); - stringBuilder.append(authorities); - return stringBuilder.toString(); - } - - - private class SolrOwnerSetQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrOwnerSetQueryWeight(SolrIndexSearcher searcher, Query query, String authorities) throws IOException - { - super(searcher, false, query, QueryConstants.FIELD_OWNERSET, authorities); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrOwnerSetScorer.createOwnerSetScorer(this, context, searcher, authorities); - } - - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_OWNERSET, authorities)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * @author Andy + * + */ +public class SolrOwnerSetQuery extends AbstractAuthoritySetQuery +{ + public SolrOwnerSetQuery(String authorities) + { + super(authorities); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrOwnerSetQueryWeight((SolrIndexSearcher)searcher, this, authorities); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_OWNERSET).append(':'); + stringBuilder.append(authorities); + return stringBuilder.toString(); + } + + + private class SolrOwnerSetQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrOwnerSetQueryWeight(SolrIndexSearcher searcher, Query query, String authorities) throws IOException + { + super(searcher, false, query, QueryConstants.FIELD_OWNERSET, authorities); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrOwnerSetScorer.createOwnerSetScorer(this, context, searcher, authorities); + } + + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_OWNERSET, authorities)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetScorer.java index e88a7c3af..76a426ed5 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetScorer.java @@ -1,92 +1,92 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -/** - * Find the set of documents owned by the specified set of authorities, - * for those authorities that are users (e.g. we're not interested in groups etc.) - * - * @author Matt Ward - */ -public class SolrOwnerSetScorer extends AbstractSolrCachingScorer -{ - /** - * Package private constructor. - * @param acceptDocs - */ - SolrOwnerSetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrOwnerSetScorer createOwnerSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities) throws IOException - { - - DocSet authorityOwnedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authorities); - - if(authorityOwnedDocs == null) - { - // Split the authorities. The first character in the authorities String - // specifies the separator, e.g. ",jbloggs,abeecher" - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - for(String current : auths) - { - if (AuthorityType.getAuthorityType(current) == AuthorityType.USER) - { - bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_OWNER, current)), Occur.SHOULD); - } - } - - WrappedQuery wrapped = new WrappedQuery(bQuery.build()); - wrapped.setCache(false); - authorityOwnedDocs = searcher.getDocSet(wrapped); - - searcher.cacheInsert(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authorities, authorityOwnedDocs); - } - - // TODO: Cache the final set? e.g. searcher.cacheInsert(authorities, authorityOwnedDocs) - return new SolrOwnerSetScorer(weight, authorityOwnedDocs, context, searcher); - - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +/** + * Find the set of documents owned by the specified set of authorities, + * for those authorities that are users (e.g. we're not interested in groups etc.) + * + * @author Matt Ward + */ +public class SolrOwnerSetScorer extends AbstractSolrCachingScorer +{ + /** + * Package private constructor. + * @param acceptDocs + */ + SolrOwnerSetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrOwnerSetScorer createOwnerSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities) throws IOException + { + + DocSet authorityOwnedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authorities); + + if(authorityOwnedDocs == null) + { + // Split the authorities. The first character in the authorities String + // specifies the separator, e.g. ",jbloggs,abeecher" + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + for(String current : auths) + { + if (AuthorityType.getAuthorityType(current) == AuthorityType.USER) + { + bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_OWNER, current)), Occur.SHOULD); + } + } + + WrappedQuery wrapped = new WrappedQuery(bQuery.build()); + wrapped.setCache(false); + authorityOwnedDocs = searcher.getDocSet(wrapped); + + searcher.cacheInsert(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authorities, authorityOwnedDocs); + } + + // TODO: Cache the final set? e.g. searcher.cacheInsert(authorities, authorityOwnedDocs) + return new SolrOwnerSetScorer(weight, authorityOwnedDocs, context, searcher); + + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathQuery.java index 051231996..b25f2dbc2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathQuery.java @@ -1,337 +1,337 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; - -/** - * An extension to the Lucene query set. This query supports structured queries against paths. The field must have been - * tokenised using the path tokeniser. This class manages linking together an ordered chain of absolute and relative - * positional queries. - * - * @author Andy Hind - */ -public class SolrPathQuery extends Query -{ - private String pathField = "PATH"; - - private int unitSize = 2; - - private List pathStructuredFieldPositions = new ArrayList(); - - private DictionaryService dictionaryService; - - private boolean repeats = false; - - /** - * The base query - * - * @param dictionaryService DictionaryService - */ - - public SolrPathQuery(DictionaryService dictionaryService) - { - super(); - this.dictionaryService = dictionaryService; - } - - public void setQuery(List path) - { - pathStructuredFieldPositions.clear(); - if (path.size() % unitSize != 0) - { - throw new UnsupportedOperationException(); - } - pathStructuredFieldPositions.addAll(path); - } - - public void appendQuery(List sfps) - { - if (sfps.size() != unitSize) - { - throw new UnsupportedOperationException(); - } - - StructuredFieldPosition last = null; - StructuredFieldPosition next = sfps.get(0); - - if (pathStructuredFieldPositions.size() > 0) - { - last = pathStructuredFieldPositions.get(pathStructuredFieldPositions.size() - 1); - } - - if ((last != null) && next.linkParent() && !last.allowslinkingByParent()) - { - return; - } - - if ((last != null) && next.linkSelf() && !last.allowsLinkingBySelf()) - { - return; - } - - pathStructuredFieldPositions.addAll(sfps); - } - - public String getPathField() - { - return pathField; - } - - public void setPathField(String pathField) - { - this.pathField = pathField; - } - - public Term getPathRootTerm() - { - return new Term(getPathField(), ";"); - } - - ArrayList getTerms() - { - ArrayList answer = new ArrayList(pathStructuredFieldPositions.size()); - for (StructuredFieldPosition sfp : pathStructuredFieldPositions) - { - if (sfp.getTermText() != null) - { - Term term = new Term(pathField, sfp.getTermText()); - answer.add(term); - } - } - return answer; - } - - /* - * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) - */ - public Weight createWeight(IndexSearcher searcher, boolean needsScore) - { - return new StructuredFieldWeight(); - } - - /* - * @see java.lang.Object#toString() - */ - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("PATH:"); - for(int i = 0; i < pathStructuredFieldPositions.size(); i+=2) - { - if(pathStructuredFieldPositions.get(i) instanceof AbsoluteStructuredFieldPosition) - { - stringBuilder.append("/"); - if((pathStructuredFieldPositions.get(i).getTermText() == null) && (pathStructuredFieldPositions.get(i+1).getTermText() == null)) - { - stringBuilder.append("*"); - } - else - { - stringBuilder.append("{"); - stringBuilder.append(pathStructuredFieldPositions.get(i).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i).getTermText()); - stringBuilder.append("}:"); - stringBuilder.append(pathStructuredFieldPositions.get(i+1).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i+1).getTermText()); - - } - } - else if(pathStructuredFieldPositions.get(i) instanceof DescendantAndSelfStructuredFieldPosition) - { - stringBuilder.append("//"); - } - else if(pathStructuredFieldPositions.get(i) instanceof RelativeStructuredFieldPosition) - { - stringBuilder.append("/"); - if((pathStructuredFieldPositions.get(i).getTermText() == null) && (pathStructuredFieldPositions.get(i+1).getTermText() == null)) - { - stringBuilder.append("*"); - } - else - { - stringBuilder.append(pathStructuredFieldPositions.get(i).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i).getTermText()); - stringBuilder.append(":"); - stringBuilder.append(pathStructuredFieldPositions.get(i+1).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i+1).getTermText()); - - } - } - else if(pathStructuredFieldPositions.get(i) instanceof SelfAxisStructuredFieldPosition) - { - stringBuilder.append("."); - } - } - return stringBuilder.toString(); - } - - private class StructuredFieldWeight extends Weight - { - public StructuredFieldWeight() - { - super(SolrPathQuery.this); - } - - - /* (non-Javadoc) - * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.AtomicReaderContext, int) - */ - @Override - public Explanation explain(LeafReaderContext context, int doc) throws IOException - { - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.Weight#getValueForNormalization() - */ - @Override - public float getValueForNormalization() throws IOException - { - return 1.0f; - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.Weight#normalize(float, float) - */ - @Override - public void normalize(float norm, float topLevelBoost) - { - - - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.AtomicReaderContext, org.apache.lucene.util.Bits) - */ - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrPathScorer.createPathScorer(SolrPathQuery.this, context, this, dictionaryService, repeats); - } - - - @Override - public void extractTerms(Set terms) { - throw new UnsupportedOperationException(); - - } - } - - public void removeDescendantAndSelf() - { - while ((getLast() != null) && getLast().linkSelf()) - { - removeLast(); - removeLast(); - } - } - - private StructuredFieldPosition getLast() - - { - if (pathStructuredFieldPositions.size() > 0) - { - return pathStructuredFieldPositions.get(pathStructuredFieldPositions.size() - 1); - } - else - { - return null; - } - } - - private void removeLast() - { - pathStructuredFieldPositions.remove(pathStructuredFieldPositions.size() - 1); - } - - public boolean isEmpty() - { - return pathStructuredFieldPositions.size() == 0; - } - - public List getPathStructuredFieldPositions() - { - return pathStructuredFieldPositions; - } - - public void setRepeats(boolean repeats) - { - this.repeats = repeats; - } - - @Override - public int hashCode() { - int result = pathField != null ? pathField.hashCode() : 0; - result = 31 * result + unitSize; - result = 31 * result + (pathStructuredFieldPositions != null ? pathStructuredFieldPositions.hashCode() : 0); - result = 31 * result + (repeats ? 1 : 0); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (getClass() != obj.getClass()) - return false; - SolrPathQuery other = (SolrPathQuery) obj; - if (pathField == null) - { - if (other.pathField != null) - return false; - } - else if (!pathField.equals(other.pathField)) - return false; - if (pathStructuredFieldPositions == null) - { - if (other.pathStructuredFieldPositions != null) - return false; - } - else if (!pathStructuredFieldPositions.equals(other.pathStructuredFieldPositions)) - return false; - if (repeats != other.repeats) - return false; - if (unitSize != other.unitSize) - return false; - return true; - } - - - - -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; + +/** + * An extension to the Lucene query set. This query supports structured queries against paths. The field must have been + * tokenised using the path tokeniser. This class manages linking together an ordered chain of absolute and relative + * positional queries. + * + * @author Andy Hind + */ +public class SolrPathQuery extends Query +{ + private String pathField = "PATH"; + + private int unitSize = 2; + + private List pathStructuredFieldPositions = new ArrayList(); + + private DictionaryService dictionaryService; + + private boolean repeats = false; + + /** + * The base query + * + * @param dictionaryService DictionaryService + */ + + public SolrPathQuery(DictionaryService dictionaryService) + { + super(); + this.dictionaryService = dictionaryService; + } + + public void setQuery(List path) + { + pathStructuredFieldPositions.clear(); + if (path.size() % unitSize != 0) + { + throw new UnsupportedOperationException(); + } + pathStructuredFieldPositions.addAll(path); + } + + public void appendQuery(List sfps) + { + if (sfps.size() != unitSize) + { + throw new UnsupportedOperationException(); + } + + StructuredFieldPosition last = null; + StructuredFieldPosition next = sfps.get(0); + + if (pathStructuredFieldPositions.size() > 0) + { + last = pathStructuredFieldPositions.get(pathStructuredFieldPositions.size() - 1); + } + + if ((last != null) && next.linkParent() && !last.allowslinkingByParent()) + { + return; + } + + if ((last != null) && next.linkSelf() && !last.allowsLinkingBySelf()) + { + return; + } + + pathStructuredFieldPositions.addAll(sfps); + } + + public String getPathField() + { + return pathField; + } + + public void setPathField(String pathField) + { + this.pathField = pathField; + } + + public Term getPathRootTerm() + { + return new Term(getPathField(), ";"); + } + + ArrayList getTerms() + { + ArrayList answer = new ArrayList(pathStructuredFieldPositions.size()); + for (StructuredFieldPosition sfp : pathStructuredFieldPositions) + { + if (sfp.getTermText() != null) + { + Term term = new Term(pathField, sfp.getTermText()); + answer.add(term); + } + } + return answer; + } + + /* + * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) + */ + public Weight createWeight(IndexSearcher searcher, boolean needsScore) + { + return new StructuredFieldWeight(); + } + + /* + * @see java.lang.Object#toString() + */ + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("PATH:"); + for(int i = 0; i < pathStructuredFieldPositions.size(); i+=2) + { + if(pathStructuredFieldPositions.get(i) instanceof AbsoluteStructuredFieldPosition) + { + stringBuilder.append("/"); + if((pathStructuredFieldPositions.get(i).getTermText() == null) && (pathStructuredFieldPositions.get(i+1).getTermText() == null)) + { + stringBuilder.append("*"); + } + else + { + stringBuilder.append("{"); + stringBuilder.append(pathStructuredFieldPositions.get(i).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i).getTermText()); + stringBuilder.append("}:"); + stringBuilder.append(pathStructuredFieldPositions.get(i+1).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i+1).getTermText()); + + } + } + else if(pathStructuredFieldPositions.get(i) instanceof DescendantAndSelfStructuredFieldPosition) + { + stringBuilder.append("//"); + } + else if(pathStructuredFieldPositions.get(i) instanceof RelativeStructuredFieldPosition) + { + stringBuilder.append("/"); + if((pathStructuredFieldPositions.get(i).getTermText() == null) && (pathStructuredFieldPositions.get(i+1).getTermText() == null)) + { + stringBuilder.append("*"); + } + else + { + stringBuilder.append(pathStructuredFieldPositions.get(i).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i).getTermText()); + stringBuilder.append(":"); + stringBuilder.append(pathStructuredFieldPositions.get(i+1).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i+1).getTermText()); + + } + } + else if(pathStructuredFieldPositions.get(i) instanceof SelfAxisStructuredFieldPosition) + { + stringBuilder.append("."); + } + } + return stringBuilder.toString(); + } + + private class StructuredFieldWeight extends Weight + { + public StructuredFieldWeight() + { + super(SolrPathQuery.this); + } + + + /* (non-Javadoc) + * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.AtomicReaderContext, int) + */ + @Override + public Explanation explain(LeafReaderContext context, int doc) throws IOException + { + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.Weight#getValueForNormalization() + */ + @Override + public float getValueForNormalization() throws IOException + { + return 1.0f; + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.Weight#normalize(float, float) + */ + @Override + public void normalize(float norm, float topLevelBoost) + { + + + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.AtomicReaderContext, org.apache.lucene.util.Bits) + */ + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrPathScorer.createPathScorer(SolrPathQuery.this, context, this, dictionaryService, repeats); + } + + + @Override + public void extractTerms(Set terms) { + throw new UnsupportedOperationException(); + + } + } + + public void removeDescendantAndSelf() + { + while ((getLast() != null) && getLast().linkSelf()) + { + removeLast(); + removeLast(); + } + } + + private StructuredFieldPosition getLast() + + { + if (pathStructuredFieldPositions.size() > 0) + { + return pathStructuredFieldPositions.get(pathStructuredFieldPositions.size() - 1); + } + else + { + return null; + } + } + + private void removeLast() + { + pathStructuredFieldPositions.remove(pathStructuredFieldPositions.size() - 1); + } + + public boolean isEmpty() + { + return pathStructuredFieldPositions.size() == 0; + } + + public List getPathStructuredFieldPositions() + { + return pathStructuredFieldPositions; + } + + public void setRepeats(boolean repeats) + { + this.repeats = repeats; + } + + @Override + public int hashCode() { + int result = pathField != null ? pathField.hashCode() : 0; + result = 31 * result + unitSize; + result = 31 * result + (pathStructuredFieldPositions != null ? pathStructuredFieldPositions.hashCode() : 0); + result = 31 * result + (repeats ? 1 : 0); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (getClass() != obj.getClass()) + return false; + SolrPathQuery other = (SolrPathQuery) obj; + if (pathField == null) + { + if (other.pathField != null) + return false; + } + else if (!pathField.equals(other.pathField)) + return false; + if (pathStructuredFieldPositions == null) + { + if (other.pathStructuredFieldPositions != null) + return false; + } + else if (!pathStructuredFieldPositions.equals(other.pathStructuredFieldPositions)) + return false; + if (repeats != other.repeats) + return false; + if (unitSize != other.unitSize) + return false; + return true; + } + + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathScorer.java index d8e409b27..157e8dce3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathScorer.java @@ -1,128 +1,128 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; - -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; - -public class SolrPathScorer extends Scorer -{ - Scorer scorer; - - SolrPathScorer(Weight weight, Scorer scorer) - { - super(weight); - this.scorer = scorer; - } - - - public static SolrPathScorer createPathScorer(SolrPathQuery solrPathQuery, LeafReaderContext context, Weight weight, DictionaryService dictionarySertvice, boolean repeat) throws IOException - { - -// StructuredFieldPosition last = null; -// if(solrPathQuery.getPathStructuredFieldPositions().size() > 0) -// { -// last = solrPathQuery.getPathStructuredFieldPositions().get(solrPathQuery.getPathStructuredFieldPositions().size() - 1); -// } - - - if (solrPathQuery.getPathStructuredFieldPositions().size() == 0) - { - ArrayList answer = new ArrayList(2); - answer.add(new SelfAxisStructuredFieldPosition()); - answer.add(new SelfAxisStructuredFieldPosition()); - - solrPathQuery.appendQuery(answer); - } - - - for (StructuredFieldPosition sfp : solrPathQuery.getPathStructuredFieldPositions()) - { - if (sfp.getTermText() != null) - { - PostingsEnum p = context.reader().postings(new Term(solrPathQuery.getPathField(), sfp.getTermText()), PostingsEnum.POSITIONS); - if (p == null) - return null; - CachingTermPositions ctp = new CachingTermPositions(p); - sfp.setCachingTermPositions(ctp); - } - } - - SolrContainerScorer cs = null; - - PostingsEnum rootContainerPositions = null; - if (solrPathQuery.getPathRootTerm() != null) - { - rootContainerPositions = context.reader().postings(solrPathQuery.getPathRootTerm(), PostingsEnum.POSITIONS); - } - - if (solrPathQuery.getPathStructuredFieldPositions().size() > 0) - { - cs = new SolrContainerScorer(weight, rootContainerPositions, (StructuredFieldPosition[]) solrPathQuery.getPathStructuredFieldPositions().toArray(new StructuredFieldPosition[] {})); - } - - - return new SolrPathScorer(weight, cs); - } - - @Override - public float score() throws IOException - { - return scorer.score(); - } - - @Override - public int freq() throws IOException - { - return scorer.freq(); - } - - @Override - public int docID() - { - return scorer.docID(); - } - - - @Override - public DocIdSetIterator iterator() - { - return scorer.iterator(); - } - - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; + +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.PostingsEnum; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; + +public class SolrPathScorer extends Scorer +{ + Scorer scorer; + + SolrPathScorer(Weight weight, Scorer scorer) + { + super(weight); + this.scorer = scorer; + } + + + public static SolrPathScorer createPathScorer(SolrPathQuery solrPathQuery, LeafReaderContext context, Weight weight, DictionaryService dictionarySertvice, boolean repeat) throws IOException + { + +// StructuredFieldPosition last = null; +// if(solrPathQuery.getPathStructuredFieldPositions().size() > 0) +// { +// last = solrPathQuery.getPathStructuredFieldPositions().get(solrPathQuery.getPathStructuredFieldPositions().size() - 1); +// } + + + if (solrPathQuery.getPathStructuredFieldPositions().size() == 0) + { + ArrayList answer = new ArrayList(2); + answer.add(new SelfAxisStructuredFieldPosition()); + answer.add(new SelfAxisStructuredFieldPosition()); + + solrPathQuery.appendQuery(answer); + } + + + for (StructuredFieldPosition sfp : solrPathQuery.getPathStructuredFieldPositions()) + { + if (sfp.getTermText() != null) + { + PostingsEnum p = context.reader().postings(new Term(solrPathQuery.getPathField(), sfp.getTermText()), PostingsEnum.POSITIONS); + if (p == null) + return null; + CachingTermPositions ctp = new CachingTermPositions(p); + sfp.setCachingTermPositions(ctp); + } + } + + SolrContainerScorer cs = null; + + PostingsEnum rootContainerPositions = null; + if (solrPathQuery.getPathRootTerm() != null) + { + rootContainerPositions = context.reader().postings(solrPathQuery.getPathRootTerm(), PostingsEnum.POSITIONS); + } + + if (solrPathQuery.getPathStructuredFieldPositions().size() > 0) + { + cs = new SolrContainerScorer(weight, rootContainerPositions, (StructuredFieldPosition[]) solrPathQuery.getPathStructuredFieldPositions().toArray(new StructuredFieldPosition[] {})); + } + + + return new SolrPathScorer(weight, cs); + } + + @Override + public float score() throws IOException + { + return scorer.score(); + } + + @Override + public int freq() throws IOException + { + return scorer.freq(); + } + + @Override + public int docID() + { + return scorer.docID(); + } + + + @Override + public DocIdSetIterator iterator() + { + return scorer.iterator(); + } + + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderQuery.java index a3e5178b7..fee314665 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderQuery.java @@ -1,91 +1,91 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * TODO: comment me! - * @author Matt Ward - */ -public class SolrReaderQuery extends AbstractAuthorityQuery -{ - public SolrReaderQuery(String authority) - { - super(authority); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_READER).append(':'); - stringBuilder.append(authority); - return stringBuilder.toString(); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrReaderQueryWeight((SolrIndexSearcher)searcher, this, authority); - } - - - private class SolrReaderQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrReaderQueryWeight(SolrIndexSearcher searcher, Query query, String reader) throws IOException - { - super(searcher, false, query, QueryConstants.FIELD_READER, reader); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrReaderScorer.createReaderScorer(this, context, searcher, authority); - } - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_READER, authority)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * TODO: comment me! + * @author Matt Ward + */ +public class SolrReaderQuery extends AbstractAuthorityQuery +{ + public SolrReaderQuery(String authority) + { + super(authority); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_READER).append(':'); + stringBuilder.append(authority); + return stringBuilder.toString(); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrReaderQueryWeight((SolrIndexSearcher)searcher, this, authority); + } + + + private class SolrReaderQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrReaderQueryWeight(SolrIndexSearcher searcher, Query query, String reader) throws IOException + { + super(searcher, false, query, QueryConstants.FIELD_READER, reader); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrReaderScorer.createReaderScorer(this, context, searcher, authority); + } + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_READER, authority)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderScorer.java index 70d0a0ea8..443c4640c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderScorer.java @@ -1,89 +1,89 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Find the set of readable documents for a given authority. - * - * @author Matt Ward - */ -public class SolrReaderScorer extends AbstractSolrCachingScorer -{ - SolrReaderScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrReaderScorer createReaderScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException - { - DocSet readableDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authority); - - if (readableDocs == null) - { - // Cache miss: query the index for ACL docs where the reader matches the authority. - DocSet aclDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_READER, authority))); - - // Allocate a bitset to store the results. - readableDocs = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - // Translate from ACL docs to real docs - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); - SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); - Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); - DocSet docsForAclId = searcher.getDocSet(query); - readableDocs = readableDocs.union(docsForAclId); - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - readableDocs = readableDocs.andNot(aclDocs); - } - - searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authority, readableDocs); - } - - return new SolrReaderScorer(weight, readableDocs, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Find the set of readable documents for a given authority. + * + * @author Matt Ward + */ +public class SolrReaderScorer extends AbstractSolrCachingScorer +{ + SolrReaderScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrReaderScorer createReaderScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException + { + DocSet readableDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authority); + + if (readableDocs == null) + { + // Cache miss: query the index for ACL docs where the reader matches the authority. + DocSet aclDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_READER, authority))); + + // Allocate a bitset to store the results. + readableDocs = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + // Translate from ACL docs to real docs + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); + SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); + Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); + DocSet docsForAclId = searcher.getDocSet(query); + readableDocs = readableDocs.union(docsForAclId); + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + readableDocs = readableDocs.andNot(aclDocs); + } + + searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authority, readableDocs); + } + + return new SolrReaderScorer(weight, readableDocs, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetQuery.java index 15c2de074..dd0199203 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetQuery.java @@ -1,92 +1,92 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * @author Andy - * @author Matt Ward - */ -public class SolrReaderSetQuery extends AbstractAuthoritySetQuery -{ - public SolrReaderSetQuery(String authorities) - { - super(authorities); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrReaderSetQueryWeight((SolrIndexSearcher)searcher, this, authorities); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_READERSET).append(':'); - stringBuilder.append(authorities); - return stringBuilder.toString(); - } - - private class SolrReaderSetQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrReaderSetQueryWeight(SolrIndexSearcher searcher, Query query, String readers) throws IOException - { - super(searcher, false, query, QueryConstants.FIELD_READERSET, readers); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - LeafReader reader = context.reader(); - return SolrReaderSetScorer2.createReaderSetScorer(this, context, searcher, authorities, reader); - } - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_READERSET, authorities)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * @author Andy + * @author Matt Ward + */ +public class SolrReaderSetQuery extends AbstractAuthoritySetQuery +{ + public SolrReaderSetQuery(String authorities) + { + super(authorities); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrReaderSetQueryWeight((SolrIndexSearcher)searcher, this, authorities); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_READERSET).append(':'); + stringBuilder.append(authorities); + return stringBuilder.toString(); + } + + private class SolrReaderSetQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrReaderSetQueryWeight(SolrIndexSearcher searcher, Query query, String readers) throws IOException + { + super(searcher, false, query, QueryConstants.FIELD_READERSET, readers); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + LeafReader reader = context.reader(); + return SolrReaderSetScorer2.createReaderSetScorer(this, context, searcher, authorities, reader); + } + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_READERSET, authorities)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer.java index 19b14b6a7..e27a374f8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer.java @@ -1,104 +1,104 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -public class SolrReaderSetScorer extends AbstractSolrCachingScorer -{ - - SolrReaderSetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrReaderSetScorer createReaderSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException - { - - DocSet readableDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authorities); - - if (readableDocSet == null) - { - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - readableDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - for(String current : auths) - { - bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_READER, current)), Occur.SHOULD); - } - - DocSet aclDocs = searcher.getDocSet(bQuery.build()); - - BooleanQuery.Builder aQuery = new BooleanQuery.Builder(); - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); - SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); - Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); - aQuery.add(query, Occur.SHOULD); - - if((aQuery.build().clauses().size() > 999) || !it.hasNext()) - { - DocSet docsForAclId = searcher.getDocSet(aQuery.build()); - readableDocSet = readableDocSet.union(docsForAclId); - - aQuery = new BooleanQuery.Builder(); - } - } - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - readableDocSet = readableDocSet.andNot(aclDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authorities, readableDocSet); - } - - // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) - // plus check of course, for presence in cache at start of method. - return new SolrReaderSetScorer(weight, readableDocSet, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +public class SolrReaderSetScorer extends AbstractSolrCachingScorer +{ + + SolrReaderSetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrReaderSetScorer createReaderSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException + { + + DocSet readableDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authorities); + + if (readableDocSet == null) + { + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + readableDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + for(String current : auths) + { + bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_READER, current)), Occur.SHOULD); + } + + DocSet aclDocs = searcher.getDocSet(bQuery.build()); + + BooleanQuery.Builder aQuery = new BooleanQuery.Builder(); + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); + SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); + Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); + aQuery.add(query, Occur.SHOULD); + + if((aQuery.build().clauses().size() > 999) || !it.hasNext()) + { + DocSet docsForAclId = searcher.getDocSet(aQuery.build()); + readableDocSet = readableDocSet.union(docsForAclId); + + aQuery = new BooleanQuery.Builder(); + } + } + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + readableDocSet = readableDocSet.andNot(aclDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authorities, readableDocSet); + } + + // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) + // plus check of course, for presence in cache at start of method. + return new SolrReaderSetScorer(weight, readableDocSet, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer2.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer2.java index 5b451c8a8..027f99a26 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer2.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer2.java @@ -1,121 +1,121 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; -import java.util.HashSet; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -public class SolrReaderSetScorer2 extends AbstractSolrCachingScorer -{ - SolrReaderSetScorer2(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static AbstractSolrCachingScorer createReaderSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException - { - - DocSet readableDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authorities); - - if (readableDocSet == null) - { - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - readableDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - for(String current : auths) - { - bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_READER, current)), Occur.SHOULD); - } - WrappedQuery wrapped = new WrappedQuery(bQuery.build()); - wrapped.setCache(false); - - DocSet aclDocs = searcher.getDocSet(wrapped); - - HashSet aclsFound = new HashSet(aclDocs.size()); - NumericDocValues aclDocValues = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID); - - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = aclDocValues.get(docID); - aclsFound.add(getLong(aclID)); - } - - if(aclsFound.size() > 0) - { - for(LeafReaderContext readerContext : searcher.getSlowAtomicReader().leaves() ) - { - int maxDoc = readerContext.reader().maxDoc(); - NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, readerContext.reader()); - if(fieldValues != null) - { - for(int i = 0; i < maxDoc ; i++) - { - long aclID = fieldValues.get(i); - Long key = getLong(aclID); - if(aclsFound.contains(key)) - { - readableDocSet.add(readerContext.docBase + i); - } - } - } - - } - } - - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - readableDocSet = readableDocSet.andNot(aclDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authorities, readableDocSet); - } - - // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) - // plus check of course, for presence in cache at start of method. - return new SolrReaderSetScorer2(weight, readableDocSet, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; +import java.util.HashSet; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +public class SolrReaderSetScorer2 extends AbstractSolrCachingScorer +{ + SolrReaderSetScorer2(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static AbstractSolrCachingScorer createReaderSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException + { + + DocSet readableDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authorities); + + if (readableDocSet == null) + { + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + readableDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + for(String current : auths) + { + bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_READER, current)), Occur.SHOULD); + } + WrappedQuery wrapped = new WrappedQuery(bQuery.build()); + wrapped.setCache(false); + + DocSet aclDocs = searcher.getDocSet(wrapped); + + HashSet aclsFound = new HashSet(aclDocs.size()); + NumericDocValues aclDocValues = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID); + + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = aclDocValues.get(docID); + aclsFound.add(getLong(aclID)); + } + + if(aclsFound.size() > 0) + { + for(LeafReaderContext readerContext : searcher.getSlowAtomicReader().leaves() ) + { + int maxDoc = readerContext.reader().maxDoc(); + NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, readerContext.reader()); + if(fieldValues != null) + { + for(int i = 0; i < maxDoc ; i++) + { + long aclID = fieldValues.get(i); + Long key = getLong(aclID); + if(aclsFound.contains(key)) + { + readableDocSet.add(readerContext.docBase + i); + } + } + } + + } + } + + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + readableDocSet = readableDocSet.andNot(aclDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authorities, readableDocSet); + } + + // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) + // plus check of course, for presence in cache at start of method. + return new SolrReaderSetScorer2(weight, readableDocSet, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrXPathHandler.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrXPathHandler.java index 4f0cd4991..110155e35 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrXPathHandler.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrXPathHandler.java @@ -1,533 +1,533 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.util.ArrayList; - -import org.alfresco.repo.search.impl.lucene.analysis.PathTokenFilter; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.ISO9075; -import org.jaxen.saxpath.Axis; -import org.jaxen.saxpath.Operator; -import org.jaxen.saxpath.SAXPathException; -import org.jaxen.saxpath.XPathHandler; -/** - * XPATH handler for lucene - currently only does basic paths - * - * @author andyh - * - */ -public class SolrXPathHandler implements XPathHandler -{ - private SolrPathQuery query; - - private boolean isAbsolutePath = true; - - int absolutePosition = 0; - - private NamespacePrefixResolver namespacePrefixResolver; - - private DictionaryService dictionaryService; - - /** - * - */ - public SolrXPathHandler() - { - super(); - } - - /** - * Get the resulting query - * @return - the query - */ - public SolrPathQuery getQuery() - { - return this.query; - } - - public void endAbsoluteLocationPath() throws SAXPathException - { - // No action - } - - public void endAdditiveExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.ADD: - case Operator.SUBTRACT: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endAllNodeStep() throws SAXPathException - { - // Nothing to do - // Todo: Predicates - } - - public void endAndExpr(boolean create) throws SAXPathException - { - if (create) - { - throw new UnsupportedOperationException(); - } - } - - public void endCommentNodeStep() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endEqualityExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.EQUALS: - case Operator.NOT_EQUALS: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endFilterExpr() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endFunction() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endMultiplicativeExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.MULTIPLY: - case Operator.DIV: - case Operator.MOD: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endNameStep() throws SAXPathException - { - // Do nothing at the moment - // Could have repdicates - } - - public void endOrExpr(boolean create) throws SAXPathException - { - if (create) - { - throw new UnsupportedOperationException(); - } - } - - public void endPathExpr() throws SAXPathException - { - // Already built - } - - public void endPredicate() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endProcessingInstructionNodeStep() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endRelationalExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.GREATER_THAN: - case Operator.GREATER_THAN_EQUALS: - case Operator.LESS_THAN: - case Operator.LESS_THAN_EQUALS: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endRelativeLocationPath() throws SAXPathException - { - // No action - } - - public void endTextNodeStep() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endUnaryExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.NEGATIVE: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endUnionExpr(boolean create) throws SAXPathException - { - if (create) - { - throw new UnsupportedOperationException(); - } - } - - public void endXPath() throws SAXPathException - { - // Do nothing at the moment - } - - public void literal(String arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void number(double arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void number(int arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startAbsoluteLocationPath() throws SAXPathException - { - if (!isAbsolutePath) - { - throw new IllegalStateException(); - } - - } - - public void startAdditiveExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startAllNodeStep(int axis) throws SAXPathException - { - switch (axis) - { - case Axis.CHILD: - if (isAbsolutePath) - { - // addAbsolute(null, null); - // We can always do relative stuff - addRelative(null, null); - } - else - { - addRelative(null, null); - } - break; - case Axis.DESCENDANT_OR_SELF: - query.appendQuery(getArrayList(new DescendantAndSelfStructuredFieldPosition(), new DescendantAndSelfStructuredFieldPosition())); - break; - case Axis.SELF: - query.appendQuery(getArrayList(new SelfAxisStructuredFieldPosition(), new SelfAxisStructuredFieldPosition())); - break; - default: - throw new UnsupportedOperationException(); - } - } - - private ArrayList getArrayList(StructuredFieldPosition one, StructuredFieldPosition two) - { - ArrayList answer = new ArrayList(2); - answer.add(one); - answer.add(two); - return answer; - } - - public void startAndExpr() throws SAXPathException - { - // Do nothing - } - - public void startCommentNodeStep(int arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startEqualityExpr() throws SAXPathException - { - // Do nothing - } - - public void startFilterExpr() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startFunction(String arg0, String arg1) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startMultiplicativeExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startNameStep(int axis, String nameSpace, String localName) throws SAXPathException - { - switch (axis) - { - case Axis.CHILD: - if (isAbsolutePath) - { - // addAbsolute(nameSpace, localName); - // we can always do relative stuff - addRelative(nameSpace, localName); - } - else - { - addRelative(nameSpace, localName); - } - break; - default: - throw new UnsupportedOperationException(); - } - - } - - @SuppressWarnings("unused") - private void addAbsolute(String nameSpace, String localName) - { - ArrayList answer = new ArrayList(2); - // TODO: Resolve name space - absolutePosition++; - if ((nameSpace == null) || (nameSpace.length() == 0)) - { - - if(localName.equals("*")) - { - answer.add(new AbsoluteStructuredFieldPosition("*", absolutePosition)); - } - else if (namespacePrefixResolver.getNamespaceURI("") == null) - { - answer.add(new AbsoluteStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT, absolutePosition)); - } - else - { - answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(""), absolutePosition)); - } - - } - else - { - answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace), absolutePosition)); - } - - absolutePosition++; - if ((localName == null) || (localName.length() == 0)) - { - answer.add(new AbsoluteStructuredFieldPosition("*", absolutePosition)); - } - else - { - if(localName.equals("*")) - { - answer.add(new AbsoluteStructuredFieldPosition(localName, absolutePosition)); - } - else - { - answer.add(new AbsoluteStructuredFieldPosition(ISO9075.encode(QName.createValidLocalName(ISO9075.decode(localName))), absolutePosition)); - } - } - query.appendQuery(answer); - - } - - private void addRelative(String nameSpace, String localName) - { - ArrayList answer = new ArrayList(2); - if ((nameSpace == null) || (nameSpace.length() == 0)) - { - if(localName.equals("*")) - { - answer.add(new RelativeStructuredFieldPosition("*")); - } - else if (namespacePrefixResolver.getNamespaceURI("") == null) - { - answer.add(new RelativeStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT)); - } - else if (namespacePrefixResolver.getNamespaceURI("").equals("")) - { - answer.add(new RelativeStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT)); - } - else - { - answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(""))); - } - } - else - { - answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace))); - } - - if ((localName == null) || (localName.length() == 0)) - { - answer.add(new RelativeStructuredFieldPosition("*")); - } - else - { - if(localName.equals("*")) - { - answer.add(new RelativeStructuredFieldPosition(localName)); - } - else - { - answer.add(new RelativeStructuredFieldPosition(ISO9075.encode(QName.createValidLocalName(ISO9075.decode(localName))))); - } - } - query.appendQuery(answer); - } - - public void startOrExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startPathExpr() throws SAXPathException - { - // Just need one! - } - - public void startPredicate() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startProcessingInstructionNodeStep(int arg0, String arg1) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startRelationalExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startRelativeLocationPath() throws SAXPathException - { - isAbsolutePath = false; - } - - public void startTextNodeStep(int arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startUnaryExpr() throws SAXPathException - { - // Do nothing for now - } - - public void startUnionExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startXPath() throws SAXPathException - { - query = new SolrPathQuery(dictionaryService); - } - - public void variableReference(String uri, String localName) throws SAXPathException - { - - } - - /** - * Set the service to resolve name space prefixes - * @param namespacePrefixResolver - */ - public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) - { - this.namespacePrefixResolver = namespacePrefixResolver; - } - - /** - * Set the dictionary service. - * @param dictionaryService - */ - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.util.ArrayList; + +import org.alfresco.repo.search.impl.lucene.analysis.PathTokenFilter; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.jaxen.saxpath.Axis; +import org.jaxen.saxpath.Operator; +import org.jaxen.saxpath.SAXPathException; +import org.jaxen.saxpath.XPathHandler; +/** + * XPATH handler for lucene - currently only does basic paths + * + * @author andyh + * + */ +public class SolrXPathHandler implements XPathHandler +{ + private SolrPathQuery query; + + private boolean isAbsolutePath = true; + + int absolutePosition = 0; + + private NamespacePrefixResolver namespacePrefixResolver; + + private DictionaryService dictionaryService; + + /** + * + */ + public SolrXPathHandler() + { + super(); + } + + /** + * Get the resulting query + * @return - the query + */ + public SolrPathQuery getQuery() + { + return this.query; + } + + public void endAbsoluteLocationPath() throws SAXPathException + { + // No action + } + + public void endAdditiveExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.ADD: + case Operator.SUBTRACT: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endAllNodeStep() throws SAXPathException + { + // Nothing to do + // Todo: Predicates + } + + public void endAndExpr(boolean create) throws SAXPathException + { + if (create) + { + throw new UnsupportedOperationException(); + } + } + + public void endCommentNodeStep() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endEqualityExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.EQUALS: + case Operator.NOT_EQUALS: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endFilterExpr() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endFunction() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endMultiplicativeExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.MULTIPLY: + case Operator.DIV: + case Operator.MOD: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endNameStep() throws SAXPathException + { + // Do nothing at the moment + // Could have repdicates + } + + public void endOrExpr(boolean create) throws SAXPathException + { + if (create) + { + throw new UnsupportedOperationException(); + } + } + + public void endPathExpr() throws SAXPathException + { + // Already built + } + + public void endPredicate() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endProcessingInstructionNodeStep() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endRelationalExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.GREATER_THAN: + case Operator.GREATER_THAN_EQUALS: + case Operator.LESS_THAN: + case Operator.LESS_THAN_EQUALS: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endRelativeLocationPath() throws SAXPathException + { + // No action + } + + public void endTextNodeStep() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endUnaryExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.NEGATIVE: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endUnionExpr(boolean create) throws SAXPathException + { + if (create) + { + throw new UnsupportedOperationException(); + } + } + + public void endXPath() throws SAXPathException + { + // Do nothing at the moment + } + + public void literal(String arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void number(double arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void number(int arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startAbsoluteLocationPath() throws SAXPathException + { + if (!isAbsolutePath) + { + throw new IllegalStateException(); + } + + } + + public void startAdditiveExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startAllNodeStep(int axis) throws SAXPathException + { + switch (axis) + { + case Axis.CHILD: + if (isAbsolutePath) + { + // addAbsolute(null, null); + // We can always do relative stuff + addRelative(null, null); + } + else + { + addRelative(null, null); + } + break; + case Axis.DESCENDANT_OR_SELF: + query.appendQuery(getArrayList(new DescendantAndSelfStructuredFieldPosition(), new DescendantAndSelfStructuredFieldPosition())); + break; + case Axis.SELF: + query.appendQuery(getArrayList(new SelfAxisStructuredFieldPosition(), new SelfAxisStructuredFieldPosition())); + break; + default: + throw new UnsupportedOperationException(); + } + } + + private ArrayList getArrayList(StructuredFieldPosition one, StructuredFieldPosition two) + { + ArrayList answer = new ArrayList(2); + answer.add(one); + answer.add(two); + return answer; + } + + public void startAndExpr() throws SAXPathException + { + // Do nothing + } + + public void startCommentNodeStep(int arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startEqualityExpr() throws SAXPathException + { + // Do nothing + } + + public void startFilterExpr() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startFunction(String arg0, String arg1) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startMultiplicativeExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startNameStep(int axis, String nameSpace, String localName) throws SAXPathException + { + switch (axis) + { + case Axis.CHILD: + if (isAbsolutePath) + { + // addAbsolute(nameSpace, localName); + // we can always do relative stuff + addRelative(nameSpace, localName); + } + else + { + addRelative(nameSpace, localName); + } + break; + default: + throw new UnsupportedOperationException(); + } + + } + + @SuppressWarnings("unused") + private void addAbsolute(String nameSpace, String localName) + { + ArrayList answer = new ArrayList(2); + // TODO: Resolve name space + absolutePosition++; + if ((nameSpace == null) || (nameSpace.length() == 0)) + { + + if(localName.equals("*")) + { + answer.add(new AbsoluteStructuredFieldPosition("*", absolutePosition)); + } + else if (namespacePrefixResolver.getNamespaceURI("") == null) + { + answer.add(new AbsoluteStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT, absolutePosition)); + } + else + { + answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(""), absolutePosition)); + } + + } + else + { + answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace), absolutePosition)); + } + + absolutePosition++; + if ((localName == null) || (localName.length() == 0)) + { + answer.add(new AbsoluteStructuredFieldPosition("*", absolutePosition)); + } + else + { + if(localName.equals("*")) + { + answer.add(new AbsoluteStructuredFieldPosition(localName, absolutePosition)); + } + else + { + answer.add(new AbsoluteStructuredFieldPosition(ISO9075.encode(QName.createValidLocalName(ISO9075.decode(localName))), absolutePosition)); + } + } + query.appendQuery(answer); + + } + + private void addRelative(String nameSpace, String localName) + { + ArrayList answer = new ArrayList(2); + if ((nameSpace == null) || (nameSpace.length() == 0)) + { + if(localName.equals("*")) + { + answer.add(new RelativeStructuredFieldPosition("*")); + } + else if (namespacePrefixResolver.getNamespaceURI("") == null) + { + answer.add(new RelativeStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT)); + } + else if (namespacePrefixResolver.getNamespaceURI("").equals("")) + { + answer.add(new RelativeStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT)); + } + else + { + answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(""))); + } + } + else + { + answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace))); + } + + if ((localName == null) || (localName.length() == 0)) + { + answer.add(new RelativeStructuredFieldPosition("*")); + } + else + { + if(localName.equals("*")) + { + answer.add(new RelativeStructuredFieldPosition(localName)); + } + else + { + answer.add(new RelativeStructuredFieldPosition(ISO9075.encode(QName.createValidLocalName(ISO9075.decode(localName))))); + } + } + query.appendQuery(answer); + } + + public void startOrExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startPathExpr() throws SAXPathException + { + // Just need one! + } + + public void startPredicate() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startProcessingInstructionNodeStep(int arg0, String arg1) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startRelationalExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startRelativeLocationPath() throws SAXPathException + { + isAbsolutePath = false; + } + + public void startTextNodeStep(int arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startUnaryExpr() throws SAXPathException + { + // Do nothing for now + } + + public void startUnionExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startXPath() throws SAXPathException + { + query = new SolrPathQuery(dictionaryService); + } + + public void variableReference(String uri, String localName) throws SAXPathException + { + + } + + /** + * Set the service to resolve name space prefixes + * @param namespacePrefixResolver + */ + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } + + /** + * Set the dictionary service. + * @param dictionaryService + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldPosition.java index 5479de3ec..23d1e4fa2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldPosition.java @@ -1,124 +1,124 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.io.IOException; - -/** - * Elements used to test agains path and Qname - * - * @author andyh - */ -public interface StructuredFieldPosition -{ - - /** - * Does this element match - * - * @param start - - * the start postion of the paths terms - * @param end - - * the end position of the paths terms - * @param offset - - * the current offset in the path - * @return returns the next match position (usually offset + 1) or -1 if it - * does not match. - * @throws IOException - */ - public int matches(int start, int end, int offset) throws IOException; - - /** - * If this position is last in the chain and it is terminal it will ensure - * it is an exact match for the length of the chain found. If false, it will - * effectively allow prefix mathces for the likes of descendant-and-below - * style queries. - * - * @return boolean - */ - public boolean isTerminal(); - - /** - * Is this an absolute element; that is, it knows its exact position. - * - * @return boolean - */ - public boolean isAbsolute(); - - /** - * This element only knows its position relative to the previous element. - * - * @return boolean - */ - public boolean isRelative(); - - /** - * Get the test to search for in the term query. This may be null if it - * should not have a term query - * - * @return String - */ - public String getTermText(); - - /** - * If absolute return the position. If relative we could compute the - * position knowing the previous term unless this element is preceded by a - * descendat and below style element - * - * @return int - */ - public int getPosition(); - - /** - * A reference to the caching term positions this element uses. This may be - * null which indicates all terms match, in that case there is no action - * against the index - * - * @param tps CachingTermPositions - */ - public void setCachingTermPositions(CachingTermPositions tps); - - public CachingTermPositions getCachingTermPositions(); - - /** - * Normally paths would require onlt parent chaining. for some it is parent - * and child chaining. - * - * @return boolean - */ - - public boolean linkSelf(); - - public boolean linkParent(); - - public boolean allowslinkingByParent(); - - public boolean allowsLinkingBySelf(); - - public boolean isDescendant(); - - public boolean matchesAll(); -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import java.io.IOException; + +/** + * Elements used to test agains path and Qname + * + * @author andyh + */ +public interface StructuredFieldPosition +{ + + /** + * Does this element match + * + * @param start - + * the start postion of the paths terms + * @param end - + * the end position of the paths terms + * @param offset - + * the current offset in the path + * @return returns the next match position (usually offset + 1) or -1 if it + * does not match. + * @throws IOException + */ + public int matches(int start, int end, int offset) throws IOException; + + /** + * If this position is last in the chain and it is terminal it will ensure + * it is an exact match for the length of the chain found. If false, it will + * effectively allow prefix mathces for the likes of descendant-and-below + * style queries. + * + * @return boolean + */ + public boolean isTerminal(); + + /** + * Is this an absolute element; that is, it knows its exact position. + * + * @return boolean + */ + public boolean isAbsolute(); + + /** + * This element only knows its position relative to the previous element. + * + * @return boolean + */ + public boolean isRelative(); + + /** + * Get the test to search for in the term query. This may be null if it + * should not have a term query + * + * @return String + */ + public String getTermText(); + + /** + * If absolute return the position. If relative we could compute the + * position knowing the previous term unless this element is preceded by a + * descendat and below style element + * + * @return int + */ + public int getPosition(); + + /** + * A reference to the caching term positions this element uses. This may be + * null which indicates all terms match, in that case there is no action + * against the index + * + * @param tps CachingTermPositions + */ + public void setCachingTermPositions(CachingTermPositions tps); + + public CachingTermPositions getCachingTermPositions(); + + /** + * Normally paths would require onlt parent chaining. for some it is parent + * and child chaining. + * + * @return boolean + */ + + public boolean linkSelf(); + + public boolean linkParent(); + + public boolean allowslinkingByParent(); + + public boolean allowsLinkingBySelf(); + + public boolean isDescendant(); + + public boolean matchesAll(); +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldTerm.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldTerm.java index d6fe47d09..9fc0075dc 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldTerm.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldTerm.java @@ -1,65 +1,65 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import org.apache.lucene.index.Term; - -/** - * @author andyh - */ -public class StructuredFieldTerm -{ - - private Term term; - - private StructuredFieldPosition sfp; - - /** - * - */ - public StructuredFieldTerm(Term term, StructuredFieldPosition sfp) - { - this.term = term; - this.sfp = sfp; - } - - /** - * @return Returns the sfp. - */ - public StructuredFieldPosition getSfp() - { - return sfp; - } - - /** - * @return Returns the term. - */ - public Term getTerm() - { - return term; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import org.apache.lucene.index.Term; + +/** + * @author andyh + */ +public class StructuredFieldTerm +{ + + private Term term; + + private StructuredFieldPosition sfp; + + /** + * + */ + public StructuredFieldTerm(Term term, StructuredFieldPosition sfp) + { + this.term = term; + this.sfp = sfp; + } + + /** + * @return Returns the sfp. + */ + public StructuredFieldPosition getSfp() + { + return sfp; + } + + /** + * @return Returns the term. + */ + public Term getTerm() + { + return term; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/CompositeReader.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/CompositeReader.java index ebcb8428d..95648bc0b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/CompositeReader.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/CompositeReader.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -83,4 +83,4 @@ class CompositeReader extends Reader { exhausted.forEach(Utils::silentyClose); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextAnalyzer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextAnalyzer.java index 15c872157..5ef46d1fa 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextAnalyzer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextAnalyzer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -71,4 +71,4 @@ public class LanguagePrefixedTextAnalyzer extends Analyzer return new TokenStreamComponents(decorator, decorator); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextField.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextField.java index f321fcc39..efdb13e4c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextField.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextField.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -61,4 +61,4 @@ public class LanguagePrefixedTextField extends TextField setIndexAnalyzer(new LanguagePrefixedTextAnalyzer(schema, AlfrescoAnalyzerWrapper.Mode.INDEX)); setQueryAnalyzer(new LanguagePrefixedTextAnalyzer(schema, AlfrescoAnalyzerWrapper.Mode.QUERY)); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStream.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStream.java index bc3d6f080..9b9330e05 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStream.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStream.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -373,4 +373,4 @@ public final class LanguagePrefixedTokenStream extends Tokenizer { return language.map(String::length).map(length -> length + 2).orElse(0); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/package-info.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/package-info.java index 0751a8e82..1a370e428 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/package-info.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/package-info.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -72,4 +72,4 @@ * * */ -package org.alfresco.solr.schema.highlight; \ No newline at end of file +package org.alfresco.solr.schema.highlight; diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/servlet/Solr4X509ServletFilter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/servlet/Solr4X509ServletFilter.java index 3286482f3..7a83308be 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/servlet/Solr4X509ServletFilter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/servlet/Solr4X509ServletFilter.java @@ -1,289 +1,289 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.servlet; - -import org.alfresco.web.scripts.servlet.X509ServletFilterBase; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.solr.core.SolrResourceLoader; - -import javax.management.MBeanServer; -import javax.management.MBeanServerFactory; -import javax.management.ObjectName; -import javax.management.Query; -import javax.management.QueryExp; -import javax.servlet.ServletContext; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.Properties; -import java.util.Set; -import java.util.function.Function; - -import static java.util.Optional.ofNullable; - -/** - * The Solr4X509ServletFilter implements the checkEnforce method of the X509ServletFilterBase. - * This allows the configuration of X509 authentication to be toggled on/off through a - * configuration outside of the web.xml. - **/ - -public class Solr4X509ServletFilter extends X509ServletFilterBase -{ - - private static final String SECURE_COMMS = "alfresco.secureComms"; - - private static Log logger = LogFactory.getLog(Solr4X509ServletFilter.class); - - static final int NOT_FOUND_HTTPS_PORT_NUMBER = -1; - - private Function extractPortNumber = name -> - { - try - { - return ofNullable(mxServer().getAttribute(name, "Port")) - .map(Number.class::cast) - .map(Number::intValue) - .orElse(NOT_FOUND_HTTPS_PORT_NUMBER); - } - catch(final Exception exception) - { - logger.error("Error getting https port from MBean " + name, exception); - return NOT_FOUND_HTTPS_PORT_NUMBER; - } - }; - - @Override - protected boolean checkEnforce(ServletContext context) throws IOException - { - /* - * Rely on the SolrResourceLoader to locate the solr home directory. - */ - - int httpsPort = getHttpsPort(); - - if(httpsPort > -1) - { - setHttpsPort(httpsPort); - } - - String solrHome = SolrResourceLoader.locateSolrHome().toString(); - - if(logger.isDebugEnabled()) - { - logger.debug("solrHome:"+solrHome); - } - - /* - * Find the active cores. - */ - List cores = new ArrayList(); - findCores(new File(solrHome), cores); - - /* - * Get the alfresco.secureComms value for each core. - */ - Set secureCommsSet = new HashSet(); - for(File core : cores) - { - collectSecureComms(core, secureCommsSet); - } - - /* - * alfresco.secureComms values should be in sync for each core - */ - - if(secureCommsSet.size() > 1) - { - StringBuilder buf = new StringBuilder(); - int i = 0; - for(String s : secureCommsSet) - { - if(i > 0) - { - buf.append(" | "); - } - buf.append(s); - i++; - } - - throw new IOException("More then one distinct value found for alfresco.secureComms:"+ buf.toString()+ - ". All alfresco.secureComms values must be set to the same value."); - } - - if(secureCommsSet.size() == 0) - { - //No secureComms were found. - return false; - } - - - String secureComms = secureCommsSet.iterator().next(); - - if(logger.isDebugEnabled()) - { - logger.debug("secureComms:"+secureComms); - } - - if("none".equals(secureComms)) - { - return false; - } - else - { - return true; - } - } - - - private void findCores(File dir, List cores) - { - File[] files = dir.listFiles(); - if (files != null) - { - for (File file : files) - { - if (file.isDirectory()) - { - findCores(file, cores); - } - else - { - if ("core.properties".equals(file.getName())) - { - if (logger.isDebugEnabled()) - { - logger.debug("Found core:" + dir.getAbsolutePath()); - } - - cores.add(dir); - } - } - } - } - } - /* - * Scan through all the files and folders under a dir looking for solrcore.properties file. - * Gather the SECURE_COMMS property when found. - */ - - private void collectSecureComms(File base, Set secureCommsSet) throws IOException - { - File[] files = base.listFiles(); - if (files != null) - { - for (File file : files) - { - if (file.isDirectory()) - { - collectSecureComms(file, secureCommsSet); - } - else - { - - if (logger.isDebugEnabled()) - { - logger.debug("scanning file:" + file.getAbsolutePath()); - } - - if ("solrcore.properties".equals(file.getName())) - { - FileReader propReader = null; - Properties props = new Properties(); - try - { - propReader = new FileReader(file); - props.load(propReader); - String prop = props.getProperty(SECURE_COMMS); - - if (prop != null) - { - if (logger.isDebugEnabled()) - { - logger.debug("Found alfresco.secureComms in:" + file.getAbsolutePath() + " : " + prop); - } - secureCommsSet.add(prop); - } - else - { - secureCommsSet.add("none"); - } - } - finally - { - if (propReader != null) - { - propReader.close(); - } - } - } - } - } - } - } - - int getHttpsPort() - { - return connectorMBeanName().map(extractPortNumber).orElse(NOT_FOUND_HTTPS_PORT_NUMBER); - } - - Optional connectorMBeanName() - { - try - { - final QueryExp query = Query.eq(Query.attr("Scheme"), Query.value("https")); - final Set connectors = mxServer().queryNames(null, query); - return connectors - .stream() - .findFirst(); - } - catch (final Exception exception) - { - logger.error("Error getting the Connector MBean.", exception); - return Optional.empty(); - } - } - - MBeanServer mxServer() - { - return ofNullable((MBeanServerFactory.findMBeanServer(null))) - .filter(servers -> !servers.isEmpty()) - .map(Collection::iterator) - .map(Iterator::next) - .orElseThrow(NoSuchElementException::new); - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.servlet; + +import org.alfresco.web.scripts.servlet.X509ServletFilterBase; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.solr.core.SolrResourceLoader; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; +import javax.management.Query; +import javax.management.QueryExp; +import javax.servlet.ServletContext; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import java.util.function.Function; + +import static java.util.Optional.ofNullable; + +/** + * The Solr4X509ServletFilter implements the checkEnforce method of the X509ServletFilterBase. + * This allows the configuration of X509 authentication to be toggled on/off through a + * configuration outside of the web.xml. + **/ + +public class Solr4X509ServletFilter extends X509ServletFilterBase +{ + + private static final String SECURE_COMMS = "alfresco.secureComms"; + + private static Log logger = LogFactory.getLog(Solr4X509ServletFilter.class); + + static final int NOT_FOUND_HTTPS_PORT_NUMBER = -1; + + private Function extractPortNumber = name -> + { + try + { + return ofNullable(mxServer().getAttribute(name, "Port")) + .map(Number.class::cast) + .map(Number::intValue) + .orElse(NOT_FOUND_HTTPS_PORT_NUMBER); + } + catch(final Exception exception) + { + logger.error("Error getting https port from MBean " + name, exception); + return NOT_FOUND_HTTPS_PORT_NUMBER; + } + }; + + @Override + protected boolean checkEnforce(ServletContext context) throws IOException + { + /* + * Rely on the SolrResourceLoader to locate the solr home directory. + */ + + int httpsPort = getHttpsPort(); + + if(httpsPort > -1) + { + setHttpsPort(httpsPort); + } + + String solrHome = SolrResourceLoader.locateSolrHome().toString(); + + if(logger.isDebugEnabled()) + { + logger.debug("solrHome:"+solrHome); + } + + /* + * Find the active cores. + */ + List cores = new ArrayList(); + findCores(new File(solrHome), cores); + + /* + * Get the alfresco.secureComms value for each core. + */ + Set secureCommsSet = new HashSet(); + for(File core : cores) + { + collectSecureComms(core, secureCommsSet); + } + + /* + * alfresco.secureComms values should be in sync for each core + */ + + if(secureCommsSet.size() > 1) + { + StringBuilder buf = new StringBuilder(); + int i = 0; + for(String s : secureCommsSet) + { + if(i > 0) + { + buf.append(" | "); + } + buf.append(s); + i++; + } + + throw new IOException("More then one distinct value found for alfresco.secureComms:"+ buf.toString()+ + ". All alfresco.secureComms values must be set to the same value."); + } + + if(secureCommsSet.size() == 0) + { + //No secureComms were found. + return false; + } + + + String secureComms = secureCommsSet.iterator().next(); + + if(logger.isDebugEnabled()) + { + logger.debug("secureComms:"+secureComms); + } + + if("none".equals(secureComms)) + { + return false; + } + else + { + return true; + } + } + + + private void findCores(File dir, List cores) + { + File[] files = dir.listFiles(); + if (files != null) + { + for (File file : files) + { + if (file.isDirectory()) + { + findCores(file, cores); + } + else + { + if ("core.properties".equals(file.getName())) + { + if (logger.isDebugEnabled()) + { + logger.debug("Found core:" + dir.getAbsolutePath()); + } + + cores.add(dir); + } + } + } + } + } + /* + * Scan through all the files and folders under a dir looking for solrcore.properties file. + * Gather the SECURE_COMMS property when found. + */ + + private void collectSecureComms(File base, Set secureCommsSet) throws IOException + { + File[] files = base.listFiles(); + if (files != null) + { + for (File file : files) + { + if (file.isDirectory()) + { + collectSecureComms(file, secureCommsSet); + } + else + { + + if (logger.isDebugEnabled()) + { + logger.debug("scanning file:" + file.getAbsolutePath()); + } + + if ("solrcore.properties".equals(file.getName())) + { + FileReader propReader = null; + Properties props = new Properties(); + try + { + propReader = new FileReader(file); + props.load(propReader); + String prop = props.getProperty(SECURE_COMMS); + + if (prop != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("Found alfresco.secureComms in:" + file.getAbsolutePath() + " : " + prop); + } + secureCommsSet.add(prop); + } + else + { + secureCommsSet.add("none"); + } + } + finally + { + if (propReader != null) + { + propReader.close(); + } + } + } + } + } + } + } + + int getHttpsPort() + { + return connectorMBeanName().map(extractPortNumber).orElse(NOT_FOUND_HTTPS_PORT_NUMBER); + } + + Optional connectorMBeanName() + { + try + { + final QueryExp query = Query.eq(Query.attr("Scheme"), Query.value("https")); + final Set connectors = mxServer().queryNames(null, query); + return connectors + .stream() + .findFirst(); + } + catch (final Exception exception) + { + logger.error("Error getting the Connector MBean.", exception); + return Optional.empty(); + } + } + + MBeanServer mxServer() + { + return ofNullable((MBeanServerFactory.findMBeanServer(null))) + .filter(servers -> !servers.isEmpty()) + .map(Collection::iterator) + .map(Iterator::next) + .orElseThrow(NoSuchElementException::new); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDModRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDModRouter.java index 3a7dd790c..a62ec7de6 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDModRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDModRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -67,4 +67,4 @@ public class ACLIDModRouter implements DocRouter { return id % shardCount == shardInstance; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDMurmurRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDMurmurRouter.java index cc6fa85ec..bbed8310a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDMurmurRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDMurmurRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -61,4 +61,4 @@ public class ACLIDMurmurRouter implements DocRouter String value = Long.toString(id); return (Math.abs(Hash.murmurhash3_x86_32(value, 0, value.length(), 77)) % numShards) == shardInstance; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java index f020f71a5..82821adad 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java @@ -1,472 +1,472 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker; - -import static java.util.Optional.ofNullable; - -import static org.alfresco.repo.index.shard.ShardMethodEnum.DB_ID; -import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_KEY_KEY; - -import java.net.ConnectException; -import java.net.SocketTimeoutException; -import java.util.Optional; -import java.util.Properties; -import java.util.concurrent.Semaphore; - -import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.repo.index.shard.ShardMethodEnum; -import org.alfresco.repo.search.impl.QueryParserUtils; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.IndexTrackingShutdownException; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.NodeReport; -import org.alfresco.solr.TrackerState; -import org.alfresco.solr.client.SOLRAPIClient; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Abstract base class that provides common {@link Tracker} behaviour. - * - * @author Matt Ward - */ -public abstract class AbstractTracker implements Tracker -{ - static final long TIME_STEP_32_DAYS_IN_MS = 1000 * 60 * 60 * 24 * 32L; - static final long TIME_STEP_1_HR_IN_MS = 60 * 60 * 1000L; - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTracker.class); - - protected Properties props; - protected SOLRAPIClient client; - InformationServer infoSrv; - protected String coreName; - StoreRef storeRef; - long batchCount; - TrackerStats trackerStats; - boolean runPostModelLoadInit = true; - private int maxLiveSearchers; - private volatile boolean shutdown = false; - - protected volatile TrackerState state; - protected int shardCount; - protected int shardInstance; - ShardMethodEnum shardMethod; - protected boolean transformContent; - String shardTemplate; - protected volatile boolean rollback; - /** - * When rollback is set, original error is also gathered in order to provide detailed logging. - */ - protected Throwable rollbackCausedBy; - protected final Type type; - protected final String trackerId; - - DocRouter docRouter; - - /** - * The property to use for determining the shard. - * Note that this property is not used by all trackers, it is actually managed by the {@link ShardStatePublisher} and - * {@link MetadataTracker}. We put this property here because otherwise we should introduce another supertype layer - * for those two trackers. - */ - protected Optional shardProperty = Optional.empty(); - - /** - * The string representation of the shard key. - * Note that this property is not used by all trackers, it is actually managed by the {@link ShardStatePublisher} and - * {@link MetadataTracker}. We put this property here because otherwise we should introduce another supertype layer - * for those two trackers. - */ - protected Optional shardKey; - - /** - * Default constructor, strictly for testing. - */ - protected AbstractTracker(Type type) - { - this.type = type; - this.trackerId = type + "@" + hashCode(); - } - - protected AbstractTracker(Properties p, SOLRAPIClient client, String coreName, InformationServer informationServer,Type type) - { - this.props = p; - this.client = client; - this.coreName = coreName; - this.infoSrv = informationServer; - - storeRef = new StoreRef(p.getProperty("alfresco.stores", "workspace://SpacesStore")); - batchCount = Integer.parseInt(p.getProperty("alfresco.batch.count", "5000")); - maxLiveSearchers = Integer.parseInt(p.getProperty("alfresco.maxLiveSearchers", "2")); - - shardCount = Integer.parseInt(p.getProperty("shard.count", "1")); - shardInstance = Integer.parseInt(p.getProperty("shard.instance", "0")); - shardMethod = ShardMethodEnum.getShardMethod(p.getProperty("shard.method", DB_ID.name())); - - shardTemplate = p.getProperty("alfresco.template", ""); - - transformContent = Boolean.parseBoolean(p.getProperty("alfresco.index.transformContent", "true")); - - this.trackerStats = this.infoSrv.getTrackerStats(); - - this.type = type; - - this.trackerId = type + "@" + hashCode(); - - shardKey = ofNullable(p.getProperty(SHARD_KEY_KEY)); - - firstUpdateShardProperty(); - - docRouter = DocRouterFactory.getRouter(p, shardMethod); - } - - /** - * Subclasses must implement behaviour that completes the following steps, in order: - * - *

    - *
  1. Purge
  2. - *
  3. Reindex
  4. - *
  5. Index
  6. - *
  7. Track repository
  8. - *
- * - * @param iterationId an identifier which is uniquely associated with a given iteration. - */ - protected abstract void doTrack(String iterationId) throws Throwable; - - - private boolean assertTrackerStateRemainsNull() { - - /* - * This assertion is added to accommodate DistributedAlfrescoSolrTrackerRaceTest. - * The sleep is needed to allow the test case to add a txn into the queue before - * the tracker makes its call to pull transactions from the test repo client. - */ - - try - { - Thread.sleep(5000); - } - catch(Exception e) - { - // Ignore - } - - - /* - * This ensures that getTrackerState does not have the side effect of setting the - * state instance variable. This allows classes outside of the tracker framework - * to safely call getTrackerState without interfering with the trackers design. - */ - - getTrackerState(); - - return state == null; - - } - /** - * Template method - subclasses must implement the {@link Tracker}-specific indexing - * by implementing the abstract method {@link #doTrack(String)}. - */ - @Override - public void track() - { - String iterationId = "IT #" + System.currentTimeMillis(); - - if(getRunLock().availablePermits() == 0) - { - LOGGER.info("[{} / {} / {}] Tracker already registered.", coreName, trackerId, iterationId); - return; - } - - try - { - /* - * The runLock ensures that for each tracker type (metadata, content, commit, cascade) only one tracker will - * be running at a time. - */ - getRunLock().acquire(); - - if (state==null && Boolean.parseBoolean(System.getProperty("alfresco.test", "false"))) - { - assert(assertTrackerStateRemainsNull()); - } - - if(this.state == null) - { - this.state = getTrackerState(); - - LOGGER.debug("[{} / {} / {}] Global Tracker State set to: {}", coreName, trackerId, iterationId, this.state.toString()); - this.state.setRunning(true); - } - else - { - continueState(); - this.state.setRunning(true); - } - - infoSrv.registerTrackerThread(); - - try - { - doTrack(iterationId); - } - catch(IndexTrackingShutdownException t) - { - setRollback(true, t); - LOGGER.info("[{} / {} / {}] Tracking cycle stopped. See the stacktrace below for further details.", coreName, trackerId, iterationId, t); - } - catch(Throwable t) - { - setRollback(true, t); - if (t instanceof SocketTimeoutException || t instanceof ConnectException) - { - LOGGER.warn("[{} / {} / {}] Tracking communication timed out. See the stacktrace below for further details.", coreName, trackerId, iterationId); - LOGGER.debug("[{} / {} / {}] Stack trace", coreName, trackerId, iterationId, t); - } - else - { - LOGGER.error("[{} / {} / {}] Tracking failure. See the stacktrace below for further details.", coreName, trackerId, iterationId, t); - } - } - } - catch (Exception exception) - { - LOGGER.error("[{} / {} / {}] Some problem was detected while resetting the Tracker State. See the stracktrace below for further details." - , coreName, trackerId, iterationId, exception); - } - finally - { - infoSrv.unregisterTrackerThread(); - - ofNullable(state).ifPresent(this::turnOff); - - getRunLock().release(); - } - } - - /** - * At the end of the tracking method, the {@link TrackerState} should be turned off. - * However, during a rollback (that could be started by another tracker) the {@link TrackerState} instance - * could be set to null, even after passing a null check we could get a NPE. - * For that reason, this method turns off the tracker state and ignore any {@link NullPointerException} (actually - * any exception) that could be thrown. - */ - private void turnOff(TrackerState state) { - try - { - state.setRunning(false); - state.setCheck(false); - } catch (Exception exception) - { - LOGGER.error("Unable to properly turn off the TrackerState instance. See the stacktrace below for further details.", exception); - } - } - - public boolean getRollback() - { - return this.rollback; - } - - public Throwable getRollbackCausedBy() - { - return this.rollbackCausedBy; - } - - public void setRollback(boolean rollback, Throwable rollbackCausedBy) - { - this.rollback = rollback; - this.rollbackCausedBy = rollbackCausedBy; - } - - private void continueState() - { - infoSrv.continueState(state); - state.incrementTrackerCycles(); - } - - public synchronized void invalidateState() - { - state = null; - } - - @Override - public synchronized TrackerState getTrackerState() - { - if(this.state != null) - { - return this.state; - } - else - { - return this.infoSrv.getTrackerInitialState(); - } - } - - int getMaxLiveSearchers() - { - return maxLiveSearchers; - } - - void checkShutdown() - { - if(shutdown) - { - throw new IndexTrackingShutdownException(); - } - } - - @Override - public boolean isAlreadyInShutDownMode() - { - return shutdown; - } - - @Override - public void setShutdown(boolean shutdown) - { - this.shutdown = shutdown; - } - - @Override - public void shutdown() - { - setShutdown(true); - } - - /** - * Trackers implementing this method should decide if the Write Lock is applied - * globally for every Tracker Thread (static) or locally for each running Thread - */ - public abstract Semaphore getWriteLock(); - - /** - * Trackers implementing this method should decide if the Run Lock is applied - * globally for every Tracker Thread (static) or locally for each running Thread - */ - public abstract Semaphore getRunLock(); - - public Properties getProps() - { - return props; - } - - public Type getType() - { - return type; - } - - /** - * Set the shard property using the shard key. - */ - void updateShardProperty() - { - shardKey.ifPresent(shardKeyName -> { - Optional updatedShardProperty = getShardProperty(shardKeyName); - if (!shardProperty.equals(updatedShardProperty)) - { - if (updatedShardProperty.isEmpty()) - { - LOGGER.warn("The model defining {} property has been disabled", shardKeyName); - } - else - { - LOGGER.info("New {} property found for {}", SHARD_KEY_KEY, shardKeyName); - } - } - shardProperty = updatedShardProperty; - }); - } - - /** - * Given the field name, returns the name of the property definition. - * If the property definition is not found, Empty optional is returned. - * - * @param field the field name. - * @return the name of the associated property definition if present, Optional.Empty() otherwise - */ - static Optional getShardProperty(String field) - { - if (StringUtils.isBlank(field)) - { - throw new IllegalArgumentException("Sharding property " + SHARD_KEY_KEY + " has not been set."); - } - - AlfrescoSolrDataModel dataModel = AlfrescoSolrDataModel.getInstance(); - NamespaceDAO namespaceDAO = dataModel.getNamespaceDAO(); - DictionaryService dictionaryService = dataModel.getDictionaryService(CMISStrictDictionaryService.DEFAULT); - PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition("http://www.alfresco.org/model/content/1.0", - namespaceDAO, - dictionaryService, - field); - - return ofNullable(propertyDef).map(PropertyDefinition::getName); - } - - /** - * Returns information about the {@link org.alfresco.solr.client.Node} associated with the given dbid. - * - * @param dbid the node identifier. - * @return the {@link org.alfresco.solr.client.Node} associated with the given dbid. - */ - public NodeReport checkNode(Long dbid) - { - NodeReport nodeReport = new NodeReport(); - nodeReport.setDbid(dbid); - - this.infoSrv.addCommonNodeReportInfo(nodeReport); - - return nodeReport; - } - - /** - * Returns the {@link DocRouter} instance in use on this node. - * - * @return the {@link DocRouter} instance in use on this node. - */ - public DocRouter getDocRouter() - { - return this.docRouter; - } - - private void firstUpdateShardProperty() - { - shardKey.ifPresent( shardKeyName -> { - updateShardProperty(); - if (shardProperty.isEmpty()) - { - LOGGER.warn("Sharding property {} was set to {}, but no such property was found.", SHARD_KEY_KEY, shardKeyName); - } - }); - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker; + +import static java.util.Optional.ofNullable; + +import static org.alfresco.repo.index.shard.ShardMethodEnum.DB_ID; +import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_KEY_KEY; + +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.Semaphore; + +import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.index.shard.ShardMethodEnum; +import org.alfresco.repo.search.impl.QueryParserUtils; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.IndexTrackingShutdownException; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.NodeReport; +import org.alfresco.solr.TrackerState; +import org.alfresco.solr.client.SOLRAPIClient; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract base class that provides common {@link Tracker} behaviour. + * + * @author Matt Ward + */ +public abstract class AbstractTracker implements Tracker +{ + static final long TIME_STEP_32_DAYS_IN_MS = 1000 * 60 * 60 * 24 * 32L; + static final long TIME_STEP_1_HR_IN_MS = 60 * 60 * 1000L; + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTracker.class); + + protected Properties props; + protected SOLRAPIClient client; + InformationServer infoSrv; + protected String coreName; + StoreRef storeRef; + long batchCount; + TrackerStats trackerStats; + boolean runPostModelLoadInit = true; + private int maxLiveSearchers; + private volatile boolean shutdown = false; + + protected volatile TrackerState state; + protected int shardCount; + protected int shardInstance; + ShardMethodEnum shardMethod; + protected boolean transformContent; + String shardTemplate; + protected volatile boolean rollback; + /** + * When rollback is set, original error is also gathered in order to provide detailed logging. + */ + protected Throwable rollbackCausedBy; + protected final Type type; + protected final String trackerId; + + DocRouter docRouter; + + /** + * The property to use for determining the shard. + * Note that this property is not used by all trackers, it is actually managed by the {@link ShardStatePublisher} and + * {@link MetadataTracker}. We put this property here because otherwise we should introduce another supertype layer + * for those two trackers. + */ + protected Optional shardProperty = Optional.empty(); + + /** + * The string representation of the shard key. + * Note that this property is not used by all trackers, it is actually managed by the {@link ShardStatePublisher} and + * {@link MetadataTracker}. We put this property here because otherwise we should introduce another supertype layer + * for those two trackers. + */ + protected Optional shardKey; + + /** + * Default constructor, strictly for testing. + */ + protected AbstractTracker(Type type) + { + this.type = type; + this.trackerId = type + "@" + hashCode(); + } + + protected AbstractTracker(Properties p, SOLRAPIClient client, String coreName, InformationServer informationServer,Type type) + { + this.props = p; + this.client = client; + this.coreName = coreName; + this.infoSrv = informationServer; + + storeRef = new StoreRef(p.getProperty("alfresco.stores", "workspace://SpacesStore")); + batchCount = Integer.parseInt(p.getProperty("alfresco.batch.count", "5000")); + maxLiveSearchers = Integer.parseInt(p.getProperty("alfresco.maxLiveSearchers", "2")); + + shardCount = Integer.parseInt(p.getProperty("shard.count", "1")); + shardInstance = Integer.parseInt(p.getProperty("shard.instance", "0")); + shardMethod = ShardMethodEnum.getShardMethod(p.getProperty("shard.method", DB_ID.name())); + + shardTemplate = p.getProperty("alfresco.template", ""); + + transformContent = Boolean.parseBoolean(p.getProperty("alfresco.index.transformContent", "true")); + + this.trackerStats = this.infoSrv.getTrackerStats(); + + this.type = type; + + this.trackerId = type + "@" + hashCode(); + + shardKey = ofNullable(p.getProperty(SHARD_KEY_KEY)); + + firstUpdateShardProperty(); + + docRouter = DocRouterFactory.getRouter(p, shardMethod); + } + + /** + * Subclasses must implement behaviour that completes the following steps, in order: + * + *
    + *
  1. Purge
  2. + *
  3. Reindex
  4. + *
  5. Index
  6. + *
  7. Track repository
  8. + *
+ * + * @param iterationId an identifier which is uniquely associated with a given iteration. + */ + protected abstract void doTrack(String iterationId) throws Throwable; + + + private boolean assertTrackerStateRemainsNull() { + + /* + * This assertion is added to accommodate DistributedAlfrescoSolrTrackerRaceTest. + * The sleep is needed to allow the test case to add a txn into the queue before + * the tracker makes its call to pull transactions from the test repo client. + */ + + try + { + Thread.sleep(5000); + } + catch(Exception e) + { + // Ignore + } + + + /* + * This ensures that getTrackerState does not have the side effect of setting the + * state instance variable. This allows classes outside of the tracker framework + * to safely call getTrackerState without interfering with the trackers design. + */ + + getTrackerState(); + + return state == null; + + } + /** + * Template method - subclasses must implement the {@link Tracker}-specific indexing + * by implementing the abstract method {@link #doTrack(String)}. + */ + @Override + public void track() + { + String iterationId = "IT #" + System.currentTimeMillis(); + + if(getRunLock().availablePermits() == 0) + { + LOGGER.info("[{} / {} / {}] Tracker already registered.", coreName, trackerId, iterationId); + return; + } + + try + { + /* + * The runLock ensures that for each tracker type (metadata, content, commit, cascade) only one tracker will + * be running at a time. + */ + getRunLock().acquire(); + + if (state==null && Boolean.parseBoolean(System.getProperty("alfresco.test", "false"))) + { + assert(assertTrackerStateRemainsNull()); + } + + if(this.state == null) + { + this.state = getTrackerState(); + + LOGGER.debug("[{} / {} / {}] Global Tracker State set to: {}", coreName, trackerId, iterationId, this.state.toString()); + this.state.setRunning(true); + } + else + { + continueState(); + this.state.setRunning(true); + } + + infoSrv.registerTrackerThread(); + + try + { + doTrack(iterationId); + } + catch(IndexTrackingShutdownException t) + { + setRollback(true, t); + LOGGER.info("[{} / {} / {}] Tracking cycle stopped. See the stacktrace below for further details.", coreName, trackerId, iterationId, t); + } + catch(Throwable t) + { + setRollback(true, t); + if (t instanceof SocketTimeoutException || t instanceof ConnectException) + { + LOGGER.warn("[{} / {} / {}] Tracking communication timed out. See the stacktrace below for further details.", coreName, trackerId, iterationId); + LOGGER.debug("[{} / {} / {}] Stack trace", coreName, trackerId, iterationId, t); + } + else + { + LOGGER.error("[{} / {} / {}] Tracking failure. See the stacktrace below for further details.", coreName, trackerId, iterationId, t); + } + } + } + catch (Exception exception) + { + LOGGER.error("[{} / {} / {}] Some problem was detected while resetting the Tracker State. See the stracktrace below for further details." + , coreName, trackerId, iterationId, exception); + } + finally + { + infoSrv.unregisterTrackerThread(); + + ofNullable(state).ifPresent(this::turnOff); + + getRunLock().release(); + } + } + + /** + * At the end of the tracking method, the {@link TrackerState} should be turned off. + * However, during a rollback (that could be started by another tracker) the {@link TrackerState} instance + * could be set to null, even after passing a null check we could get a NPE. + * For that reason, this method turns off the tracker state and ignore any {@link NullPointerException} (actually + * any exception) that could be thrown. + */ + private void turnOff(TrackerState state) { + try + { + state.setRunning(false); + state.setCheck(false); + } catch (Exception exception) + { + LOGGER.error("Unable to properly turn off the TrackerState instance. See the stacktrace below for further details.", exception); + } + } + + public boolean getRollback() + { + return this.rollback; + } + + public Throwable getRollbackCausedBy() + { + return this.rollbackCausedBy; + } + + public void setRollback(boolean rollback, Throwable rollbackCausedBy) + { + this.rollback = rollback; + this.rollbackCausedBy = rollbackCausedBy; + } + + private void continueState() + { + infoSrv.continueState(state); + state.incrementTrackerCycles(); + } + + public synchronized void invalidateState() + { + state = null; + } + + @Override + public synchronized TrackerState getTrackerState() + { + if(this.state != null) + { + return this.state; + } + else + { + return this.infoSrv.getTrackerInitialState(); + } + } + + int getMaxLiveSearchers() + { + return maxLiveSearchers; + } + + void checkShutdown() + { + if(shutdown) + { + throw new IndexTrackingShutdownException(); + } + } + + @Override + public boolean isAlreadyInShutDownMode() + { + return shutdown; + } + + @Override + public void setShutdown(boolean shutdown) + { + this.shutdown = shutdown; + } + + @Override + public void shutdown() + { + setShutdown(true); + } + + /** + * Trackers implementing this method should decide if the Write Lock is applied + * globally for every Tracker Thread (static) or locally for each running Thread + */ + public abstract Semaphore getWriteLock(); + + /** + * Trackers implementing this method should decide if the Run Lock is applied + * globally for every Tracker Thread (static) or locally for each running Thread + */ + public abstract Semaphore getRunLock(); + + public Properties getProps() + { + return props; + } + + public Type getType() + { + return type; + } + + /** + * Set the shard property using the shard key. + */ + void updateShardProperty() + { + shardKey.ifPresent(shardKeyName -> { + Optional updatedShardProperty = getShardProperty(shardKeyName); + if (!shardProperty.equals(updatedShardProperty)) + { + if (updatedShardProperty.isEmpty()) + { + LOGGER.warn("The model defining {} property has been disabled", shardKeyName); + } + else + { + LOGGER.info("New {} property found for {}", SHARD_KEY_KEY, shardKeyName); + } + } + shardProperty = updatedShardProperty; + }); + } + + /** + * Given the field name, returns the name of the property definition. + * If the property definition is not found, Empty optional is returned. + * + * @param field the field name. + * @return the name of the associated property definition if present, Optional.Empty() otherwise + */ + static Optional getShardProperty(String field) + { + if (StringUtils.isBlank(field)) + { + throw new IllegalArgumentException("Sharding property " + SHARD_KEY_KEY + " has not been set."); + } + + AlfrescoSolrDataModel dataModel = AlfrescoSolrDataModel.getInstance(); + NamespaceDAO namespaceDAO = dataModel.getNamespaceDAO(); + DictionaryService dictionaryService = dataModel.getDictionaryService(CMISStrictDictionaryService.DEFAULT); + PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition("http://www.alfresco.org/model/content/1.0", + namespaceDAO, + dictionaryService, + field); + + return ofNullable(propertyDef).map(PropertyDefinition::getName); + } + + /** + * Returns information about the {@link org.alfresco.solr.client.Node} associated with the given dbid. + * + * @param dbid the node identifier. + * @return the {@link org.alfresco.solr.client.Node} associated with the given dbid. + */ + public NodeReport checkNode(Long dbid) + { + NodeReport nodeReport = new NodeReport(); + nodeReport.setDbid(dbid); + + this.infoSrv.addCommonNodeReportInfo(nodeReport); + + return nodeReport; + } + + /** + * Returns the {@link DocRouter} instance in use on this node. + * + * @return the {@link DocRouter} instance in use on this node. + */ + public DocRouter getDocRouter() + { + return this.docRouter; + } + + private void firstUpdateShardProperty() + { + shardKey.ifPresent( shardKeyName -> { + updateShardProperty(); + if (shardProperty.isEmpty()) + { + LOGGER.warn("Sharding property {} was set to {}, but no such property was found.", SHARD_KEY_KEY, shardKeyName); + } + }); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractWorker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractWorker.java index 1d013f18f..0ed19ab02 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractWorker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractWorker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java index bc08e05b8..35ff9cc17 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java @@ -1,939 +1,939 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import com.google.common.collect.Lists; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.repo.index.shard.ShardMethodEnum; -import org.alfresco.solr.AclReport; -import org.alfresco.solr.BoundedDeque; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.TrackerState; -import org.alfresco.solr.adapters.IOpenBitSet; -import org.alfresco.solr.client.Acl; -import org.alfresco.solr.client.AclChangeSet; -import org.alfresco.solr.client.AclChangeSets; -import org.alfresco.solr.client.AclReaders; -import org.alfresco.solr.client.SOLRAPIClient; -import org.json.JSONException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Multithreaded ACL {@link Tracker} implementation. - * - * @author Matt Ward - **/ - -public class AclTracker extends ActivatableTracker -{ - protected final static Logger LOGGER = LoggerFactory.getLogger(AclTracker.class); - - private static final int ACL_CHANGE_SETS_FOUND_QUEUE_SIZE = 100; - private static final int DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE = 2000; - private static final int DEFAULT_ACL_BATCH_SIZE = 100; - private static final int DEFAULT_ACL_TRACKER_MAX_PARALLELISM = 32; - private static final long DEFAULT_ACL_TRACKER_TIMESTEP = TIME_STEP_1_HR_IN_MS; - - private static final long INITIAL_MAX_ACL_CHANGE_SET_ID = 2000L; - private static final int MAX_NUMBER_OF_ACL_CHANGE_SETS = 2000; - - private static final long MAX_TIME_STEP = TIME_STEP_32_DAYS_IN_MS; - - // Repository Remote API doesn't accept more than 512 aclChangeSetIds by invocation - private static final int MAX_ACL_CHANGE_SET_BATCH_SIZE = 512; - - private int aclTrackerParallelism; - - private int changeSetAclsBatchSize; - private int aclBatchSize; - private long timeStep; - private int maxNumberOfAclChangeSets; - - private ConcurrentLinkedQueue aclChangeSetsToReindex = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclChangeSetsToIndex = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclChangeSetsToPurge = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclsToReindex = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclsToIndex = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclsToPurge = new ConcurrentLinkedQueue<>(); - private DocRouter docRouter; - - private ForkJoinPool forkJoinPool; - - // Share run and write locks across all AclTracker threads - private static Map RUN_LOCK_BY_CORE = new ConcurrentHashMap<>(); - private static Map WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>(); - @Override - public Semaphore getWriteLock() - { - return WRITE_LOCK_BY_CORE.get(coreName); - } - @Override - public Semaphore getRunLock() - { - return RUN_LOCK_BY_CORE.get(coreName); - } - - - /** - * Default constructor, for testing. - */ - AclTracker() - { - super(Tracker.Type.ACL); - } - - public AclTracker(Properties p, SOLRAPIClient client, - String coreName, InformationServer informationServer) - { - super(p, client, coreName, informationServer, Tracker.Type.ACL); - changeSetAclsBatchSize = Integer.parseInt(p.getProperty("alfresco.changeSetAclsBatchSize", - String.valueOf(DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE))); - if (changeSetAclsBatchSize > MAX_ACL_CHANGE_SET_BATCH_SIZE) - { - LOGGER.warn("Max value for 'alfresco.changeSetAclsBatchSize' is 512. " - + "This value is being taken instead of the one specified in 'solrcore.properties': " + changeSetAclsBatchSize); - changeSetAclsBatchSize = MAX_ACL_CHANGE_SET_BATCH_SIZE; - } - aclBatchSize = Integer.parseInt(p.getProperty("alfresco.aclBatchSize", - String.valueOf(DEFAULT_ACL_BATCH_SIZE))); - docRouter = DocRouterFactory.getRouter(p, shardMethod); - - aclTrackerParallelism = Integer.parseInt(p.getProperty("alfresco.acl.tracker.maxParallelism", - String.valueOf(DEFAULT_ACL_TRACKER_MAX_PARALLELISM))); - forkJoinPool = new ForkJoinPool(aclTrackerParallelism); - - timeStep = Long.parseLong(p.getProperty("alfresco.acl.tracker.timestep", - String.valueOf(DEFAULT_ACL_TRACKER_TIMESTEP))); - - maxNumberOfAclChangeSets = Integer.parseInt(p.getProperty("alfresco.acl.tracker.maxNumberOfAclChangeSets", - String.valueOf(MAX_NUMBER_OF_ACL_CHANGE_SETS))); - - RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); - WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); - } - - @Override - protected void doTrack(String iterationId) throws Throwable - { - trackRepository(); - } - - public void maintenance() throws Exception - { - purgeAclChangeSets(); - purgeAcls(); - reindexAclChangeSets(); - reindexAcls(); - indexAclChangeSets(); - indexAcls(); - } - - - public boolean hasMaintenance() - { - return aclChangeSetsToReindex.size() > 0 || - aclChangeSetsToIndex.size() > 0 || - aclChangeSetsToPurge.size() > 0 || - aclsToReindex.size() > 0 || - aclsToIndex.size() > 0 || - aclsToPurge.size() > 0; - } - - protected void indexAclChangeSets() throws AuthenticationException, IOException, JSONException - { - boolean requiresCommit = false; - while (aclChangeSetsToIndex.peek() != null) - { - Long aclChangeSetId = aclChangeSetsToIndex.poll(); - if (aclChangeSetId != null) - { - AclChangeSets aclChangeSets = client.getAclChangeSets(null, aclChangeSetId, - null, aclChangeSetId+1, 1); - if ((aclChangeSets.getAclChangeSets().size() > 0) && - aclChangeSetId.equals(aclChangeSets.getAclChangeSets().get(0).getId())) - { - AclChangeSet changeSet = aclChangeSets.getAclChangeSets().get(0); - List acls = client.getAcls(Collections.singletonList(changeSet), null, Integer.MAX_VALUE); - for (Acl acl : acls) - { - List readers = client.getAclReaders(Collections.singletonList(acl)); - indexAcl(readers, false); - } - this.infoSrv.indexAclTransaction(changeSet, false); - LOGGER.info("[CORE {}] - INDEX ACTION - AclChangeSetId {} has been indexed", coreName, aclChangeSetId); - requiresCommit = true; - } - else - { - LOGGER.info("[CORE {}] - INDEX ACTION - AclChangeSetId {} was not found in database, " + - "it has NOT been reindexed", coreName, aclChangeSetId); - } - } - checkShutdown(); - } - if(requiresCommit) - { - checkShutdown(); - //this.infoSrv.commit(); - } - } - - protected void indexAcls() throws AuthenticationException, IOException, JSONException - { - while (aclsToIndex.peek() != null) - { - Long aclId = aclsToIndex.poll(); - if (aclId != null) - { - //System.out.println("############## Indexing ACL ID:"+aclId); - Acl acl = new Acl(0, aclId); - List readers = client.getAclReaders(Collections.singletonList(acl)); - //AclReaders r = readers.get(0); - //System.out.println("############## READERS ID:"+r.getId()+":"+r.getReaders()); - indexAcl(readers, false); - LOGGER.info("[CORE {}] - INDEX ACTION - AclId {} has been indexed", coreName, aclId); - } - checkShutdown(); - } - } - - protected void reindexAclChangeSets() throws AuthenticationException, IOException, JSONException - { - boolean requiresCommit = false; - while (aclChangeSetsToReindex.peek() != null) - { - Long aclChangeSetId = aclChangeSetsToReindex.poll(); - if (aclChangeSetId != null) - { - this.infoSrv.deleteByAclChangeSetId(aclChangeSetId); - - AclChangeSets aclChangeSets = client.getAclChangeSets(null, aclChangeSetId, - null, aclChangeSetId+1, 1); - if ((aclChangeSets.getAclChangeSets().size() > 0) && - aclChangeSetId.equals(aclChangeSets.getAclChangeSets().get(0).getId())) - { - AclChangeSet changeSet = aclChangeSets.getAclChangeSets().get(0); - List acls = client.getAcls(Collections.singletonList(changeSet), - null, Integer.MAX_VALUE); - for (Acl acl : acls) - { - List readers = client.getAclReaders(Collections.singletonList(acl)); - indexAcl(readers, true); - } - - this.infoSrv.indexAclTransaction(changeSet, true); - LOGGER.info("[CORE {}] - REINDEX ACTION - AclChangeSetId {} has been reindexed", - coreName, aclChangeSetId); - requiresCommit = true; - } - else - { - LOGGER.info("[CORE {}] - REINDEX ACTION - AclChangeSetId {} was not found in database, " + - "it has NOT been reindexed", coreName, aclChangeSetId); - } - } - checkShutdown(); - } - if(requiresCommit) - { - checkShutdown(); - //this.infoSrv.commit(); - } - } - - protected void reindexAcls() throws AuthenticationException, IOException, JSONException - { - boolean requiresCommit = false; - while (aclsToReindex.peek() != null) - { - Long aclId = aclsToReindex.poll(); - if (aclId != null) - { - this.infoSrv.deleteByAclId(aclId); - - Acl acl = new Acl(0, aclId); - List readers = client.getAclReaders(Collections.singletonList(acl)); - indexAcl(readers, true); - LOGGER.info("[CORE {}] - REINDEX ACTION - aclId {} has been reindexed", coreName, aclId); - requiresCommit = true; - } - checkShutdown(); - } - if(requiresCommit) - { - checkShutdown(); - //this.infoSrv.commit(); - } - } - - protected void purgeAclChangeSets() throws IOException, JSONException - { - while (aclChangeSetsToPurge.peek() != null) - { - Long aclChangeSetId = aclChangeSetsToPurge.poll(); - if (aclChangeSetId != null) - { - this.infoSrv.deleteByAclChangeSetId(aclChangeSetId); - LOGGER.info("[CORE {}] - PURGE ACTION - Purged aclChangeSetId {}", coreName, aclChangeSetId); - } - checkShutdown(); - } - } - - protected void purgeAcls() throws IOException, JSONException - { - while (aclsToPurge.peek() != null) - { - Long aclId = aclsToPurge.poll(); - if (aclId != null) - { - this.infoSrv.deleteByAclId(aclId); - LOGGER.info("[CORE {}] - PURGE ACTION - Purged aclId {}", coreName, aclId); - } - checkShutdown(); - } - } - - - // ACL change sets - - public void addAclChangeSetToReindex(Long aclChangeSetToReindex) - { - aclChangeSetsToReindex.offer(aclChangeSetToReindex); - } - - public void addAclChangeSetToIndex(Long aclChangeSetToIndex) - { - aclChangeSetsToIndex.offer(aclChangeSetToIndex); - } - - public void addAclChangeSetToPurge(Long aclChangeSetToPurge) - { - aclChangeSetsToPurge.offer(aclChangeSetToPurge); - } - - // ACLs - - public void addAclToReindex(Long aclToReindex) - { - aclsToReindex.offer(aclToReindex); - } - - public void addAclToIndex(Long aclToIndex) - { - aclsToIndex.offer(aclToIndex); - } - - public void addAclToPurge(Long aclToPurge) - { - aclsToPurge.offer(aclToPurge); - } - - @Override - protected void clearScheduledMaintenanceWork() - { - logAndClear(aclChangeSetsToIndex, "ACL ChangeSets to be indexed"); - logAndClear(aclsToIndex, "ACLs to be indexed"); - - logAndClear(aclChangeSetsToReindex, "ACL ChangeSets to be re-indexed"); - logAndClear(aclsToReindex, "ACLs to be re-indexed"); - - logAndClear(aclChangeSetsToPurge, "ACL ChangeSets to be purged"); - logAndClear(aclsToPurge, "ACLs to be purged"); - } - - protected void trackRepository() throws IOException, AuthenticationException, JSONException - { - checkShutdown(); - - TrackerState state = super.getTrackerState(); - - // Check we are tracking the correct repository - if(state.getTrackerCycles() == 0) - { - //We have a new tracker state so do the checks. - checkRepoAndIndexConsistency(state); - } - - checkShutdown(); - trackAclChangeSets(); - } - - /** - * Checks the first and last TX time - */ - private void checkRepoAndIndexConsistency(TrackerState state) throws AuthenticationException, IOException, JSONException - { - AclChangeSets firstChangeSets = null; - if (state.getLastGoodChangeSetCommitTimeInIndex() == 0) - { - state.setCheckedLastAclTransactionTime(true); - state.setCheckedFirstAclTransactionTime(true); - LOGGER.info("[CORE {}] - No acl transactions found - no verification required", coreName); - - firstChangeSets = client.getAclChangeSets(null, 0L, - null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); - if (!firstChangeSets.getAclChangeSets().isEmpty()) - { - AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0); - long firstChangeSetCommitTime = firstChangeSet.getCommitTimeMs(); - state.setLastGoodChangeSetCommitTimeInIndex(firstChangeSetCommitTime); - setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state); - } - } - - if (!state.isCheckedFirstAclTransactionTime()) - { - firstChangeSets = client.getAclChangeSets(null, 0L, - null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); - if (!firstChangeSets.getAclChangeSets().isEmpty()) - { - AclChangeSet firstAclChangeSet= firstChangeSets.getAclChangeSets().get(0); - long firstAclTxId = firstAclChangeSet.getId(); - long firstAclTxCommitTime = firstAclChangeSet.getCommitTimeMs(); - int setSize = this.infoSrv.getAclTxDocsSize(Long.toString(firstAclTxId), - Long.toString(firstAclTxCommitTime)); - - if (setSize == 0) - { - LOGGER.error("[CORE {}] First acl transaction was not found with the correct timestamp.", coreName); - LOGGER.error("SOLR has successfully connected to your repository " + - "however the SOLR indexes and repository database do not match."); - LOGGER.error("If this is a new or rebuilt database your SOLR indexes " + - "also need to be re-built to match the database."); - LOGGER.error("You can also check your SOLR connection details in solrcore.properties."); - throw new AlfrescoRuntimeException("Initial acl transaction not found with correct timestamp"); - } - else if (setSize == 1) - { - state.setCheckedFirstTransactionTime(true); - LOGGER.info("[CORE {}] Verified first acl transaction and timestamp in index", coreName); - } - else - { - LOGGER.warn("[CORE {}] Duplicate initial acl transaction found with correct timestamp", coreName); - } - } - } - - // Checks that the last aclTxId in solr is <= last aclTxId in repo - if (!state.isCheckedLastAclTransactionTime()) - { - if (firstChangeSets == null) - { - firstChangeSets = client.getAclChangeSets(null, 0L, - null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); - } - - setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state); - Long maxChangeSetCommitTimeInRepo = firstChangeSets.getMaxChangeSetCommitTime(); - Long maxChangeSetIdInRepo = firstChangeSets.getMaxChangeSetId(); - if (maxChangeSetCommitTimeInRepo != null && maxChangeSetIdInRepo != null) - { - AclChangeSet maxAclTxInIndex = this.infoSrv.getMaxAclChangeSetIdAndCommitTimeInIndex(); - if (maxAclTxInIndex.getCommitTimeMs() > maxChangeSetCommitTimeInRepo) - { - LOGGER.error("[CORE {}] Last acl transaction was found in index with " + - "timestamp later than that of repository.", coreName); - LOGGER.error("Max Acl Tx In Index: " + maxAclTxInIndex.getId() + ", In Repo: " + maxChangeSetIdInRepo); - LOGGER.error("Max Acl Tx Commit Time In Index: " + maxAclTxInIndex.getCommitTimeMs() + ", In Repo: " - + maxChangeSetCommitTimeInRepo); - LOGGER.error("SOLR has successfully connected to your repository" + - " however the SOLR indexes and repository database do not match."); - LOGGER.error("If this is a new or rebuilt database your SOLR indexes " + - "also need to be re-built to match the database."); - LOGGER.error("You can also check your SOLR connection details in solrcore.properties."); - throw new AlfrescoRuntimeException("Last acl transaction found in index with incorrect timestamp"); - } - else - { - state.setCheckedLastAclTransactionTime(true); - LOGGER.info("[CORE {}] - Verified last acl transaction timestamp in index less" + - " than or equal to that of repository.", coreName); - } - } - } - } - - /** - * @param changeSetsFound BoundedDeque - * @param lastGoodChangeSetCommitTimeInIndex long - * @return Long - */ - protected Long getChangeSetFromCommitTime(BoundedDeque changeSetsFound, long lastGoodChangeSetCommitTimeInIndex) - { - if(changeSetsFound.size() > 0) - { - return changeSetsFound.getLast().getCommitTimeMs(); - } - else - { - return lastGoodChangeSetCommitTimeInIndex; - } - } - - protected AclChangeSets getSomeAclChangeSets(BoundedDeque changeSetsFound, Long fromCommitTime, - long timeStep, int maxResults, long endTime) - throws AuthenticationException, IOException, JSONException - { - long actualTimeStep = timeStep; - - AclChangeSets aclChangeSets; - // step forward in time until we find something or hit the time bound - // max id unbounded - Long startTime = fromCommitTime == null ? Long.valueOf(0L) : fromCommitTime; - do - { - aclChangeSets = client.getAclChangeSets(startTime, null, - startTime + actualTimeStep, null, maxResults); - startTime += actualTimeStep; - actualTimeStep *= 2; - if(actualTimeStep > MAX_TIME_STEP) - { - actualTimeStep = MAX_TIME_STEP; - } - } - while( ((aclChangeSets.getAclChangeSets().size() == 0) && (startTime < endTime)) || - ((aclChangeSets.getAclChangeSets().size() > 0) && alreadyFoundChangeSets(changeSetsFound, aclChangeSets))); - - return aclChangeSets; - - } - - private boolean alreadyFoundChangeSets(BoundedDeque changeSetsFound, AclChangeSets aclChangeSets) - { - if(changeSetsFound.size() == 0) - { - return false; - } - - if(aclChangeSets.getAclChangeSets().size() == 1) - { - return aclChangeSets.getAclChangeSets().get(0).getId() == changeSetsFound.getLast().getId(); - } - else - { - HashSet alreadyFound = new HashSet<>(changeSetsFound.getDeque()); - for(AclChangeSet aclChangeSet : aclChangeSets.getAclChangeSets()) - { - if(!alreadyFound.contains(aclChangeSet)) - { - return false; - } - } - return true; - } - } - - /** - * @param aclReaderList List - * @param overwrite boolean - */ - protected void indexAcl(List aclReaderList, boolean overwrite) throws IOException - { - long time = this.infoSrv.indexAcl(aclReaderList, overwrite); - trackerStats.addAclTime(time); - } - - public IndexHealthReport checkIndex(Long toAclTx, Long fromTime, Long toTime) - throws AuthenticationException, IOException, JSONException - { - // DB ACL TX Count - long firstChangeSetCommitTimex = 0; - AclChangeSets firstChangeSets = client.getAclChangeSets(null, 0L, - null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); - if(firstChangeSets.getAclChangeSets().size() > 0) - { - AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0); - firstChangeSetCommitTimex = firstChangeSet.getCommitTimeMs(); - } - - IOpenBitSet aclTxIdsInDb = infoSrv.getOpenBitSetInstance(); - long lastAclTxCommitTime = firstChangeSetCommitTimex; - if (fromTime != null) - { - lastAclTxCommitTime = fromTime; - } - - long maxAclTxId = 0; - Long minAclTxId = null; - long endTime = System.currentTimeMillis() + infoSrv.getHoleRetention(); - AclChangeSets aclTransactions; - BoundedDeque changeSetsFound = new BoundedDeque<>(ACL_CHANGE_SETS_FOUND_QUEUE_SIZE); - DO: do - { - aclTransactions = getSomeAclChangeSets(changeSetsFound, - lastAclTxCommitTime, timeStep, maxNumberOfAclChangeSets, endTime); - for (AclChangeSet set : aclTransactions.getAclChangeSets()) - { - // include - if (toTime != null) - { - if (set.getCommitTimeMs() > toTime) - { - break DO; - } - } - if (toAclTx != null) - { - if (set.getId() > toAclTx) - { - break DO; - } - } - - // bounds for later loops - if (minAclTxId == null) - { - minAclTxId = set.getId(); - } - if (maxAclTxId < set.getId()) - { - maxAclTxId = set.getId(); - } - - lastAclTxCommitTime = set.getCommitTimeMs(); - aclTxIdsInDb.set(set.getId()); - changeSetsFound.add(set); - } - } - while (aclTransactions.getAclChangeSets().size() > 0); - - return this.infoSrv.reportAclTransactionsInIndex(minAclTxId, aclTxIdsInDb, maxAclTxId); - } - - public List getAclsForDbAclTransaction(Long acltxid) - { - try - { - ArrayList answer = new ArrayList<>(); - AclChangeSets changeSet = client.getAclChangeSets(null, acltxid, null, - acltxid+1, 1); - List acls = client.getAcls(changeSet.getAclChangeSets(), null, Integer.MAX_VALUE); - for (Acl acl : acls) - { - answer.add(acl.getId()); - } - return answer; - } - catch (IOException | JSONException | AuthenticationException e) - { - throw new AlfrescoRuntimeException("Failed to get acls", e); - } - } - - public AclReport checkAcl(Long aclid) - { - AclReport aclReport = new AclReport(); - aclReport.setAclId(aclid); - - // In DB - - try - { - List readers = client.getAclReaders(Collections.singletonList(new Acl(0, aclid))); - aclReport.setExistsInDb(readers.size() == 1); - } - catch (IOException | JSONException | AuthenticationException e) - { - aclReport.setExistsInDb(false); - } - - // In Index - return this.infoSrv.checkAclInIndex(aclid, aclReport); - } - - /** - * Every ACL Change Set contains a list of ACLs to be indexed. - * This method gets ACL Change Sets from Alfresco Repository to be indexed. - * - * The indexing is performed in batches of ACL Change Sets and the Tracker Status - * is updated in batched of ACLs. - * - * Tracker Status contains the Commit Time from the latest ACL Change Set indexed, - * so new operations can be retrieved from Repository starting with that time. - */ - protected void trackAclChangeSets() throws AuthenticationException, IOException, JSONException - { - - long startElapsed = System.nanoTime(); - - AclChangeSets aclChangeSets; - BoundedDeque changeSetsFound = new BoundedDeque<>(ACL_CHANGE_SETS_FOUND_QUEUE_SIZE); - long totalAclCount = 0; - int aclCount; - - LOGGER.info("{}-[CORE {}] Tracking ACLs", Thread.currentThread().getId(), coreName); - - do - { - try - { - getWriteLock().acquire(); - - /* - * We acquire the tracker state again here and set it globally. This is because the - * tracker state could have been invalidated due to a rollback by the CommitTracker. - * In this case the state will revert to the last transaction state record in the index. - */ - - this.state = getTrackerState(); - - Long fromCommitTime = getChangeSetFromCommitTime(changeSetsFound, - state.getLastChangeSetCommitTimeOnServer() == 0 ? state.getLastGoodChangeSetCommitTimeInIndex() - : state.getLastChangeSetCommitTimeOnServer()); - aclChangeSets = getSomeAclChangeSets(changeSetsFound, fromCommitTime, timeStep, maxNumberOfAclChangeSets, - state.getTimeToStopIndexing()); - - if (aclChangeSets.getAclChangeSets().size() > 0) - { - LOGGER.info("{}-[CORE {}] Found {} ACL change sets after lastTxCommitTime {}, ACL Change Sets from {} to {}", - Thread.currentThread().getId(), - coreName, - aclChangeSets.getAclChangeSets().size(), - fromCommitTime, - aclChangeSets.getAclChangeSets().get(0), - aclChangeSets.getAclChangeSets().get(aclChangeSets.getAclChangeSets().size() - 1)); - } - else - { - LOGGER.info("{}-[CORE {}] No ACL change set found after lastTxCommitTime {}", - Thread.currentThread().getId(), coreName, fromCommitTime); - } - - - // Make sure we do not go ahead of where we started - we will check the holes here - // correctly next time - if (aclChangeSets.getAclChangeSets() - .stream() - .anyMatch(changeSet -> changeSet.getCommitTimeMs() > state.getTimeToStopIndexing())) - { - break; - } - - final AtomicInteger counter = new AtomicInteger(); - Collection> changeSetBatches = aclChangeSets.getAclChangeSets().stream() - .peek(changeSetsFound::add) - .filter(this::isAclChangeSetAlreadyIndexed) - .collect(Collectors.groupingBy(it -> counter.getAndAdd(1) / changeSetAclsBatchSize)) - .values(); - - - for (List changeSetBatch : changeSetBatches) - { - aclCount = indexBatchOfChangeSets(changeSetBatch); - // Update last committed transactions - setLastChangeSetIdAndCommitTimeInTrackerState(changeSetBatch, state); - indexAclChangeSetAfterWorker(changeSetBatch, state); - - long endElapsed = System.nanoTime(); - trackerStats.addElapsedAclTime(aclCount, endElapsed-startElapsed); - startElapsed = endElapsed; - totalAclCount += aclCount; - } - - } - catch(InterruptedException | ExecutionException e) - { - throw new IOException(e); - } - finally - { - getWriteLock().release(); - } - - } - while ((aclChangeSets.getAclChangeSets().size() > 0)); - - LOGGER.info("{}-[CORE {}] Tracked {} ACLs", Thread.currentThread().getId(), coreName, totalAclCount); - - } - - private boolean isAclChangeSetAlreadyIndexed(AclChangeSet changeSet) - { - - try - { - boolean isInIndex = (changeSet.getCommitTimeMs() <= state.getLastIndexedChangeSetCommitTime() && - infoSrv.aclChangeSetInIndex(changeSet.getId(), true)); - if (LOGGER.isTraceEnabled()) - { - LOGGER.trace("{}-[CORE {}] Skipping change Set Id {} as it was already indexed", - Thread.currentThread().getId(), coreName, changeSet.getId()); - } - return !isInIndex; - } - catch (IOException e) - { - LOGGER.warn( - "{}-[CORE {}] Error catched while checking if ACL Change Set {} was in index", - Thread.currentThread().getId(), coreName, changeSet.getId(), e); - return true; - } - - } - - private void setLastChangeSetIdAndCommitTimeInTrackerState(List aclChangeSets, TrackerState state) - { - - if (!aclChangeSets.isEmpty()) - { - long maxChangeSetCommitTime = - aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getCommitTimeMs)).orElseThrow().getCommitTimeMs(); - state.setLastChangeSetCommitTimeOnServer(maxChangeSetCommitTime); - - long maxChangeSetId = aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getId)).orElseThrow().getId(); - state.setLastChangeSetIdOnServer(maxChangeSetId); - } - - } - - /** - * Index ACL Change Set transaction after ACLs has been indexed by the worker - */ - private void indexAclChangeSetAfterWorker(Collection changeSetsIndexed, TrackerState state) - throws IOException - { - for (AclChangeSet set : changeSetsIndexed) - { - infoSrv.indexAclTransaction(set, true); - // Acl change sets are ordered by commit time and tie-broken by id - if (set.getCommitTimeMs() > state.getLastIndexedChangeSetCommitTime() - || set.getCommitTimeMs() == state.getLastIndexedChangeSetCommitTime() - && set.getId() > state.getLastIndexedChangeSetId()) - { - state.setLastIndexedChangeSetCommitTime(set.getCommitTimeMs()); - state.setLastIndexedChangeSetId(set.getId()); - } - trackerStats.addChangeSetAcls(set.getAclCount()); - } - } - - - /** - * Index ACLs from ACL Change Sets contained in changeSetBatch - * When total ACL indexed count is greater than the specified for a single execution - * (maxAclsPerExecution), no more ACL Change Sets are processed. - * - * @param changeSetBatch List of ACL Change Sets to be indexed - * @return List of ACL Change Set indexed and Count of ACL indexed - */ - private int indexBatchOfChangeSets(List changeSetBatch) - throws AuthenticationException, IOException, JSONException, ExecutionException, InterruptedException { - // Exclude ACL Change Set with no ACLs inside - List nonEmptyChangeSets = changeSetBatch.stream() - .filter(set -> set.getAclCount() > 0) - .collect(Collectors.toList()); - - List acls = client.getAcls(nonEmptyChangeSets, null, Integer.MAX_VALUE); - - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug("{}-[CORE {}] Found {} Acls from Acl Change Sets: {}", Thread.currentThread().getId(), - coreName, acls.size(), nonEmptyChangeSets); - } - - List> aclBatches = Lists.partition(acls, aclBatchSize); - - return forkJoinPool.submit(() -> - aclBatches.parallelStream().map(batch -> { - new AclIndexWorker(batch).run(); - return batch.size(); - }).reduce(0, Integer::sum) - ).get(); - } - - - /** - * ACL Indexer - */ - class AclIndexWorker extends AbstractWorker - { - List acls; - - AclIndexWorker(List acls) - { - this.acls = acls; - } - - @Override - protected void doWork() throws IOException, AuthenticationException, JSONException - { - List filteredAcls = filterAcls(acls); - if(filteredAcls.size() > 0) - { - List readers = client.getAclReaders(filteredAcls); - indexAcl(readers, true); - } - } - - @Override - protected void onFail(Throwable failCausedBy) - { - setRollback(true, failCausedBy); - } - - private List filterAcls(List acls) - { - ArrayList filteredList = new ArrayList<>(acls.size()); - for(Acl acl : acls) - { - if(docRouter.routeAcl(shardCount, shardInstance, acl)) - { - filteredList.add(acl); - } - } - return filteredList; - } - } - - - public void invalidateState() - { - super.invalidateState(); - infoSrv.clearProcessedAclChangeSets(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import com.google.common.collect.Lists; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.repo.index.shard.ShardMethodEnum; +import org.alfresco.solr.AclReport; +import org.alfresco.solr.BoundedDeque; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.TrackerState; +import org.alfresco.solr.adapters.IOpenBitSet; +import org.alfresco.solr.client.Acl; +import org.alfresco.solr.client.AclChangeSet; +import org.alfresco.solr.client.AclChangeSets; +import org.alfresco.solr.client.AclReaders; +import org.alfresco.solr.client.SOLRAPIClient; +import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Multithreaded ACL {@link Tracker} implementation. + * + * @author Matt Ward + **/ + +public class AclTracker extends ActivatableTracker +{ + protected final static Logger LOGGER = LoggerFactory.getLogger(AclTracker.class); + + private static final int ACL_CHANGE_SETS_FOUND_QUEUE_SIZE = 100; + private static final int DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE = 2000; + private static final int DEFAULT_ACL_BATCH_SIZE = 100; + private static final int DEFAULT_ACL_TRACKER_MAX_PARALLELISM = 32; + private static final long DEFAULT_ACL_TRACKER_TIMESTEP = TIME_STEP_1_HR_IN_MS; + + private static final long INITIAL_MAX_ACL_CHANGE_SET_ID = 2000L; + private static final int MAX_NUMBER_OF_ACL_CHANGE_SETS = 2000; + + private static final long MAX_TIME_STEP = TIME_STEP_32_DAYS_IN_MS; + + // Repository Remote API doesn't accept more than 512 aclChangeSetIds by invocation + private static final int MAX_ACL_CHANGE_SET_BATCH_SIZE = 512; + + private int aclTrackerParallelism; + + private int changeSetAclsBatchSize; + private int aclBatchSize; + private long timeStep; + private int maxNumberOfAclChangeSets; + + private ConcurrentLinkedQueue aclChangeSetsToReindex = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclChangeSetsToIndex = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclChangeSetsToPurge = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclsToReindex = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclsToIndex = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclsToPurge = new ConcurrentLinkedQueue<>(); + private DocRouter docRouter; + + private ForkJoinPool forkJoinPool; + + // Share run and write locks across all AclTracker threads + private static Map RUN_LOCK_BY_CORE = new ConcurrentHashMap<>(); + private static Map WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>(); + @Override + public Semaphore getWriteLock() + { + return WRITE_LOCK_BY_CORE.get(coreName); + } + @Override + public Semaphore getRunLock() + { + return RUN_LOCK_BY_CORE.get(coreName); + } + + + /** + * Default constructor, for testing. + */ + AclTracker() + { + super(Tracker.Type.ACL); + } + + public AclTracker(Properties p, SOLRAPIClient client, + String coreName, InformationServer informationServer) + { + super(p, client, coreName, informationServer, Tracker.Type.ACL); + changeSetAclsBatchSize = Integer.parseInt(p.getProperty("alfresco.changeSetAclsBatchSize", + String.valueOf(DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE))); + if (changeSetAclsBatchSize > MAX_ACL_CHANGE_SET_BATCH_SIZE) + { + LOGGER.warn("Max value for 'alfresco.changeSetAclsBatchSize' is 512. " + + "This value is being taken instead of the one specified in 'solrcore.properties': " + changeSetAclsBatchSize); + changeSetAclsBatchSize = MAX_ACL_CHANGE_SET_BATCH_SIZE; + } + aclBatchSize = Integer.parseInt(p.getProperty("alfresco.aclBatchSize", + String.valueOf(DEFAULT_ACL_BATCH_SIZE))); + docRouter = DocRouterFactory.getRouter(p, shardMethod); + + aclTrackerParallelism = Integer.parseInt(p.getProperty("alfresco.acl.tracker.maxParallelism", + String.valueOf(DEFAULT_ACL_TRACKER_MAX_PARALLELISM))); + forkJoinPool = new ForkJoinPool(aclTrackerParallelism); + + timeStep = Long.parseLong(p.getProperty("alfresco.acl.tracker.timestep", + String.valueOf(DEFAULT_ACL_TRACKER_TIMESTEP))); + + maxNumberOfAclChangeSets = Integer.parseInt(p.getProperty("alfresco.acl.tracker.maxNumberOfAclChangeSets", + String.valueOf(MAX_NUMBER_OF_ACL_CHANGE_SETS))); + + RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); + WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); + } + + @Override + protected void doTrack(String iterationId) throws Throwable + { + trackRepository(); + } + + public void maintenance() throws Exception + { + purgeAclChangeSets(); + purgeAcls(); + reindexAclChangeSets(); + reindexAcls(); + indexAclChangeSets(); + indexAcls(); + } + + + public boolean hasMaintenance() + { + return aclChangeSetsToReindex.size() > 0 || + aclChangeSetsToIndex.size() > 0 || + aclChangeSetsToPurge.size() > 0 || + aclsToReindex.size() > 0 || + aclsToIndex.size() > 0 || + aclsToPurge.size() > 0; + } + + protected void indexAclChangeSets() throws AuthenticationException, IOException, JSONException + { + boolean requiresCommit = false; + while (aclChangeSetsToIndex.peek() != null) + { + Long aclChangeSetId = aclChangeSetsToIndex.poll(); + if (aclChangeSetId != null) + { + AclChangeSets aclChangeSets = client.getAclChangeSets(null, aclChangeSetId, + null, aclChangeSetId+1, 1); + if ((aclChangeSets.getAclChangeSets().size() > 0) && + aclChangeSetId.equals(aclChangeSets.getAclChangeSets().get(0).getId())) + { + AclChangeSet changeSet = aclChangeSets.getAclChangeSets().get(0); + List acls = client.getAcls(Collections.singletonList(changeSet), null, Integer.MAX_VALUE); + for (Acl acl : acls) + { + List readers = client.getAclReaders(Collections.singletonList(acl)); + indexAcl(readers, false); + } + this.infoSrv.indexAclTransaction(changeSet, false); + LOGGER.info("[CORE {}] - INDEX ACTION - AclChangeSetId {} has been indexed", coreName, aclChangeSetId); + requiresCommit = true; + } + else + { + LOGGER.info("[CORE {}] - INDEX ACTION - AclChangeSetId {} was not found in database, " + + "it has NOT been reindexed", coreName, aclChangeSetId); + } + } + checkShutdown(); + } + if(requiresCommit) + { + checkShutdown(); + //this.infoSrv.commit(); + } + } + + protected void indexAcls() throws AuthenticationException, IOException, JSONException + { + while (aclsToIndex.peek() != null) + { + Long aclId = aclsToIndex.poll(); + if (aclId != null) + { + //System.out.println("############## Indexing ACL ID:"+aclId); + Acl acl = new Acl(0, aclId); + List readers = client.getAclReaders(Collections.singletonList(acl)); + //AclReaders r = readers.get(0); + //System.out.println("############## READERS ID:"+r.getId()+":"+r.getReaders()); + indexAcl(readers, false); + LOGGER.info("[CORE {}] - INDEX ACTION - AclId {} has been indexed", coreName, aclId); + } + checkShutdown(); + } + } + + protected void reindexAclChangeSets() throws AuthenticationException, IOException, JSONException + { + boolean requiresCommit = false; + while (aclChangeSetsToReindex.peek() != null) + { + Long aclChangeSetId = aclChangeSetsToReindex.poll(); + if (aclChangeSetId != null) + { + this.infoSrv.deleteByAclChangeSetId(aclChangeSetId); + + AclChangeSets aclChangeSets = client.getAclChangeSets(null, aclChangeSetId, + null, aclChangeSetId+1, 1); + if ((aclChangeSets.getAclChangeSets().size() > 0) && + aclChangeSetId.equals(aclChangeSets.getAclChangeSets().get(0).getId())) + { + AclChangeSet changeSet = aclChangeSets.getAclChangeSets().get(0); + List acls = client.getAcls(Collections.singletonList(changeSet), + null, Integer.MAX_VALUE); + for (Acl acl : acls) + { + List readers = client.getAclReaders(Collections.singletonList(acl)); + indexAcl(readers, true); + } + + this.infoSrv.indexAclTransaction(changeSet, true); + LOGGER.info("[CORE {}] - REINDEX ACTION - AclChangeSetId {} has been reindexed", + coreName, aclChangeSetId); + requiresCommit = true; + } + else + { + LOGGER.info("[CORE {}] - REINDEX ACTION - AclChangeSetId {} was not found in database, " + + "it has NOT been reindexed", coreName, aclChangeSetId); + } + } + checkShutdown(); + } + if(requiresCommit) + { + checkShutdown(); + //this.infoSrv.commit(); + } + } + + protected void reindexAcls() throws AuthenticationException, IOException, JSONException + { + boolean requiresCommit = false; + while (aclsToReindex.peek() != null) + { + Long aclId = aclsToReindex.poll(); + if (aclId != null) + { + this.infoSrv.deleteByAclId(aclId); + + Acl acl = new Acl(0, aclId); + List readers = client.getAclReaders(Collections.singletonList(acl)); + indexAcl(readers, true); + LOGGER.info("[CORE {}] - REINDEX ACTION - aclId {} has been reindexed", coreName, aclId); + requiresCommit = true; + } + checkShutdown(); + } + if(requiresCommit) + { + checkShutdown(); + //this.infoSrv.commit(); + } + } + + protected void purgeAclChangeSets() throws IOException, JSONException + { + while (aclChangeSetsToPurge.peek() != null) + { + Long aclChangeSetId = aclChangeSetsToPurge.poll(); + if (aclChangeSetId != null) + { + this.infoSrv.deleteByAclChangeSetId(aclChangeSetId); + LOGGER.info("[CORE {}] - PURGE ACTION - Purged aclChangeSetId {}", coreName, aclChangeSetId); + } + checkShutdown(); + } + } + + protected void purgeAcls() throws IOException, JSONException + { + while (aclsToPurge.peek() != null) + { + Long aclId = aclsToPurge.poll(); + if (aclId != null) + { + this.infoSrv.deleteByAclId(aclId); + LOGGER.info("[CORE {}] - PURGE ACTION - Purged aclId {}", coreName, aclId); + } + checkShutdown(); + } + } + + + // ACL change sets + + public void addAclChangeSetToReindex(Long aclChangeSetToReindex) + { + aclChangeSetsToReindex.offer(aclChangeSetToReindex); + } + + public void addAclChangeSetToIndex(Long aclChangeSetToIndex) + { + aclChangeSetsToIndex.offer(aclChangeSetToIndex); + } + + public void addAclChangeSetToPurge(Long aclChangeSetToPurge) + { + aclChangeSetsToPurge.offer(aclChangeSetToPurge); + } + + // ACLs + + public void addAclToReindex(Long aclToReindex) + { + aclsToReindex.offer(aclToReindex); + } + + public void addAclToIndex(Long aclToIndex) + { + aclsToIndex.offer(aclToIndex); + } + + public void addAclToPurge(Long aclToPurge) + { + aclsToPurge.offer(aclToPurge); + } + + @Override + protected void clearScheduledMaintenanceWork() + { + logAndClear(aclChangeSetsToIndex, "ACL ChangeSets to be indexed"); + logAndClear(aclsToIndex, "ACLs to be indexed"); + + logAndClear(aclChangeSetsToReindex, "ACL ChangeSets to be re-indexed"); + logAndClear(aclsToReindex, "ACLs to be re-indexed"); + + logAndClear(aclChangeSetsToPurge, "ACL ChangeSets to be purged"); + logAndClear(aclsToPurge, "ACLs to be purged"); + } + + protected void trackRepository() throws IOException, AuthenticationException, JSONException + { + checkShutdown(); + + TrackerState state = super.getTrackerState(); + + // Check we are tracking the correct repository + if(state.getTrackerCycles() == 0) + { + //We have a new tracker state so do the checks. + checkRepoAndIndexConsistency(state); + } + + checkShutdown(); + trackAclChangeSets(); + } + + /** + * Checks the first and last TX time + */ + private void checkRepoAndIndexConsistency(TrackerState state) throws AuthenticationException, IOException, JSONException + { + AclChangeSets firstChangeSets = null; + if (state.getLastGoodChangeSetCommitTimeInIndex() == 0) + { + state.setCheckedLastAclTransactionTime(true); + state.setCheckedFirstAclTransactionTime(true); + LOGGER.info("[CORE {}] - No acl transactions found - no verification required", coreName); + + firstChangeSets = client.getAclChangeSets(null, 0L, + null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); + if (!firstChangeSets.getAclChangeSets().isEmpty()) + { + AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0); + long firstChangeSetCommitTime = firstChangeSet.getCommitTimeMs(); + state.setLastGoodChangeSetCommitTimeInIndex(firstChangeSetCommitTime); + setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state); + } + } + + if (!state.isCheckedFirstAclTransactionTime()) + { + firstChangeSets = client.getAclChangeSets(null, 0L, + null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); + if (!firstChangeSets.getAclChangeSets().isEmpty()) + { + AclChangeSet firstAclChangeSet= firstChangeSets.getAclChangeSets().get(0); + long firstAclTxId = firstAclChangeSet.getId(); + long firstAclTxCommitTime = firstAclChangeSet.getCommitTimeMs(); + int setSize = this.infoSrv.getAclTxDocsSize(Long.toString(firstAclTxId), + Long.toString(firstAclTxCommitTime)); + + if (setSize == 0) + { + LOGGER.error("[CORE {}] First acl transaction was not found with the correct timestamp.", coreName); + LOGGER.error("SOLR has successfully connected to your repository " + + "however the SOLR indexes and repository database do not match."); + LOGGER.error("If this is a new or rebuilt database your SOLR indexes " + + "also need to be re-built to match the database."); + LOGGER.error("You can also check your SOLR connection details in solrcore.properties."); + throw new AlfrescoRuntimeException("Initial acl transaction not found with correct timestamp"); + } + else if (setSize == 1) + { + state.setCheckedFirstTransactionTime(true); + LOGGER.info("[CORE {}] Verified first acl transaction and timestamp in index", coreName); + } + else + { + LOGGER.warn("[CORE {}] Duplicate initial acl transaction found with correct timestamp", coreName); + } + } + } + + // Checks that the last aclTxId in solr is <= last aclTxId in repo + if (!state.isCheckedLastAclTransactionTime()) + { + if (firstChangeSets == null) + { + firstChangeSets = client.getAclChangeSets(null, 0L, + null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); + } + + setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state); + Long maxChangeSetCommitTimeInRepo = firstChangeSets.getMaxChangeSetCommitTime(); + Long maxChangeSetIdInRepo = firstChangeSets.getMaxChangeSetId(); + if (maxChangeSetCommitTimeInRepo != null && maxChangeSetIdInRepo != null) + { + AclChangeSet maxAclTxInIndex = this.infoSrv.getMaxAclChangeSetIdAndCommitTimeInIndex(); + if (maxAclTxInIndex.getCommitTimeMs() > maxChangeSetCommitTimeInRepo) + { + LOGGER.error("[CORE {}] Last acl transaction was found in index with " + + "timestamp later than that of repository.", coreName); + LOGGER.error("Max Acl Tx In Index: " + maxAclTxInIndex.getId() + ", In Repo: " + maxChangeSetIdInRepo); + LOGGER.error("Max Acl Tx Commit Time In Index: " + maxAclTxInIndex.getCommitTimeMs() + ", In Repo: " + + maxChangeSetCommitTimeInRepo); + LOGGER.error("SOLR has successfully connected to your repository" + + " however the SOLR indexes and repository database do not match."); + LOGGER.error("If this is a new or rebuilt database your SOLR indexes " + + "also need to be re-built to match the database."); + LOGGER.error("You can also check your SOLR connection details in solrcore.properties."); + throw new AlfrescoRuntimeException("Last acl transaction found in index with incorrect timestamp"); + } + else + { + state.setCheckedLastAclTransactionTime(true); + LOGGER.info("[CORE {}] - Verified last acl transaction timestamp in index less" + + " than or equal to that of repository.", coreName); + } + } + } + } + + /** + * @param changeSetsFound BoundedDeque + * @param lastGoodChangeSetCommitTimeInIndex long + * @return Long + */ + protected Long getChangeSetFromCommitTime(BoundedDeque changeSetsFound, long lastGoodChangeSetCommitTimeInIndex) + { + if(changeSetsFound.size() > 0) + { + return changeSetsFound.getLast().getCommitTimeMs(); + } + else + { + return lastGoodChangeSetCommitTimeInIndex; + } + } + + protected AclChangeSets getSomeAclChangeSets(BoundedDeque changeSetsFound, Long fromCommitTime, + long timeStep, int maxResults, long endTime) + throws AuthenticationException, IOException, JSONException + { + long actualTimeStep = timeStep; + + AclChangeSets aclChangeSets; + // step forward in time until we find something or hit the time bound + // max id unbounded + Long startTime = fromCommitTime == null ? Long.valueOf(0L) : fromCommitTime; + do + { + aclChangeSets = client.getAclChangeSets(startTime, null, + startTime + actualTimeStep, null, maxResults); + startTime += actualTimeStep; + actualTimeStep *= 2; + if(actualTimeStep > MAX_TIME_STEP) + { + actualTimeStep = MAX_TIME_STEP; + } + } + while( ((aclChangeSets.getAclChangeSets().size() == 0) && (startTime < endTime)) || + ((aclChangeSets.getAclChangeSets().size() > 0) && alreadyFoundChangeSets(changeSetsFound, aclChangeSets))); + + return aclChangeSets; + + } + + private boolean alreadyFoundChangeSets(BoundedDeque changeSetsFound, AclChangeSets aclChangeSets) + { + if(changeSetsFound.size() == 0) + { + return false; + } + + if(aclChangeSets.getAclChangeSets().size() == 1) + { + return aclChangeSets.getAclChangeSets().get(0).getId() == changeSetsFound.getLast().getId(); + } + else + { + HashSet alreadyFound = new HashSet<>(changeSetsFound.getDeque()); + for(AclChangeSet aclChangeSet : aclChangeSets.getAclChangeSets()) + { + if(!alreadyFound.contains(aclChangeSet)) + { + return false; + } + } + return true; + } + } + + /** + * @param aclReaderList List + * @param overwrite boolean + */ + protected void indexAcl(List aclReaderList, boolean overwrite) throws IOException + { + long time = this.infoSrv.indexAcl(aclReaderList, overwrite); + trackerStats.addAclTime(time); + } + + public IndexHealthReport checkIndex(Long toAclTx, Long fromTime, Long toTime) + throws AuthenticationException, IOException, JSONException + { + // DB ACL TX Count + long firstChangeSetCommitTimex = 0; + AclChangeSets firstChangeSets = client.getAclChangeSets(null, 0L, + null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); + if(firstChangeSets.getAclChangeSets().size() > 0) + { + AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0); + firstChangeSetCommitTimex = firstChangeSet.getCommitTimeMs(); + } + + IOpenBitSet aclTxIdsInDb = infoSrv.getOpenBitSetInstance(); + long lastAclTxCommitTime = firstChangeSetCommitTimex; + if (fromTime != null) + { + lastAclTxCommitTime = fromTime; + } + + long maxAclTxId = 0; + Long minAclTxId = null; + long endTime = System.currentTimeMillis() + infoSrv.getHoleRetention(); + AclChangeSets aclTransactions; + BoundedDeque changeSetsFound = new BoundedDeque<>(ACL_CHANGE_SETS_FOUND_QUEUE_SIZE); + DO: do + { + aclTransactions = getSomeAclChangeSets(changeSetsFound, + lastAclTxCommitTime, timeStep, maxNumberOfAclChangeSets, endTime); + for (AclChangeSet set : aclTransactions.getAclChangeSets()) + { + // include + if (toTime != null) + { + if (set.getCommitTimeMs() > toTime) + { + break DO; + } + } + if (toAclTx != null) + { + if (set.getId() > toAclTx) + { + break DO; + } + } + + // bounds for later loops + if (minAclTxId == null) + { + minAclTxId = set.getId(); + } + if (maxAclTxId < set.getId()) + { + maxAclTxId = set.getId(); + } + + lastAclTxCommitTime = set.getCommitTimeMs(); + aclTxIdsInDb.set(set.getId()); + changeSetsFound.add(set); + } + } + while (aclTransactions.getAclChangeSets().size() > 0); + + return this.infoSrv.reportAclTransactionsInIndex(minAclTxId, aclTxIdsInDb, maxAclTxId); + } + + public List getAclsForDbAclTransaction(Long acltxid) + { + try + { + ArrayList answer = new ArrayList<>(); + AclChangeSets changeSet = client.getAclChangeSets(null, acltxid, null, + acltxid+1, 1); + List acls = client.getAcls(changeSet.getAclChangeSets(), null, Integer.MAX_VALUE); + for (Acl acl : acls) + { + answer.add(acl.getId()); + } + return answer; + } + catch (IOException | JSONException | AuthenticationException e) + { + throw new AlfrescoRuntimeException("Failed to get acls", e); + } + } + + public AclReport checkAcl(Long aclid) + { + AclReport aclReport = new AclReport(); + aclReport.setAclId(aclid); + + // In DB + + try + { + List readers = client.getAclReaders(Collections.singletonList(new Acl(0, aclid))); + aclReport.setExistsInDb(readers.size() == 1); + } + catch (IOException | JSONException | AuthenticationException e) + { + aclReport.setExistsInDb(false); + } + + // In Index + return this.infoSrv.checkAclInIndex(aclid, aclReport); + } + + /** + * Every ACL Change Set contains a list of ACLs to be indexed. + * This method gets ACL Change Sets from Alfresco Repository to be indexed. + * + * The indexing is performed in batches of ACL Change Sets and the Tracker Status + * is updated in batched of ACLs. + * + * Tracker Status contains the Commit Time from the latest ACL Change Set indexed, + * so new operations can be retrieved from Repository starting with that time. + */ + protected void trackAclChangeSets() throws AuthenticationException, IOException, JSONException + { + + long startElapsed = System.nanoTime(); + + AclChangeSets aclChangeSets; + BoundedDeque changeSetsFound = new BoundedDeque<>(ACL_CHANGE_SETS_FOUND_QUEUE_SIZE); + long totalAclCount = 0; + int aclCount; + + LOGGER.info("{}-[CORE {}] Tracking ACLs", Thread.currentThread().getId(), coreName); + + do + { + try + { + getWriteLock().acquire(); + + /* + * We acquire the tracker state again here and set it globally. This is because the + * tracker state could have been invalidated due to a rollback by the CommitTracker. + * In this case the state will revert to the last transaction state record in the index. + */ + + this.state = getTrackerState(); + + Long fromCommitTime = getChangeSetFromCommitTime(changeSetsFound, + state.getLastChangeSetCommitTimeOnServer() == 0 ? state.getLastGoodChangeSetCommitTimeInIndex() + : state.getLastChangeSetCommitTimeOnServer()); + aclChangeSets = getSomeAclChangeSets(changeSetsFound, fromCommitTime, timeStep, maxNumberOfAclChangeSets, + state.getTimeToStopIndexing()); + + if (aclChangeSets.getAclChangeSets().size() > 0) + { + LOGGER.info("{}-[CORE {}] Found {} ACL change sets after lastTxCommitTime {}, ACL Change Sets from {} to {}", + Thread.currentThread().getId(), + coreName, + aclChangeSets.getAclChangeSets().size(), + fromCommitTime, + aclChangeSets.getAclChangeSets().get(0), + aclChangeSets.getAclChangeSets().get(aclChangeSets.getAclChangeSets().size() - 1)); + } + else + { + LOGGER.info("{}-[CORE {}] No ACL change set found after lastTxCommitTime {}", + Thread.currentThread().getId(), coreName, fromCommitTime); + } + + + // Make sure we do not go ahead of where we started - we will check the holes here + // correctly next time + if (aclChangeSets.getAclChangeSets() + .stream() + .anyMatch(changeSet -> changeSet.getCommitTimeMs() > state.getTimeToStopIndexing())) + { + break; + } + + final AtomicInteger counter = new AtomicInteger(); + Collection> changeSetBatches = aclChangeSets.getAclChangeSets().stream() + .peek(changeSetsFound::add) + .filter(this::isAclChangeSetAlreadyIndexed) + .collect(Collectors.groupingBy(it -> counter.getAndAdd(1) / changeSetAclsBatchSize)) + .values(); + + + for (List changeSetBatch : changeSetBatches) + { + aclCount = indexBatchOfChangeSets(changeSetBatch); + // Update last committed transactions + setLastChangeSetIdAndCommitTimeInTrackerState(changeSetBatch, state); + indexAclChangeSetAfterWorker(changeSetBatch, state); + + long endElapsed = System.nanoTime(); + trackerStats.addElapsedAclTime(aclCount, endElapsed-startElapsed); + startElapsed = endElapsed; + totalAclCount += aclCount; + } + + } + catch(InterruptedException | ExecutionException e) + { + throw new IOException(e); + } + finally + { + getWriteLock().release(); + } + + } + while ((aclChangeSets.getAclChangeSets().size() > 0)); + + LOGGER.info("{}-[CORE {}] Tracked {} ACLs", Thread.currentThread().getId(), coreName, totalAclCount); + + } + + private boolean isAclChangeSetAlreadyIndexed(AclChangeSet changeSet) + { + + try + { + boolean isInIndex = (changeSet.getCommitTimeMs() <= state.getLastIndexedChangeSetCommitTime() && + infoSrv.aclChangeSetInIndex(changeSet.getId(), true)); + if (LOGGER.isTraceEnabled()) + { + LOGGER.trace("{}-[CORE {}] Skipping change Set Id {} as it was already indexed", + Thread.currentThread().getId(), coreName, changeSet.getId()); + } + return !isInIndex; + } + catch (IOException e) + { + LOGGER.warn( + "{}-[CORE {}] Error catched while checking if ACL Change Set {} was in index", + Thread.currentThread().getId(), coreName, changeSet.getId(), e); + return true; + } + + } + + private void setLastChangeSetIdAndCommitTimeInTrackerState(List aclChangeSets, TrackerState state) + { + + if (!aclChangeSets.isEmpty()) + { + long maxChangeSetCommitTime = + aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getCommitTimeMs)).orElseThrow().getCommitTimeMs(); + state.setLastChangeSetCommitTimeOnServer(maxChangeSetCommitTime); + + long maxChangeSetId = aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getId)).orElseThrow().getId(); + state.setLastChangeSetIdOnServer(maxChangeSetId); + } + + } + + /** + * Index ACL Change Set transaction after ACLs has been indexed by the worker + */ + private void indexAclChangeSetAfterWorker(Collection changeSetsIndexed, TrackerState state) + throws IOException + { + for (AclChangeSet set : changeSetsIndexed) + { + infoSrv.indexAclTransaction(set, true); + // Acl change sets are ordered by commit time and tie-broken by id + if (set.getCommitTimeMs() > state.getLastIndexedChangeSetCommitTime() + || set.getCommitTimeMs() == state.getLastIndexedChangeSetCommitTime() + && set.getId() > state.getLastIndexedChangeSetId()) + { + state.setLastIndexedChangeSetCommitTime(set.getCommitTimeMs()); + state.setLastIndexedChangeSetId(set.getId()); + } + trackerStats.addChangeSetAcls(set.getAclCount()); + } + } + + + /** + * Index ACLs from ACL Change Sets contained in changeSetBatch + * When total ACL indexed count is greater than the specified for a single execution + * (maxAclsPerExecution), no more ACL Change Sets are processed. + * + * @param changeSetBatch List of ACL Change Sets to be indexed + * @return List of ACL Change Set indexed and Count of ACL indexed + */ + private int indexBatchOfChangeSets(List changeSetBatch) + throws AuthenticationException, IOException, JSONException, ExecutionException, InterruptedException { + // Exclude ACL Change Set with no ACLs inside + List nonEmptyChangeSets = changeSetBatch.stream() + .filter(set -> set.getAclCount() > 0) + .collect(Collectors.toList()); + + List acls = client.getAcls(nonEmptyChangeSets, null, Integer.MAX_VALUE); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("{}-[CORE {}] Found {} Acls from Acl Change Sets: {}", Thread.currentThread().getId(), + coreName, acls.size(), nonEmptyChangeSets); + } + + List> aclBatches = Lists.partition(acls, aclBatchSize); + + return forkJoinPool.submit(() -> + aclBatches.parallelStream().map(batch -> { + new AclIndexWorker(batch).run(); + return batch.size(); + }).reduce(0, Integer::sum) + ).get(); + } + + + /** + * ACL Indexer + */ + class AclIndexWorker extends AbstractWorker + { + List acls; + + AclIndexWorker(List acls) + { + this.acls = acls; + } + + @Override + protected void doWork() throws IOException, AuthenticationException, JSONException + { + List filteredAcls = filterAcls(acls); + if(filteredAcls.size() > 0) + { + List readers = client.getAclReaders(filteredAcls); + indexAcl(readers, true); + } + } + + @Override + protected void onFail(Throwable failCausedBy) + { + setRollback(true, failCausedBy); + } + + private List filterAcls(List acls) + { + ArrayList filteredList = new ArrayList<>(acls.size()); + for(Acl acl : acls) + { + if(docRouter.routeAcl(shardCount, shardInstance, acl)) + { + filteredList.add(acl); + } + } + return filteredList; + } + } + + + public void invalidateState() + { + super.invalidateState(); + infoSrv.clearProcessedAclChangeSets(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ActivatableTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ActivatableTracker.java index dad53b3fc..63e4b8e90 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ActivatableTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ActivatableTracker.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 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% diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CascadeTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CascadeTracker.java index df0eda7af..e8d78bee2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CascadeTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CascadeTracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CommitTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CommitTracker.java index b98995ddb..57b9530e7 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CommitTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CommitTracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ComposableDocRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ComposableDocRouter.java index ee57db5a3..ca7e7bff1 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ComposableDocRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ComposableDocRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ContentTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ContentTracker.java index d6f2de2df..a5c870072 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ContentTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ContentTracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRangeRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRangeRouter.java index ecb22d115..90f6aa7ed 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRangeRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRangeRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRouter.java index 120b440bf..a1197d41b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -68,4 +68,4 @@ public class DBIDRouter implements DocRouter String dbid = Long.toString(node.getId()); return (Math.abs(Hash.murmurhash3_x86_32(dbid, 0, dbid.length(), 77)) % shardCount) == shardInstance; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateMonthRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateMonthRouter.java index 2219c83d8..f88d7b6c7 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateMonthRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateMonthRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateQuarterRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateQuarterRouter.java index 6b488d180..a1f9feb0b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateQuarterRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateQuarterRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -72,4 +72,4 @@ public class DateQuarterRouter implements DocRouter return ceilGroupInstance % numShards == shardInstance; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouter.java index 257b99da0..074431bd4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterFactory.java index f99990b3b..fef90dff1 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterWithFallback.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterWithFallback.java index 691294979..a05131a20 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterWithFallback.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterWithFallback.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouter.java index 4d46d00d1..c6c4d192e 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouter.java index 55e95d13d..8d5f85088 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -87,4 +87,4 @@ public class ExplicitShardIdWithStaticPropertyRouter extends ComposableDocRouter return explicitShardId.equals(shardInstance); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java index e65eb7f41..547667d2f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java index 4495305a0..b0c19b9c4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java @@ -1,580 +1,580 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker; - -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Semaphore; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.M2Namespace; -import org.alfresco.service.cmr.dictionary.ModelDefinition.XMLBindingType; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.client.AlfrescoModel; -import org.alfresco.solr.client.AlfrescoModelDiff; -import org.alfresco.solr.client.SOLRAPIClient; -import org.alfresco.solr.config.ConfigUtil; -import org.apache.solr.core.SolrResourceLoader; -import org.json.JSONException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @startuml - * title ModelTracker - * participant Scheduler - * participant ModelTracker - * participant SOLRAPIClient - * == Initialisation == - * [-> ModelTracker: <> - * activate ModelTracker - * ModelTracker -> ModelTracker : locate create persisted models - * ModelTracker -> ModelTracker : load persisted models - * deactivate ModelTracker - * == Tracking == - * Scheduler -> ModelTracker: Run - * activate ModelTracker - * ModelTracker -> SOLRAPIClient: getModelChanges(Models + CRCs) - * activate SOLRAPIClient - * SOLRAPIClient -> ModelTracker: changedModels - * deactivate SOLRAPIClient - * loop changed models - * ModelTracker -> SOLRAPIClient: getModel - * activate SOLRAPIClient - * SOLRAPIClient -> ModelTracker: Model XML - * deactivate SOLRAPIClient - * end - * ModelTracker -> ModelTracker: update local dictionary - * ModelTracker -> ModelTracker: update cached models - * ModelTracker -> ModelTracker: intialise model dependancies - * ModelTracker -> Scheduler - * deactivate ModelTracker - * @enduml - */ -public class ModelTracker extends AbstractTracker -{ - private static final Logger LOGGER = LoggerFactory.getLogger(ModelTracker.class); - - private final Set indexedStores = new HashSet<>(); - private final Set ignoredStores = new HashSet<>(); - private final Set indexedTenants = new HashSet<>(); - private final Set ignoredTenants = new HashSet<>(); - private final Set indexedDataTypes = new HashSet<>(); - private final Set ignoredDataTypes = new HashSet<>(); - private final Set indexedTypes = new HashSet<>(); - private final Set ignoredTypes = new HashSet<>(); - private final Set indexedAspects = new HashSet<>(); - private final Set ignoredAspects = new HashSet<>(); - private final Set indexedFields = new HashSet<>(); - private final Set ignoredFields = new HashSet<>(); - - private ReentrantReadWriteLock modelLock = new ReentrantReadWriteLock(); - private volatile boolean hasModels = false; - private File alfrescoModelDir; - - // Share run and write locks across all ModelTracker threads - private static Map RUN_LOCK_BY_CORE = new ConcurrentHashMap<>(); - private static Map WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>(); - @Override - public Semaphore getWriteLock() - { - return WRITE_LOCK_BY_CORE.get(coreName); - } - @Override - public Semaphore getRunLock() - { - return RUN_LOCK_BY_CORE.get(coreName); - } - - public ModelTracker(String solrHome, Properties p, SOLRAPIClient client, String coreName, - InformationServer informationServer) - { - super(p, client, coreName, informationServer, Tracker.Type.MODEL); - String normalSolrHome = SolrResourceLoader.normalizeDir(solrHome); - alfrescoModelDir = new File(ConfigUtil.locateProperty("solr.model.dir", normalSolrHome+"alfrescoModels")); - LOGGER.info("Alfresco Model dir {}", alfrescoModelDir); - if (!alfrescoModelDir.exists()) - { - alfrescoModelDir.mkdir(); - } - - loadPersistedModels(); - - RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); - WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); - } - - @Override - public boolean hasMaintenance() - { - return false; - } - - @Override - public void maintenance() - { - // Nothing to be done here - } - - /** - * - */ - private void loadPersistedModels() - { - HashMap modelMap = new HashMap<>(); - if (alfrescoModelDir.exists() && alfrescoModelDir.isDirectory()) - { - // A filter for XML files - FileFilter filter = pathname -> pathname.isFile() && pathname.getName().endsWith(".xml"); - // List XML files - File[] files = alfrescoModelDir.listFiles(filter); - if (files != null) - { - for (File file : files) - { - InputStream modelStream = null; - M2Model model; - try - { - modelStream = new FileInputStream(file); - model = M2Model.createModel(modelStream); - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("File not found: " + file, e); - } - finally - { - if (modelStream != null) - { - try { modelStream.close(); } catch (Exception e) {} - } - } - // Model successfully loaded - for (M2Namespace namespace : model.getNamespaces()) - { - modelMap.put(namespace.getUri(), model); - } - } - } - } - // Load the models ensuring that they are loaded in the correct order - HashSet loadedModels = new HashSet(); - for (M2Model model : modelMap.values()) - { - loadModel(modelMap, loadedModels, model); - } - - if(modelMap.size() > 0) - { - AlfrescoSolrDataModel.getInstance().afterInitModels(); - } - } - - /** - * Default constructor, for testing. - */ - ModelTracker() - { - super(Tracker.Type.MODEL); - } - - @Override - protected void doTrack(String iterationId) throws AuthenticationException, IOException, JSONException - { - // Is the InformationServer ready to update - int registeredSearcherCount = this.infoSrv.getRegisteredSearcherCount(); - if (registeredSearcherCount >= getMaxLiveSearchers()) - { - LOGGER.info(".... skipping tracking registered searcher count = {}", registeredSearcherCount); - return; - } - - checkShutdown(); - trackModels(false); - } - - public void trackModels(boolean onlyFirstTime) throws AuthenticationException, IOException, JSONException - { - boolean requiresWriteLock; - modelLock.readLock().lock(); - try - { - if (hasModels) - { - if (onlyFirstTime) - { - return; - } - else - { - requiresWriteLock = false; - } - } - else - { - requiresWriteLock = true; - } - } - finally - { - modelLock.readLock().unlock(); - } - - if (requiresWriteLock) - { - modelLock.writeLock().lock(); - try - { - if (hasModels) - { - if (onlyFirstTime) { return; } - } - - trackModelsImpl(); - if (onlyFirstTime) - { - infoSrv.initSkippingDescendantDocs(); - } - hasModels = true; - } - finally - { - modelLock.writeLock().unlock(); - } - } - else - { - trackModelsImpl(); - } - } - - public void ensureFirstModelSync() - { - try - { - trackModels(true); - } - catch (Throwable t) - { - LOGGER.error("Model tracking failed for core: {}", coreName, t); - } - - } - - /** - * Tracks models. Reflects changes and updates on disk copy - */ - private void trackModelsImpl() throws AuthenticationException, IOException, JSONException - { - long start = System.nanoTime(); - - List modelDiffs = client.getModelsDiff(coreName, this.infoSrv.getAlfrescoModels()); - Map modelMap = new HashMap<>(); - - for (AlfrescoModelDiff modelDiff : modelDiffs) - { - switch (modelDiff.getType()) - { - case CHANGED: - case NEW: - AlfrescoModel model = client.getModel(coreName, modelDiff.getModelName()); - for (M2Namespace namespace : model.getModel().getNamespaces()) - { - modelMap.put(namespace.getUri(), model.getModel()); - } - break; - case REMOVED: - // We now remove models as we see them - MNT-17627 - // Models have to be deleted from disk before we remove them from memory - // We need to know the prefix for the uri to delete them - break; - } - } - - HashSet loadedModels = new HashSet<>(); - for (M2Model model : modelMap.values()) - { - loadModel(modelMap, loadedModels, model); - } - if (loadedModels.size() > 0) - { - this.infoSrv.afterInitModels(); - } - - for (AlfrescoModelDiff modelDiff : modelDiffs) - { - switch (modelDiff.getType()) - { - case CHANGED: - removeMatchingModels(alfrescoModelDir, modelDiff.getModelName()); - M2Model changedModel = this.infoSrv.getM2Model(modelDiff.getModelName()); - File changedFile = new File(alfrescoModelDir, getModelFileName(changedModel)); - FileOutputStream cos = new FileOutputStream(changedFile); - changedModel.toXML(cos); - cos.flush(); - cos.close(); - break; - case NEW: - M2Model newModel = this.infoSrv.getM2Model(modelDiff.getModelName()); - // add on file - File newFile = new File(alfrescoModelDir, getModelFileName(newModel)); - FileOutputStream nos = new FileOutputStream(newFile); - newModel.toXML(nos); - nos.flush(); - nos.close(); - break; - case REMOVED: - // This will remove the model from the dictionary on completion - try - { - removeMatchingModels(alfrescoModelDir, modelDiff.getModelName()); - } - finally - { - AlfrescoSolrDataModel.getInstance().removeModel(modelDiff.getModelName()); - } - break; - } - } - - long end = System.nanoTime(); - - trackerStats.addModelTime(end - start); - - if (runPostModelLoadInit) - { - for (Object key : props.keySet()) - { - String stringKey = (String) key; - if (stringKey.startsWith("alfresco.index.store")) - { - StoreRef store = new StoreRef(props.getProperty(stringKey)); - indexedStores.add(store); - } - if (stringKey.startsWith("alfresco.ignore.store")) - { - StoreRef store = new StoreRef(props.getProperty(stringKey)); - ignoredStores.add(store); - } - if (stringKey.startsWith("alfresco.index.tenant")) - { - indexedTenants.add(props.getProperty(stringKey)); - } - if (stringKey.startsWith("alfresco.ignore.tenant")) - { - ignoredTenants.add(props.getProperty(stringKey)); - } - if (stringKey.startsWith("alfresco.index.datatype")) - { - QName qname = expandQName(props.getProperty(stringKey)); - indexedDataTypes.add(qname); - } - if (stringKey.startsWith("alfresco.ignore.datatype")) - { - QName qname = expandQName(props.getProperty(stringKey)); - ignoredDataTypes.add(qname); - } - if (stringKey.startsWith("alfresco.index.type")) - { - QName qname = expandQName(props.getProperty(stringKey)); - indexedTypes.add(qname); - } - if (stringKey.startsWith("alfresco.ignore.type")) - { - QName qname = expandQName(props.getProperty(stringKey)); - ignoredTypes.add(qname); - } - if (stringKey.startsWith("alfresco.index.aspect")) - { - QName qname = expandQName(props.getProperty(stringKey)); - indexedAspects.add(qname); - } - if (stringKey.startsWith("alfresco.ignore.aspect")) - { - QName qname = expandQName(props.getProperty(stringKey)); - ignoredAspects.add(qname); - } - if (stringKey.startsWith("alfresco.index.field")) - { - String name = expandName(props.getProperty(stringKey)); - indexedFields.add(name); - } - if (stringKey.startsWith("alfresco.ignore.field")) - { - String name = expandName(props.getProperty(stringKey)); - ignoredFields.add(name); - } - } - runPostModelLoadInit = false; - } - } - - private QName expandQName(String qName) - { - String expandedQName = qName; - if (qName.startsWith("@")) - { - return expandQName(qName.substring(1)); - } - else if (qName.startsWith("{")) - { - expandedQName = expandQNameImpl(qName); - } - else if (qName.contains(":")) - { - expandedQName = expandQNameImpl(qName); - } - - return QName.createQName(expandedQName); - - } - - private String expandQNameImpl(String q) - { - String eq = q; - // Check for any prefixes and expand to the full uri - if (q.charAt(0) != '{') - { - int colonPosition = q.indexOf(':'); - if (colonPosition == -1) - { - // use the default namespace - eq = "{" + this.infoSrv.getNamespaceDAO().getNamespaceURI("") + "}" + q; - } - else - { - // find the prefix - eq = "{" + this.infoSrv.getNamespaceDAO().getNamespaceURI(q.substring(0, colonPosition)) + "}" - + q.substring(colonPosition + 1); - } - } - return eq; - } - - private String expandName(String qName) - { - String expandedQName = qName; - if (qName.startsWith("@")) - { - return expandName(qName.substring(1)); - } - else if (qName.startsWith("{")) - { - expandedQName = expandQNameImpl(qName); - } - else if (qName.contains(":")) - { - expandedQName = expandQNameImpl(qName); - } - - return expandedQName; - } - - private void removeMatchingModels(File alfrescoModelDir, QName modelName) - { - final String prefix = modelName.toPrefixString(this.infoSrv.getNamespaceDAO()).replace(":", ".") + "."; - final String postFix = ".xml"; - - File[] toDelete = alfrescoModelDir.listFiles(pathname -> { - if (pathname.isDirectory()) { return false; } - String name = pathname.getName(); - if (!name.endsWith(postFix)) { return false; } - if (!name.startsWith(prefix)) { return false; } - // check is number between - String checksum = name.substring(prefix.length(), name.length() - postFix.length()); - try - { - Long.parseLong(checksum); - return true; - } - catch (NumberFormatException nfe) - { - return false; - } - }); - - if (toDelete != null) - { - for (File file : toDelete) - { - file.delete(); - } - } - } - - private void loadModel(Map modelMap, HashSet loadedModels, M2Model model) - { - String modelName = model.getName(); - if (!loadedModels.contains(modelName)) - { - for (M2Namespace importNamespace : model.getImports()) - { - M2Model importedModel = modelMap.get(importNamespace.getUri()); - if (importedModel != null) - { - - // Ensure that the imported model is loaded first - loadModel(modelMap, loadedModels, importedModel); - } - } - - if (this.infoSrv.putModel(model)) - { - loadedModels.add(modelName); - } - LOGGER.info("Loading model {}", model.getName()); - } - } - - private String getModelFileName(M2Model model) - { - return model.getName().replace(":", ".") + "." + model.getChecksum(XMLBindingType.DEFAULT) + ".xml"; - } - - public boolean hasModels() - { - return this.hasModels; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.M2Namespace; +import org.alfresco.service.cmr.dictionary.ModelDefinition.XMLBindingType; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.client.AlfrescoModel; +import org.alfresco.solr.client.AlfrescoModelDiff; +import org.alfresco.solr.client.SOLRAPIClient; +import org.alfresco.solr.config.ConfigUtil; +import org.apache.solr.core.SolrResourceLoader; +import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @startuml + * title ModelTracker + * participant Scheduler + * participant ModelTracker + * participant SOLRAPIClient + * == Initialisation == + * [-> ModelTracker: <> + * activate ModelTracker + * ModelTracker -> ModelTracker : locate create persisted models + * ModelTracker -> ModelTracker : load persisted models + * deactivate ModelTracker + * == Tracking == + * Scheduler -> ModelTracker: Run + * activate ModelTracker + * ModelTracker -> SOLRAPIClient: getModelChanges(Models + CRCs) + * activate SOLRAPIClient + * SOLRAPIClient -> ModelTracker: changedModels + * deactivate SOLRAPIClient + * loop changed models + * ModelTracker -> SOLRAPIClient: getModel + * activate SOLRAPIClient + * SOLRAPIClient -> ModelTracker: Model XML + * deactivate SOLRAPIClient + * end + * ModelTracker -> ModelTracker: update local dictionary + * ModelTracker -> ModelTracker: update cached models + * ModelTracker -> ModelTracker: intialise model dependancies + * ModelTracker -> Scheduler + * deactivate ModelTracker + * @enduml + */ +public class ModelTracker extends AbstractTracker +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ModelTracker.class); + + private final Set indexedStores = new HashSet<>(); + private final Set ignoredStores = new HashSet<>(); + private final Set indexedTenants = new HashSet<>(); + private final Set ignoredTenants = new HashSet<>(); + private final Set indexedDataTypes = new HashSet<>(); + private final Set ignoredDataTypes = new HashSet<>(); + private final Set indexedTypes = new HashSet<>(); + private final Set ignoredTypes = new HashSet<>(); + private final Set indexedAspects = new HashSet<>(); + private final Set ignoredAspects = new HashSet<>(); + private final Set indexedFields = new HashSet<>(); + private final Set ignoredFields = new HashSet<>(); + + private ReentrantReadWriteLock modelLock = new ReentrantReadWriteLock(); + private volatile boolean hasModels = false; + private File alfrescoModelDir; + + // Share run and write locks across all ModelTracker threads + private static Map RUN_LOCK_BY_CORE = new ConcurrentHashMap<>(); + private static Map WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>(); + @Override + public Semaphore getWriteLock() + { + return WRITE_LOCK_BY_CORE.get(coreName); + } + @Override + public Semaphore getRunLock() + { + return RUN_LOCK_BY_CORE.get(coreName); + } + + public ModelTracker(String solrHome, Properties p, SOLRAPIClient client, String coreName, + InformationServer informationServer) + { + super(p, client, coreName, informationServer, Tracker.Type.MODEL); + String normalSolrHome = SolrResourceLoader.normalizeDir(solrHome); + alfrescoModelDir = new File(ConfigUtil.locateProperty("solr.model.dir", normalSolrHome+"alfrescoModels")); + LOGGER.info("Alfresco Model dir {}", alfrescoModelDir); + if (!alfrescoModelDir.exists()) + { + alfrescoModelDir.mkdir(); + } + + loadPersistedModels(); + + RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); + WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); + } + + @Override + public boolean hasMaintenance() + { + return false; + } + + @Override + public void maintenance() + { + // Nothing to be done here + } + + /** + * + */ + private void loadPersistedModels() + { + HashMap modelMap = new HashMap<>(); + if (alfrescoModelDir.exists() && alfrescoModelDir.isDirectory()) + { + // A filter for XML files + FileFilter filter = pathname -> pathname.isFile() && pathname.getName().endsWith(".xml"); + // List XML files + File[] files = alfrescoModelDir.listFiles(filter); + if (files != null) + { + for (File file : files) + { + InputStream modelStream = null; + M2Model model; + try + { + modelStream = new FileInputStream(file); + model = M2Model.createModel(modelStream); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("File not found: " + file, e); + } + finally + { + if (modelStream != null) + { + try { modelStream.close(); } catch (Exception e) {} + } + } + // Model successfully loaded + for (M2Namespace namespace : model.getNamespaces()) + { + modelMap.put(namespace.getUri(), model); + } + } + } + } + // Load the models ensuring that they are loaded in the correct order + HashSet loadedModels = new HashSet(); + for (M2Model model : modelMap.values()) + { + loadModel(modelMap, loadedModels, model); + } + + if(modelMap.size() > 0) + { + AlfrescoSolrDataModel.getInstance().afterInitModels(); + } + } + + /** + * Default constructor, for testing. + */ + ModelTracker() + { + super(Tracker.Type.MODEL); + } + + @Override + protected void doTrack(String iterationId) throws AuthenticationException, IOException, JSONException + { + // Is the InformationServer ready to update + int registeredSearcherCount = this.infoSrv.getRegisteredSearcherCount(); + if (registeredSearcherCount >= getMaxLiveSearchers()) + { + LOGGER.info(".... skipping tracking registered searcher count = {}", registeredSearcherCount); + return; + } + + checkShutdown(); + trackModels(false); + } + + public void trackModels(boolean onlyFirstTime) throws AuthenticationException, IOException, JSONException + { + boolean requiresWriteLock; + modelLock.readLock().lock(); + try + { + if (hasModels) + { + if (onlyFirstTime) + { + return; + } + else + { + requiresWriteLock = false; + } + } + else + { + requiresWriteLock = true; + } + } + finally + { + modelLock.readLock().unlock(); + } + + if (requiresWriteLock) + { + modelLock.writeLock().lock(); + try + { + if (hasModels) + { + if (onlyFirstTime) { return; } + } + + trackModelsImpl(); + if (onlyFirstTime) + { + infoSrv.initSkippingDescendantDocs(); + } + hasModels = true; + } + finally + { + modelLock.writeLock().unlock(); + } + } + else + { + trackModelsImpl(); + } + } + + public void ensureFirstModelSync() + { + try + { + trackModels(true); + } + catch (Throwable t) + { + LOGGER.error("Model tracking failed for core: {}", coreName, t); + } + + } + + /** + * Tracks models. Reflects changes and updates on disk copy + */ + private void trackModelsImpl() throws AuthenticationException, IOException, JSONException + { + long start = System.nanoTime(); + + List modelDiffs = client.getModelsDiff(coreName, this.infoSrv.getAlfrescoModels()); + Map modelMap = new HashMap<>(); + + for (AlfrescoModelDiff modelDiff : modelDiffs) + { + switch (modelDiff.getType()) + { + case CHANGED: + case NEW: + AlfrescoModel model = client.getModel(coreName, modelDiff.getModelName()); + for (M2Namespace namespace : model.getModel().getNamespaces()) + { + modelMap.put(namespace.getUri(), model.getModel()); + } + break; + case REMOVED: + // We now remove models as we see them - MNT-17627 + // Models have to be deleted from disk before we remove them from memory + // We need to know the prefix for the uri to delete them + break; + } + } + + HashSet loadedModels = new HashSet<>(); + for (M2Model model : modelMap.values()) + { + loadModel(modelMap, loadedModels, model); + } + if (loadedModels.size() > 0) + { + this.infoSrv.afterInitModels(); + } + + for (AlfrescoModelDiff modelDiff : modelDiffs) + { + switch (modelDiff.getType()) + { + case CHANGED: + removeMatchingModels(alfrescoModelDir, modelDiff.getModelName()); + M2Model changedModel = this.infoSrv.getM2Model(modelDiff.getModelName()); + File changedFile = new File(alfrescoModelDir, getModelFileName(changedModel)); + FileOutputStream cos = new FileOutputStream(changedFile); + changedModel.toXML(cos); + cos.flush(); + cos.close(); + break; + case NEW: + M2Model newModel = this.infoSrv.getM2Model(modelDiff.getModelName()); + // add on file + File newFile = new File(alfrescoModelDir, getModelFileName(newModel)); + FileOutputStream nos = new FileOutputStream(newFile); + newModel.toXML(nos); + nos.flush(); + nos.close(); + break; + case REMOVED: + // This will remove the model from the dictionary on completion + try + { + removeMatchingModels(alfrescoModelDir, modelDiff.getModelName()); + } + finally + { + AlfrescoSolrDataModel.getInstance().removeModel(modelDiff.getModelName()); + } + break; + } + } + + long end = System.nanoTime(); + + trackerStats.addModelTime(end - start); + + if (runPostModelLoadInit) + { + for (Object key : props.keySet()) + { + String stringKey = (String) key; + if (stringKey.startsWith("alfresco.index.store")) + { + StoreRef store = new StoreRef(props.getProperty(stringKey)); + indexedStores.add(store); + } + if (stringKey.startsWith("alfresco.ignore.store")) + { + StoreRef store = new StoreRef(props.getProperty(stringKey)); + ignoredStores.add(store); + } + if (stringKey.startsWith("alfresco.index.tenant")) + { + indexedTenants.add(props.getProperty(stringKey)); + } + if (stringKey.startsWith("alfresco.ignore.tenant")) + { + ignoredTenants.add(props.getProperty(stringKey)); + } + if (stringKey.startsWith("alfresco.index.datatype")) + { + QName qname = expandQName(props.getProperty(stringKey)); + indexedDataTypes.add(qname); + } + if (stringKey.startsWith("alfresco.ignore.datatype")) + { + QName qname = expandQName(props.getProperty(stringKey)); + ignoredDataTypes.add(qname); + } + if (stringKey.startsWith("alfresco.index.type")) + { + QName qname = expandQName(props.getProperty(stringKey)); + indexedTypes.add(qname); + } + if (stringKey.startsWith("alfresco.ignore.type")) + { + QName qname = expandQName(props.getProperty(stringKey)); + ignoredTypes.add(qname); + } + if (stringKey.startsWith("alfresco.index.aspect")) + { + QName qname = expandQName(props.getProperty(stringKey)); + indexedAspects.add(qname); + } + if (stringKey.startsWith("alfresco.ignore.aspect")) + { + QName qname = expandQName(props.getProperty(stringKey)); + ignoredAspects.add(qname); + } + if (stringKey.startsWith("alfresco.index.field")) + { + String name = expandName(props.getProperty(stringKey)); + indexedFields.add(name); + } + if (stringKey.startsWith("alfresco.ignore.field")) + { + String name = expandName(props.getProperty(stringKey)); + ignoredFields.add(name); + } + } + runPostModelLoadInit = false; + } + } + + private QName expandQName(String qName) + { + String expandedQName = qName; + if (qName.startsWith("@")) + { + return expandQName(qName.substring(1)); + } + else if (qName.startsWith("{")) + { + expandedQName = expandQNameImpl(qName); + } + else if (qName.contains(":")) + { + expandedQName = expandQNameImpl(qName); + } + + return QName.createQName(expandedQName); + + } + + private String expandQNameImpl(String q) + { + String eq = q; + // Check for any prefixes and expand to the full uri + if (q.charAt(0) != '{') + { + int colonPosition = q.indexOf(':'); + if (colonPosition == -1) + { + // use the default namespace + eq = "{" + this.infoSrv.getNamespaceDAO().getNamespaceURI("") + "}" + q; + } + else + { + // find the prefix + eq = "{" + this.infoSrv.getNamespaceDAO().getNamespaceURI(q.substring(0, colonPosition)) + "}" + + q.substring(colonPosition + 1); + } + } + return eq; + } + + private String expandName(String qName) + { + String expandedQName = qName; + if (qName.startsWith("@")) + { + return expandName(qName.substring(1)); + } + else if (qName.startsWith("{")) + { + expandedQName = expandQNameImpl(qName); + } + else if (qName.contains(":")) + { + expandedQName = expandQNameImpl(qName); + } + + return expandedQName; + } + + private void removeMatchingModels(File alfrescoModelDir, QName modelName) + { + final String prefix = modelName.toPrefixString(this.infoSrv.getNamespaceDAO()).replace(":", ".") + "."; + final String postFix = ".xml"; + + File[] toDelete = alfrescoModelDir.listFiles(pathname -> { + if (pathname.isDirectory()) { return false; } + String name = pathname.getName(); + if (!name.endsWith(postFix)) { return false; } + if (!name.startsWith(prefix)) { return false; } + // check is number between + String checksum = name.substring(prefix.length(), name.length() - postFix.length()); + try + { + Long.parseLong(checksum); + return true; + } + catch (NumberFormatException nfe) + { + return false; + } + }); + + if (toDelete != null) + { + for (File file : toDelete) + { + file.delete(); + } + } + } + + private void loadModel(Map modelMap, HashSet loadedModels, M2Model model) + { + String modelName = model.getName(); + if (!loadedModels.contains(modelName)) + { + for (M2Namespace importNamespace : model.getImports()) + { + M2Model importedModel = modelMap.get(importNamespace.getUri()); + if (importedModel != null) + { + + // Ensure that the imported model is loaded first + loadModel(modelMap, loadedModels, importedModel); + } + } + + if (this.infoSrv.putModel(model)) + { + loadedModels.add(modelName); + } + LOGGER.info("Loading model {}", model.getName()); + } + } + + private String getModelFileName(M2Model model) + { + return model.getName().replace(":", ".") + "." + model.getChecksum(XMLBindingType.DEFAULT) + ".xml"; + } + + public boolean hasModels() + { + return this.hasModels; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/PropertyRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/PropertyRouter.java index 412c1c823..2d453b4a3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/PropertyRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/PropertyRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ShardStatePublisher.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ShardStatePublisher.java index 8f2a81739..4196f48ae 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ShardStatePublisher.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ShardStatePublisher.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/SolrTrackerScheduler.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/SolrTrackerScheduler.java index 900519be4..de75808ad 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/SolrTrackerScheduler.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/SolrTrackerScheduler.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/Tracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/Tracker.java index 2497a2e33..b49f04fda 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/Tracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/Tracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerJob.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerJob.java index 5d822767c..d323fb6e6 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerJob.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerJob.java @@ -1,74 +1,74 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker; - -import org.quartz.DisallowConcurrentExecution; -import org.quartz.Job; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Generic Solr tracker job, allowing Quartz to initiate an index update from - * a {@link Tracker} regardless of specific implementation. - * - * Concurrent execution is disallowed, as no parallel work can be done when - * indexing contents from the repository. - * - * @author Matt Ward - */ -@DisallowConcurrentExecution -public class TrackerJob implements Job -{ - public static final String JOBDATA_TRACKER_KEY = "TRACKER"; - protected final static Logger log = LoggerFactory.getLogger(TrackerJob.class); - - - /* - * (non-Javadoc) - * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) - */ - @Override - public void execute(JobExecutionContext jec) throws JobExecutionException - { - Tracker tracker = getTracker(jec); - tracker.track(); - } - - /** - * Retrieve the {@link Tracker} from the {@link JobExecutionContext}. - * - * @param jec JobExecutionContext - * @return The tracker - */ - private Tracker getTracker(JobExecutionContext jec) - { - Tracker tracker = (Tracker) jec.getJobDetail().getJobDataMap().get(JOBDATA_TRACKER_KEY); - return tracker; - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Generic Solr tracker job, allowing Quartz to initiate an index update from + * a {@link Tracker} regardless of specific implementation. + * + * Concurrent execution is disallowed, as no parallel work can be done when + * indexing contents from the repository. + * + * @author Matt Ward + */ +@DisallowConcurrentExecution +public class TrackerJob implements Job +{ + public static final String JOBDATA_TRACKER_KEY = "TRACKER"; + protected final static Logger log = LoggerFactory.getLogger(TrackerJob.class); + + + /* + * (non-Javadoc) + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + @Override + public void execute(JobExecutionContext jec) throws JobExecutionException + { + Tracker tracker = getTracker(jec); + tracker.track(); + } + + /** + * Retrieve the {@link Tracker} from the {@link JobExecutionContext}. + * + * @param jec JobExecutionContext + * @return The tracker + */ + private Tracker getTracker(JobExecutionContext jec) + { + Tracker tracker = (Tracker) jec.getJobDetail().getJobDataMap().get(JOBDATA_TRACKER_KEY); + return tracker; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerRegistry.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerRegistry.java index 83dd155ff..8c45cd672 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerRegistry.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerRegistry.java @@ -1,106 +1,106 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Registry of trackers for an AdminHandler - * @author Ahmed Owian - */ -public class TrackerRegistry -{ - /* - * Keyed on core name and Tracker class. - * This facilitates getting a particular type of Tracker for a given core - */ - private Map, Tracker>> trackers = new ConcurrentHashMap<>(); - - /* - * There is one ModelTracker per Alfresco data schema/repository, and it has no dependency on cores. - */ - private volatile ModelTracker modelTracker; - - public Set getCoreNames() - { - return this.trackers.keySet(); - } - - public Collection getTrackersForCore(String coreName) - { - ConcurrentHashMap, Tracker> coreTrackers = this.trackers.get(coreName); - return ((coreTrackers == null) ? Collections.emptyList() : coreTrackers.values()); - } - - public boolean hasTrackersForCore(String coreName) - { - return this.trackers.containsKey(coreName); - } - - @SuppressWarnings("unchecked") - public T getTrackerForCore(String coreName, Class trackerClass) - { - Map, Tracker> coreTrackers = this.trackers.get(coreName); - return null == coreTrackers ? null : (T) coreTrackers.get(trackerClass); - } - - public synchronized void register(String coreName, Tracker tracker) - { - ConcurrentHashMap, Tracker> coreTrackers = this.trackers.get(coreName); - if (coreTrackers == null) - { - coreTrackers = new ConcurrentHashMap<>(); - this.trackers.put(coreName, coreTrackers); - } - - coreTrackers.put(tracker.getClass(), tracker); - } - - /** - * Removes the trackers for the specified core. - * @param coreName - * @return true if there were trackers registered for the core - */ - public boolean removeTrackersForCore(String coreName) - { - return this.trackers.remove(coreName) != null; - } - - public ModelTracker getModelTracker() - { - return modelTracker; - } - - public void setModelTracker(ModelTracker modelTracker) - { - this.modelTracker = modelTracker; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Registry of trackers for an AdminHandler + * @author Ahmed Owian + */ +public class TrackerRegistry +{ + /* + * Keyed on core name and Tracker class. + * This facilitates getting a particular type of Tracker for a given core + */ + private Map, Tracker>> trackers = new ConcurrentHashMap<>(); + + /* + * There is one ModelTracker per Alfresco data schema/repository, and it has no dependency on cores. + */ + private volatile ModelTracker modelTracker; + + public Set getCoreNames() + { + return this.trackers.keySet(); + } + + public Collection getTrackersForCore(String coreName) + { + ConcurrentHashMap, Tracker> coreTrackers = this.trackers.get(coreName); + return ((coreTrackers == null) ? Collections.emptyList() : coreTrackers.values()); + } + + public boolean hasTrackersForCore(String coreName) + { + return this.trackers.containsKey(coreName); + } + + @SuppressWarnings("unchecked") + public T getTrackerForCore(String coreName, Class trackerClass) + { + Map, Tracker> coreTrackers = this.trackers.get(coreName); + return null == coreTrackers ? null : (T) coreTrackers.get(trackerClass); + } + + public synchronized void register(String coreName, Tracker tracker) + { + ConcurrentHashMap, Tracker> coreTrackers = this.trackers.get(coreName); + if (coreTrackers == null) + { + coreTrackers = new ConcurrentHashMap<>(); + this.trackers.put(coreName, coreTrackers); + } + + coreTrackers.put(tracker.getClass(), tracker); + } + + /** + * Removes the trackers for the specified core. + * @param coreName + * @return true if there were trackers registered for the core + */ + public boolean removeTrackersForCore(String coreName) + { + return this.trackers.remove(coreName) != null; + } + + public ModelTracker getModelTracker() + { + return modelTracker; + } + + public void setModelTracker(ModelTracker modelTracker) + { + this.modelTracker = modelTracker; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactory.java index fcec22b62..c740d77c2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactory.java @@ -1,179 +1,179 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker.pool; - -import java.util.Properties; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.RejectedExecutionHandler; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.alfresco.util.DynamicallySizedThreadPoolExecutor; -import org.alfresco.util.TraceableThreadFactory; - -/** - * Implementation of the {@link TrackerPoolFactory}. - * - * @author Matt Ward - */ -public class DefaultTrackerPoolFactory implements TrackerPoolFactory -{ - private static final int DEFAULT_CORE_POOL_SIZE = 4; - - private static final int DEFAULT_MAXIMUM_POOL_SIZE = -1; // -1 is a sign that it must match the core pool size - - private static final int DEFAULT_KEEP_ALIVE_TIME = 120; // seconds - - private static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY; - - private static final boolean DEFAULT_THREAD_DAEMON = Boolean.TRUE; - - private static final int DEFAULT_WORK_QUEUE_SIZE = -1; - - private static final RejectedExecutionHandler DEFAULT_REJECTED_EXECUTION_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy(); - - private String poolName = ""; - - private int corePoolSize = DEFAULT_CORE_POOL_SIZE; - - private int maximumPoolSize = DEFAULT_MAXIMUM_POOL_SIZE; - - private int keepAliveTime = DEFAULT_KEEP_ALIVE_TIME; - - private int threadPriority = DEFAULT_THREAD_PRIORITY; - - private boolean threadDaemon = DEFAULT_THREAD_DAEMON; - - private int workQueueSize = DEFAULT_WORK_QUEUE_SIZE; - - private RejectedExecutionHandler rejectedExecutionHandler = DEFAULT_REJECTED_EXECUTION_HANDLER; - - private Integer parseConfig(String trackerConfig, Properties p, Integer ori) - { - if(p.getProperty(trackerConfig) != null && !p.getProperty(trackerConfig).isEmpty()) - { - return Integer.parseInt(p.getProperty(trackerConfig)); - } - return ori; - } - - private Boolean parseConfigBoolean(String trackerConfig, Properties p) - { - if(p.getProperty(trackerConfig) !=null && !p.getProperty(trackerConfig).isEmpty()) - { - return Boolean.parseBoolean(p.getProperty(trackerConfig)); - } - String param = "alfresco" + trackerConfig.substring(trackerConfig.lastIndexOf(".")); - return Boolean.parseBoolean(p.getProperty(param,"true")); - } - - public DefaultTrackerPoolFactory(Properties p, String coreName, String trackerName) - { - corePoolSize = Integer.parseInt(p.getProperty("alfresco.corePoolSize", "4")); - maximumPoolSize = Integer.parseInt(p.getProperty("alfresco.maximumPoolSize", "-1")); - keepAliveTime = Integer.parseInt(p.getProperty("alfresco.keepAliveTime", "120")); - threadPriority = Integer.parseInt(p.getProperty("alfresco.threadPriority", "5")); - threadDaemon = Boolean.parseBoolean(p.getProperty("alfresco.threadDaemon", "true")); - workQueueSize = Integer.parseInt(p.getProperty("alfresco.workQueueSize", "-1")); - if(trackerName != null && !trackerName.isEmpty()) - { - switch (trackerName) - { - case "AclTracker": - corePoolSize = parseConfig("alfresco.acl.tracker.corePoolSize", p, corePoolSize); - maximumPoolSize = parseConfig("alfresco.acl.tracker.maximumPoolSize", p, maximumPoolSize); - keepAliveTime = parseConfig("alfresco.acl.tracker.keepAliveTime", p, keepAliveTime); - threadPriority = parseConfig("alfresco.acl.tracker.threadPriority", p, threadPriority); - threadDaemon = parseConfigBoolean("alfresco.acl.tracker.threadDaemon", p); - workQueueSize = parseConfig("alfresco.acl.tracker.workQueueSize", p, workQueueSize); - break; - case "ContentTracker": - corePoolSize = parseConfig("alfresco.content.tracker.corePoolSize", p, corePoolSize); - maximumPoolSize = parseConfig("alfresco.content.tracker.maximumPoolSize", p, maximumPoolSize); - keepAliveTime = parseConfig("alfresco.content.tracker.keepAliveTime", p, keepAliveTime); - threadPriority = parseConfig("alfresco.content.tracker.threadPriority", p, threadPriority); - threadDaemon = parseConfigBoolean("alfresco.content.tracker.threadDaemon", p); - workQueueSize = parseConfig("alfresco.content.tracker.workQueueSize", p, workQueueSize); - break; - case "MetadataTracker": - corePoolSize = parseConfig("alfresco.metadata.tracker.corePoolSize", p, corePoolSize); - maximumPoolSize = parseConfig("alfresco.metadata.tracker.maximumPoolSize", p, maximumPoolSize); - keepAliveTime = parseConfig("alfresco.metadata.tracker.keepAliveTime", p, keepAliveTime); - threadPriority = parseConfig("alfresco.metadata.tracker.threadPriority", p, threadPriority); - threadDaemon = parseConfigBoolean("alfresco.metadata.tracker.threadDaemon", p); - workQueueSize = parseConfig("alfresco.metadata.tracker.workQueueSize", p, workQueueSize); - break; - default: - break; - } - } - - - poolName = "SolrTrackingPool-" + coreName + "-" + trackerName + "-"; - - // if the maximum pool size has not been set, change it to match the core pool size - if (maximumPoolSize == DEFAULT_MAXIMUM_POOL_SIZE) - { - maximumPoolSize = corePoolSize; - } - } - - @Override - public ThreadPoolExecutor create() - { - // We need a thread factory - TraceableThreadFactory threadFactory = new TraceableThreadFactory(); - threadFactory.setThreadDaemon(threadDaemon); - threadFactory.setThreadPriority(threadPriority); - - if (poolName.length() > 0) - { - threadFactory.setNamePrefix(poolName); - } - - BlockingQueue workQueue; - if (workQueueSize < 0) - { - // We can have an unlimited queue, as we have a sensible thread pool! - workQueue = new LinkedBlockingQueue(); - } - else - { - // Use an array one for consistent performance on a small queue size - workQueue = new ArrayBlockingQueue(workQueueSize); - } - - - ThreadPoolExecutor threadPoolExecutor = new DynamicallySizedThreadPoolExecutor( - corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, - workQueue, threadFactory, rejectedExecutionHandler); - - return threadPoolExecutor; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker.pool; + +import java.util.Properties; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.alfresco.util.DynamicallySizedThreadPoolExecutor; +import org.alfresco.util.TraceableThreadFactory; + +/** + * Implementation of the {@link TrackerPoolFactory}. + * + * @author Matt Ward + */ +public class DefaultTrackerPoolFactory implements TrackerPoolFactory +{ + private static final int DEFAULT_CORE_POOL_SIZE = 4; + + private static final int DEFAULT_MAXIMUM_POOL_SIZE = -1; // -1 is a sign that it must match the core pool size + + private static final int DEFAULT_KEEP_ALIVE_TIME = 120; // seconds + + private static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY; + + private static final boolean DEFAULT_THREAD_DAEMON = Boolean.TRUE; + + private static final int DEFAULT_WORK_QUEUE_SIZE = -1; + + private static final RejectedExecutionHandler DEFAULT_REJECTED_EXECUTION_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy(); + + private String poolName = ""; + + private int corePoolSize = DEFAULT_CORE_POOL_SIZE; + + private int maximumPoolSize = DEFAULT_MAXIMUM_POOL_SIZE; + + private int keepAliveTime = DEFAULT_KEEP_ALIVE_TIME; + + private int threadPriority = DEFAULT_THREAD_PRIORITY; + + private boolean threadDaemon = DEFAULT_THREAD_DAEMON; + + private int workQueueSize = DEFAULT_WORK_QUEUE_SIZE; + + private RejectedExecutionHandler rejectedExecutionHandler = DEFAULT_REJECTED_EXECUTION_HANDLER; + + private Integer parseConfig(String trackerConfig, Properties p, Integer ori) + { + if(p.getProperty(trackerConfig) != null && !p.getProperty(trackerConfig).isEmpty()) + { + return Integer.parseInt(p.getProperty(trackerConfig)); + } + return ori; + } + + private Boolean parseConfigBoolean(String trackerConfig, Properties p) + { + if(p.getProperty(trackerConfig) !=null && !p.getProperty(trackerConfig).isEmpty()) + { + return Boolean.parseBoolean(p.getProperty(trackerConfig)); + } + String param = "alfresco" + trackerConfig.substring(trackerConfig.lastIndexOf(".")); + return Boolean.parseBoolean(p.getProperty(param,"true")); + } + + public DefaultTrackerPoolFactory(Properties p, String coreName, String trackerName) + { + corePoolSize = Integer.parseInt(p.getProperty("alfresco.corePoolSize", "4")); + maximumPoolSize = Integer.parseInt(p.getProperty("alfresco.maximumPoolSize", "-1")); + keepAliveTime = Integer.parseInt(p.getProperty("alfresco.keepAliveTime", "120")); + threadPriority = Integer.parseInt(p.getProperty("alfresco.threadPriority", "5")); + threadDaemon = Boolean.parseBoolean(p.getProperty("alfresco.threadDaemon", "true")); + workQueueSize = Integer.parseInt(p.getProperty("alfresco.workQueueSize", "-1")); + if(trackerName != null && !trackerName.isEmpty()) + { + switch (trackerName) + { + case "AclTracker": + corePoolSize = parseConfig("alfresco.acl.tracker.corePoolSize", p, corePoolSize); + maximumPoolSize = parseConfig("alfresco.acl.tracker.maximumPoolSize", p, maximumPoolSize); + keepAliveTime = parseConfig("alfresco.acl.tracker.keepAliveTime", p, keepAliveTime); + threadPriority = parseConfig("alfresco.acl.tracker.threadPriority", p, threadPriority); + threadDaemon = parseConfigBoolean("alfresco.acl.tracker.threadDaemon", p); + workQueueSize = parseConfig("alfresco.acl.tracker.workQueueSize", p, workQueueSize); + break; + case "ContentTracker": + corePoolSize = parseConfig("alfresco.content.tracker.corePoolSize", p, corePoolSize); + maximumPoolSize = parseConfig("alfresco.content.tracker.maximumPoolSize", p, maximumPoolSize); + keepAliveTime = parseConfig("alfresco.content.tracker.keepAliveTime", p, keepAliveTime); + threadPriority = parseConfig("alfresco.content.tracker.threadPriority", p, threadPriority); + threadDaemon = parseConfigBoolean("alfresco.content.tracker.threadDaemon", p); + workQueueSize = parseConfig("alfresco.content.tracker.workQueueSize", p, workQueueSize); + break; + case "MetadataTracker": + corePoolSize = parseConfig("alfresco.metadata.tracker.corePoolSize", p, corePoolSize); + maximumPoolSize = parseConfig("alfresco.metadata.tracker.maximumPoolSize", p, maximumPoolSize); + keepAliveTime = parseConfig("alfresco.metadata.tracker.keepAliveTime", p, keepAliveTime); + threadPriority = parseConfig("alfresco.metadata.tracker.threadPriority", p, threadPriority); + threadDaemon = parseConfigBoolean("alfresco.metadata.tracker.threadDaemon", p); + workQueueSize = parseConfig("alfresco.metadata.tracker.workQueueSize", p, workQueueSize); + break; + default: + break; + } + } + + + poolName = "SolrTrackingPool-" + coreName + "-" + trackerName + "-"; + + // if the maximum pool size has not been set, change it to match the core pool size + if (maximumPoolSize == DEFAULT_MAXIMUM_POOL_SIZE) + { + maximumPoolSize = corePoolSize; + } + } + + @Override + public ThreadPoolExecutor create() + { + // We need a thread factory + TraceableThreadFactory threadFactory = new TraceableThreadFactory(); + threadFactory.setThreadDaemon(threadDaemon); + threadFactory.setThreadPriority(threadPriority); + + if (poolName.length() > 0) + { + threadFactory.setNamePrefix(poolName); + } + + BlockingQueue workQueue; + if (workQueueSize < 0) + { + // We can have an unlimited queue, as we have a sensible thread pool! + workQueue = new LinkedBlockingQueue(); + } + else + { + // Use an array one for consistent performance on a small queue size + workQueue = new ArrayBlockingQueue(workQueueSize); + } + + + ThreadPoolExecutor threadPoolExecutor = new DynamicallySizedThreadPoolExecutor( + corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, + workQueue, threadFactory, rejectedExecutionHandler); + + return threadPoolExecutor; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/TrackerPoolFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/TrackerPoolFactory.java index b4a714442..fc354c0cd 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/TrackerPoolFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/TrackerPoolFactory.java @@ -1,39 +1,39 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker.pool; - -import java.util.concurrent.ThreadPoolExecutor; - -/** - * Create a thread pool for the {@link org.alfresco.solr.tracker.Tracker}s. - * - * @author Matt Ward - */ -public interface TrackerPoolFactory -{ - ThreadPoolExecutor create(); -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker.pool; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * Create a thread pool for the {@link org.alfresco.solr.tracker.Tracker}s. + * + * @author Matt Ward + */ +public interface TrackerPoolFactory +{ + ThreadPoolExecutor create(); +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformer.java index 5783e6260..9034b984f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -138,4 +138,4 @@ public class AlfrescoFieldMapperTransformer extends DocTransformer return value; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerFactory.java index 40fe2e2d4..26522265e 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformer.java index 2121275af..80d9d06e7 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformer.java @@ -1,189 +1,189 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.transformer; - -import java.io.IOException; -import java.util.ArrayList; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.SortedDocValues; -import org.apache.lucene.index.SortedNumericDocValues; -import org.apache.lucene.index.SortedSetDocValues; -import org.apache.lucene.util.NumericUtils; -import org.apache.solr.common.SolrDocument; -import org.apache.solr.response.ResultContext; -import org.apache.solr.response.transform.DocTransformer; -import org.apache.solr.schema.SchemaField; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - * - */ -public class DocValueDocTransformer extends DocTransformer -{ - protected final static Logger log = LoggerFactory.getLogger(DocValueDocTransformer.class); - - /* (non-Javadoc) - * @see org.apache.solr.response.transform.DocTransformer#getName() - */ - @Override - public String getName() - { - return "Alfresco doc value document transformer"; - } - - - public void setContext( ResultContext context ) - { - this.context = context; - } - - - /* (non-Javadoc) - * @see org.apache.solr.response.transform.DocTransformer#transform(org.apache.solr.common.SolrDocument, int) - */ - @Override - public void transform(SolrDocument doc, int docid, float score) throws IOException - { - for(String fieldName :context.getSearcher().getFieldNames()) - { - SchemaField schemaField = context.getSearcher().getSchema().getFieldOrNull(fieldName); - if(schemaField != null) - { - if(schemaField.hasDocValues()) - { - SortedDocValues sortedDocValues = context.getSearcher().getSlowAtomicReader().getSortedDocValues(fieldName); - if(sortedDocValues != null) - { - int ordinal = sortedDocValues.getOrd(docid); - if(ordinal > -1) - { - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - doc.addField(alfrescoFieldName, schemaField.getType().toObject(schemaField, sortedDocValues.lookupOrd(ordinal))); - } - } - - SortedSetDocValues sortedSetDocValues = context.getSearcher().getSlowAtomicReader().getSortedSetDocValues(fieldName); - if(sortedSetDocValues != null) - { - ArrayList newValues = new ArrayList(); - sortedSetDocValues.setDocument(docid); - long ordinal; - while ( (ordinal = sortedSetDocValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) - { - newValues.add(schemaField.getType().toObject(schemaField, sortedSetDocValues.lookupOrd(ordinal))); - } - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - doc.addField(alfrescoFieldName, newValues); - } - - - BinaryDocValues binaryDocValues = context.getSearcher().getSlowAtomicReader().getBinaryDocValues(fieldName); - if(binaryDocValues != null) - { - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - doc.addField(alfrescoFieldName, schemaField.getType().toObject(schemaField, binaryDocValues.get(docid))); - } - - if(schemaField.getType().getNumericType() != null) - { - NumericDocValues numericDocValues = context.getSearcher().getSlowAtomicReader().getNumericDocValues(fieldName); - if(numericDocValues != null) - { - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - switch(schemaField.getType().getNumericType()) - { - case DOUBLE: - doc.addField(alfrescoFieldName, Double.longBitsToDouble(numericDocValues.get(docid))); - break; - case FLOAT: - doc.addField(alfrescoFieldName, Float.intBitsToFloat((int) numericDocValues.get(docid))); - break; - case INT: - doc.addField(alfrescoFieldName, (int) numericDocValues.get(docid)); - break; - case LONG: - doc.addField(alfrescoFieldName, numericDocValues.get(docid)); - break; - } - } - - SortedNumericDocValues sortedNumericDocValues = context.getSearcher().getSlowAtomicReader().getSortedNumericDocValues(fieldName); - if(sortedNumericDocValues != null) - { - sortedNumericDocValues.setDocument(docid); - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - ArrayList newValues = new ArrayList(sortedNumericDocValues.count()); - if(sortedNumericDocValues.count() > 0) - { - - for(int i = 0; i < sortedNumericDocValues.count(); i++) - { - switch(schemaField.getType().getNumericType()) - { - case DOUBLE: - newValues.add(NumericUtils.sortableLongToDouble(sortedNumericDocValues.valueAt(i))); - break; - case FLOAT: - newValues.add(NumericUtils.sortableIntToFloat((int)sortedNumericDocValues.valueAt(i))); - break; - case INT: - newValues.add((int)sortedNumericDocValues.valueAt(i)); - break; - case LONG: - newValues.add(sortedNumericDocValues.valueAt(i)); - break; - - } - } - } - doc.addField(alfrescoFieldName, newValues); - - } - } - } - } - } - - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.transformer; + +import java.io.IOException; +import java.util.ArrayList; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.index.SortedNumericDocValues; +import org.apache.lucene.index.SortedSetDocValues; +import org.apache.lucene.util.NumericUtils; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.response.ResultContext; +import org.apache.solr.response.transform.DocTransformer; +import org.apache.solr.schema.SchemaField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + * + */ +public class DocValueDocTransformer extends DocTransformer +{ + protected final static Logger log = LoggerFactory.getLogger(DocValueDocTransformer.class); + + /* (non-Javadoc) + * @see org.apache.solr.response.transform.DocTransformer#getName() + */ + @Override + public String getName() + { + return "Alfresco doc value document transformer"; + } + + + public void setContext( ResultContext context ) + { + this.context = context; + } + + + /* (non-Javadoc) + * @see org.apache.solr.response.transform.DocTransformer#transform(org.apache.solr.common.SolrDocument, int) + */ + @Override + public void transform(SolrDocument doc, int docid, float score) throws IOException + { + for(String fieldName :context.getSearcher().getFieldNames()) + { + SchemaField schemaField = context.getSearcher().getSchema().getFieldOrNull(fieldName); + if(schemaField != null) + { + if(schemaField.hasDocValues()) + { + SortedDocValues sortedDocValues = context.getSearcher().getSlowAtomicReader().getSortedDocValues(fieldName); + if(sortedDocValues != null) + { + int ordinal = sortedDocValues.getOrd(docid); + if(ordinal > -1) + { + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + doc.addField(alfrescoFieldName, schemaField.getType().toObject(schemaField, sortedDocValues.lookupOrd(ordinal))); + } + } + + SortedSetDocValues sortedSetDocValues = context.getSearcher().getSlowAtomicReader().getSortedSetDocValues(fieldName); + if(sortedSetDocValues != null) + { + ArrayList newValues = new ArrayList(); + sortedSetDocValues.setDocument(docid); + long ordinal; + while ( (ordinal = sortedSetDocValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) + { + newValues.add(schemaField.getType().toObject(schemaField, sortedSetDocValues.lookupOrd(ordinal))); + } + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + doc.addField(alfrescoFieldName, newValues); + } + + + BinaryDocValues binaryDocValues = context.getSearcher().getSlowAtomicReader().getBinaryDocValues(fieldName); + if(binaryDocValues != null) + { + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + doc.addField(alfrescoFieldName, schemaField.getType().toObject(schemaField, binaryDocValues.get(docid))); + } + + if(schemaField.getType().getNumericType() != null) + { + NumericDocValues numericDocValues = context.getSearcher().getSlowAtomicReader().getNumericDocValues(fieldName); + if(numericDocValues != null) + { + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + switch(schemaField.getType().getNumericType()) + { + case DOUBLE: + doc.addField(alfrescoFieldName, Double.longBitsToDouble(numericDocValues.get(docid))); + break; + case FLOAT: + doc.addField(alfrescoFieldName, Float.intBitsToFloat((int) numericDocValues.get(docid))); + break; + case INT: + doc.addField(alfrescoFieldName, (int) numericDocValues.get(docid)); + break; + case LONG: + doc.addField(alfrescoFieldName, numericDocValues.get(docid)); + break; + } + } + + SortedNumericDocValues sortedNumericDocValues = context.getSearcher().getSlowAtomicReader().getSortedNumericDocValues(fieldName); + if(sortedNumericDocValues != null) + { + sortedNumericDocValues.setDocument(docid); + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + ArrayList newValues = new ArrayList(sortedNumericDocValues.count()); + if(sortedNumericDocValues.count() > 0) + { + + for(int i = 0; i < sortedNumericDocValues.count(); i++) + { + switch(schemaField.getType().getNumericType()) + { + case DOUBLE: + newValues.add(NumericUtils.sortableLongToDouble(sortedNumericDocValues.valueAt(i))); + break; + case FLOAT: + newValues.add(NumericUtils.sortableIntToFloat((int)sortedNumericDocValues.valueAt(i))); + break; + case INT: + newValues.add((int)sortedNumericDocValues.valueAt(i)); + break; + case LONG: + newValues.add(sortedNumericDocValues.valueAt(i)); + break; + + } + } + } + doc.addField(alfrescoFieldName, newValues); + + } + } + } + } + } + + } + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformerFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformerFactory.java index 0829b3457..d40ab3741 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformerFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformerFactory.java @@ -1,50 +1,50 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.transformer; - -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.transform.DocTransformer; -import org.apache.solr.response.transform.TransformerFactory; - -/** - * @author Andy - * - */ -public class DocValueDocTransformerFactory extends TransformerFactory -{ - - /* (non-Javadoc) - * @see org.apache.solr.response.transform.TransformerFactory#create(java.lang.String, org.apache.solr.common.params.SolrParams, org.apache.solr.request.SolrQueryRequest) - */ - @Override - public DocTransformer create(String field, SolrParams params, SolrQueryRequest req) - { - return new DocValueDocTransformer(); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.transformer; + +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.transform.DocTransformer; +import org.apache.solr.response.transform.TransformerFactory; + +/** + * @author Andy + * + */ +public class DocValueDocTransformerFactory extends TransformerFactory +{ + + /* (non-Javadoc) + * @see org.apache.solr.response.transform.TransformerFactory#create(java.lang.String, org.apache.solr.common.params.SolrParams, org.apache.solr.request.SolrQueryRequest) + */ + @Override + public DocTransformer create(String field, SolrParams params, SolrQueryRequest req) + { + return new DocValueDocTransformer(); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoDistributedUpdateProcessorFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoDistributedUpdateProcessorFactory.java index 7767df6f3..8e181ef51 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoDistributedUpdateProcessorFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoDistributedUpdateProcessorFactory.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 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% diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoExplicitDocumentMerger.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoExplicitDocumentMerger.java index bffb57bf6..3a3def45c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoExplicitDocumentMerger.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoExplicitDocumentMerger.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 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% diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/AlfrescoFileUtils.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/AlfrescoFileUtils.java index 7f5ba804a..b9e58be58 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/AlfrescoFileUtils.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/AlfrescoFileUtils.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingConsumer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingConsumer.java index da476c76a..4878d0412 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingConsumer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingConsumer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingFunction.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingFunction.java index a6ca43dde..3ff20d11a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingFunction.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingFunction.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/Utils.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/Utils.java index 0e95205b8..ed5f45c71 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/Utils.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/Utils.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -188,4 +188,4 @@ public abstract class Utils return null; } } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilter.java b/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilter.java index c31834e58..d0774e22d 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilter.java +++ b/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilterFactory.java b/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilterFactory.java index 3ef3b7d18..8191bc2b7 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilterFactory.java +++ b/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilterFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/core/CoreDescriptorDecorator.java b/search-services/alfresco-search/src/main/java/org/apache/solr/core/CoreDescriptorDecorator.java index 0e1b5eeee..1fc436606 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/core/CoreDescriptorDecorator.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/core/CoreDescriptorDecorator.java @@ -1,108 +1,108 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.apache.solr.core; - -import java.util.List; -import java.util.Properties; - -import org.alfresco.solr.config.ConfigUtil; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.google.common.collect.ImmutableList; - -/** - * This class was created solely for the purpose of exposing the coreProperties of the CoreDescriptor. - * It is now possible to substitute a sub-set of properties using the rules specified here @see ConfigUtil#locateProperty() - * - * The Substitutable Properties are defined in the substitutableProperties list. - * @author Ahmed Owian - * @author Gethin James - * @author aborroy - */ -public class CoreDescriptorDecorator -{ - private static Log log = LogFactory.getLog(CoreDescriptorDecorator.class); - private final Properties properties = new Properties(); - - private static String SECURE_COMMS_PROPERTY = "alfresco.secureComms"; - - public static final ImmutableList SUBSTITUTABLE_PROPERTIES_SECURE = - ImmutableList.of( - "alfresco.host", - "alfresco.port", - "alfresco.baseUrl", - "alfresco.port.ssl", - "alfresco.secureComms", - "alfresco.encryption.ssl.keystore.passwordFileLocation", - "alfresco.encryption.ssl.truststore.passwordFileLocation", - "alfresco.encryption.ssl.keystore.location", - "alfresco.encryption.ssl.truststore.location", - "alfresco.encryption.ssl.truststore.provider", - "alfresco.encryption.ssl.keystore.type", - "alfresco.encryption.ssl.keystore.provider", - "alfresco.encryption.ssl.truststore.type"); - - public static final ImmutableList SUBSTITUTABLE_PROPERTIES_NONE = - ImmutableList.of( - "alfresco.host", - "alfresco.port", - "alfresco.baseUrl", - "alfresco.secureComms"); - - public CoreDescriptorDecorator(CoreDescriptor descriptor) - { - properties.putAll(descriptor.coreProperties); - - List coreProperties; - String comms = ConfigUtil.locateProperty(SECURE_COMMS_PROPERTY, "none"); - if (comms.equals("https")) - { - coreProperties = SUBSTITUTABLE_PROPERTIES_SECURE; - } - else - { - coreProperties = SUBSTITUTABLE_PROPERTIES_NONE; - } - - try - { - coreProperties.forEach(prop -> - properties.put(prop, ConfigUtil.locateProperty(prop,properties.getProperty(prop))) - ); - } - catch(Exception e) - { - log.warn("Unable to locate alfresco host|port|baseUrl|ssl properties", e); - } - } - - public Properties getProperties() - { - return this.properties; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.apache.solr.core; + +import java.util.List; +import java.util.Properties; + +import org.alfresco.solr.config.ConfigUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.google.common.collect.ImmutableList; + +/** + * This class was created solely for the purpose of exposing the coreProperties of the CoreDescriptor. + * It is now possible to substitute a sub-set of properties using the rules specified here @see ConfigUtil#locateProperty() + * + * The Substitutable Properties are defined in the substitutableProperties list. + * @author Ahmed Owian + * @author Gethin James + * @author aborroy + */ +public class CoreDescriptorDecorator +{ + private static Log log = LogFactory.getLog(CoreDescriptorDecorator.class); + private final Properties properties = new Properties(); + + private static String SECURE_COMMS_PROPERTY = "alfresco.secureComms"; + + public static final ImmutableList SUBSTITUTABLE_PROPERTIES_SECURE = + ImmutableList.of( + "alfresco.host", + "alfresco.port", + "alfresco.baseUrl", + "alfresco.port.ssl", + "alfresco.secureComms", + "alfresco.encryption.ssl.keystore.passwordFileLocation", + "alfresco.encryption.ssl.truststore.passwordFileLocation", + "alfresco.encryption.ssl.keystore.location", + "alfresco.encryption.ssl.truststore.location", + "alfresco.encryption.ssl.truststore.provider", + "alfresco.encryption.ssl.keystore.type", + "alfresco.encryption.ssl.keystore.provider", + "alfresco.encryption.ssl.truststore.type"); + + public static final ImmutableList SUBSTITUTABLE_PROPERTIES_NONE = + ImmutableList.of( + "alfresco.host", + "alfresco.port", + "alfresco.baseUrl", + "alfresco.secureComms"); + + public CoreDescriptorDecorator(CoreDescriptor descriptor) + { + properties.putAll(descriptor.coreProperties); + + List coreProperties; + String comms = ConfigUtil.locateProperty(SECURE_COMMS_PROPERTY, "none"); + if (comms.equals("https")) + { + coreProperties = SUBSTITUTABLE_PROPERTIES_SECURE; + } + else + { + coreProperties = SUBSTITUTABLE_PROPERTIES_NONE; + } + + try + { + coreProperties.forEach(prop -> + properties.put(prop, ConfigUtil.locateProperty(prop,properties.getProperty(prop))) + ); + } + catch(Exception e) + { + log.warn("Unable to locate alfresco host|port|baseUrl|ssl properties", e); + } + } + + public Properties getProperties() + { + return this.properties; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandler.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandler.java index 7a469b086..960375786 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandler.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandler.java @@ -1,51 +1,51 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; - -import org.alfresco.solr.query.AbstractQParser; -import org.apache.http.client.HttpClient; -import org.apache.solr.client.solrj.SolrRequest; -import org.apache.solr.client.solrj.request.QueryRequest; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.util.ContentStreamBase; - -public class AlfrescoHttpShardHandler extends HttpShardHandler { - - public AlfrescoHttpShardHandler(HttpShardHandlerFactory alfrescoHttpShardHandlerFactory, HttpClient httpClient) { - super(alfrescoHttpShardHandlerFactory, httpClient); - } - - /** - * Subclasses could modify the request based on the shard - */ - protected QueryRequest makeQueryRequest(final ShardRequest sreq, ModifiableSolrParams params, String shard) - { - AlfrescoQueryRequest req = new AlfrescoQueryRequest(params, SolrRequest.METHOD.POST); - return req; - } -} - +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; + +import org.alfresco.solr.query.AbstractQParser; +import org.apache.http.client.HttpClient; +import org.apache.solr.client.solrj.SolrRequest; +import org.apache.solr.client.solrj.request.QueryRequest; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.util.ContentStreamBase; + +public class AlfrescoHttpShardHandler extends HttpShardHandler { + + public AlfrescoHttpShardHandler(HttpShardHandlerFactory alfrescoHttpShardHandlerFactory, HttpClient httpClient) { + super(alfrescoHttpShardHandlerFactory, httpClient); + } + + /** + * Subclasses could modify the request based on the shard + */ + protected QueryRequest makeQueryRequest(final ShardRequest sreq, ModifiableSolrParams params, String shard) + { + AlfrescoQueryRequest req = new AlfrescoQueryRequest(params, SolrRequest.METHOD.POST); + return req; + } +} + diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandlerFactory.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandlerFactory.java index afae2a709..d4eab0f06 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandlerFactory.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandlerFactory.java @@ -1,50 +1,50 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; - - -import org.apache.http.client.HttpClient; - - - -/** - * @author Andy - * - */ - -public class AlfrescoHttpShardHandlerFactory extends HttpShardHandlerFactory { - - - /** - * Get {@link ShardHandler} that uses custom http client. - */ - public ShardHandler getShardHandler(final HttpClient httpClient){ - return new AlfrescoHttpShardHandler(this, httpClient); - } - -} - +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; + + +import org.apache.http.client.HttpClient; + + + +/** + * @author Andy + * + */ + +public class AlfrescoHttpShardHandlerFactory extends HttpShardHandlerFactory { + + + /** + * Get {@link ShardHandler} that uses custom http client. + */ + public ShardHandler getShardHandler(final HttpClient httpClient){ + return new AlfrescoHttpShardHandler(this, httpClient); + } + +} + diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandler.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandler.java index a42d6cd4f..9c2a0b09f 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandler.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandler.java @@ -1,852 +1,852 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; - -import static org.apache.lucene.index.IndexOptions.DOCS; -import static org.apache.lucene.index.IndexOptions.DOCS_AND_FREQS; -import static org.apache.lucene.index.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS; - -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.util.CharFilterFactory; -import org.apache.lucene.analysis.util.TokenFilterFactory; -import org.apache.lucene.analysis.util.TokenizerFactory; -import org.apache.lucene.document.Document; -import org.apache.lucene.document.Field; -import org.apache.lucene.index.DirectoryReader; -import org.apache.lucene.index.DocValuesType; -import org.apache.lucene.index.FieldInfo; -import org.apache.lucene.index.IndexOptions; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.MultiFields; -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.index.SegmentReader; -import org.apache.lucene.index.Term; -import org.apache.lucene.index.Terms; -import org.apache.lucene.index.TermsEnum; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.similarities.Similarity; -import org.apache.lucene.store.Directory; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.CharsRefBuilder; -import org.apache.lucene.util.PriorityQueue; -import org.apache.solr.analysis.TokenizerChain; -import org.apache.solr.common.SolrException; -import org.apache.solr.common.SolrException.ErrorCode; -import org.apache.solr.common.luke.FieldFlag; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.Base64; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.handler.RequestHandlerBase; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.schema.CopyField; -import org.apache.solr.schema.FieldType; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.update.SolrIndexWriter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class AlfrescoLukeRequestHandler extends RequestHandlerBase { - private static final Logger log = LoggerFactory.getLogger(MethodHandles - .lookup().lookupClass()); - - public static final String NUMTERMS = "numTerms"; - public static final String INCLUDE_INDEX_FIELD_FLAGS = "includeIndexFieldFlags"; - public static final String DOC_ID = "docId"; - public static final String ID = "id"; - public static final int DEFAULT_COUNT = 10; - - static final int HIST_ARRAY_SIZE = 33; - - private static enum ShowStyle { - ALL, DOC, SCHEMA, INDEX; - - public static ShowStyle get(String v) { - if (v == null) - return null; - if ("schema".equalsIgnoreCase(v)) - return SCHEMA; - if ("index".equalsIgnoreCase(v)) - return INDEX; - if ("doc".equalsIgnoreCase(v)) - return DOC; - if ("all".equalsIgnoreCase(v)) - return ALL; - throw new SolrException(ErrorCode.BAD_REQUEST, - "Unknown Show Style: " + v); - } - }; - - @Override - public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) - throws Exception { - IndexSchema schema = req.getSchema(); - SolrIndexSearcher searcher = req.getSearcher(); - DirectoryReader reader = searcher.getIndexReader(); - SolrParams params = req.getParams(); - ShowStyle style = ShowStyle.get(params.get("show")); - - // If no doc is given, show all fields and top terms - - rsp.add("index", getIndexInfo(reader)); - - if (ShowStyle.INDEX == style) { - return; // that's all we need - } - - Integer docId = params.getInt(DOC_ID); - if (docId == null && params.get(ID) != null) { - // Look for something with a given solr ID - SchemaField uniqueKey = schema.getUniqueKeyField(); - String v = uniqueKey.getType().toInternal(params.get(ID)); - Term t = new Term(uniqueKey.getName(), v); - docId = searcher.getFirstMatch(t); - if (docId < 0) { - throw new SolrException(SolrException.ErrorCode.NOT_FOUND, - "Can't find document: " + params.get(ID)); - } - } - - // Read the document from the index - if (docId != null) { - if (style != null && style != ShowStyle.DOC) { - throw new SolrException(ErrorCode.BAD_REQUEST, - "missing doc param for doc style"); - } - Document doc = null; - try { - doc = reader.document(docId); - } catch (Exception ex) { - } - if (doc == null) { - throw new SolrException(SolrException.ErrorCode.NOT_FOUND, - "Can't find document: " + docId); - } - - SimpleOrderedMap info = getDocumentFieldsInfo(doc, docId, - reader, schema); - - SimpleOrderedMap docinfo = new SimpleOrderedMap<>(); - docinfo.add("docId", docId); - docinfo.add("lucene", info); - docinfo.add("solr", doc); - rsp.add("doc", docinfo); - } else if (ShowStyle.SCHEMA == style) { - rsp.add("schema", getSchemaInfo(req.getSchema())); - } else { - rsp.add("fields", getIndexedFieldsInfo(req)); - } - - // Add some generally helpful information - NamedList info = new SimpleOrderedMap<>(); - info.add("key", getFieldFlagsKey()); - info.add( - "NOTE", - "Document Frequency (df) is not updated when a document is marked for deletion. df values include deleted documents."); - rsp.add("info", info); - rsp.setHttpCaching(false); - } - - /** - * @return a string representing a IndexableField's flags. - */ - private static String getFieldFlags(IndexableField f) { - IndexOptions opts = (f == null) ? null : f.fieldType().indexOptions(); - - StringBuilder flags = new StringBuilder(); - - flags.append((f != null && f.fieldType().indexOptions() != IndexOptions.NONE) ? FieldFlag.INDEXED - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().tokenized()) ? FieldFlag.TOKENIZED - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().stored()) ? FieldFlag.STORED - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().docValuesType() != DocValuesType.NONE) ? FieldFlag.DOC_VALUES - .getAbbreviation() : "-"); - flags.append((false) ? FieldFlag.MULTI_VALUED.getAbbreviation() : '-'); // SchemaField - // Specific - flags.append((f != null && f.fieldType().storeTermVectors()) ? FieldFlag.TERM_VECTOR_STORED - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().storeTermVectorOffsets()) ? FieldFlag.TERM_VECTOR_OFFSET - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().storeTermVectorPositions()) ? FieldFlag.TERM_VECTOR_POSITION - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().storeTermVectorPayloads()) ? FieldFlag.TERM_VECTOR_PAYLOADS - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().omitNorms()) ? FieldFlag.OMIT_NORMS - .getAbbreviation() : '-'); - - flags.append((f != null && DOCS == opts) ? FieldFlag.OMIT_TF - .getAbbreviation() : '-'); - - flags.append((f != null && DOCS_AND_FREQS == opts) ? FieldFlag.OMIT_POSITIONS - .getAbbreviation() : '-'); - - flags.append((f != null && DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS == opts) ? FieldFlag.STORE_OFFSETS_WITH_POSITIONS - .getAbbreviation() : '-'); - - flags.append((f != null && f.getClass().getSimpleName() - .equals("LazyField")) ? FieldFlag.LAZY.getAbbreviation() : '-'); - flags.append((f != null && f.binaryValue() != null) ? FieldFlag.BINARY - .getAbbreviation() : '-'); - flags.append((false) ? FieldFlag.SORT_MISSING_FIRST.getAbbreviation() - : '-'); // SchemaField Specific - flags.append((false) ? FieldFlag.SORT_MISSING_LAST.getAbbreviation() - : '-'); // SchemaField Specific - return flags.toString(); - } - - /** - * @return a string representing a SchemaField's flags. - */ - private static String getFieldFlags(SchemaField f) { - FieldType t = (f == null) ? null : f.getType(); - - // see: - // http://www.nabble.com/schema-field-properties-tf3437753.html#a9585549 - boolean lazy = false; // "lazy" is purely a property of reading fields - boolean binary = false; // Currently not possible - - StringBuilder flags = new StringBuilder(); - flags.append((f != null && f.indexed()) ? FieldFlag.INDEXED - .getAbbreviation() : '-'); - flags.append((t != null && t.isTokenized()) ? FieldFlag.TOKENIZED - .getAbbreviation() : '-'); - flags.append((f != null && f.stored()) ? FieldFlag.STORED - .getAbbreviation() : '-'); - flags.append((f != null && f.hasDocValues()) ? FieldFlag.DOC_VALUES - .getAbbreviation() : "-"); - flags.append((f != null && f.multiValued()) ? FieldFlag.MULTI_VALUED - .getAbbreviation() : '-'); - flags.append((f != null && f.storeTermVector()) ? FieldFlag.TERM_VECTOR_STORED - .getAbbreviation() : '-'); - flags.append((f != null && f.storeTermOffsets()) ? FieldFlag.TERM_VECTOR_OFFSET - .getAbbreviation() : '-'); - flags.append((f != null && f.storeTermPositions()) ? FieldFlag.TERM_VECTOR_POSITION - .getAbbreviation() : '-'); - flags.append((f != null && f.storeTermPayloads()) ? FieldFlag.TERM_VECTOR_PAYLOADS - .getAbbreviation() : '-'); - flags.append((f != null && f.omitNorms()) ? FieldFlag.OMIT_NORMS - .getAbbreviation() : '-'); - flags.append((f != null && f.omitTermFreqAndPositions()) ? FieldFlag.OMIT_TF - .getAbbreviation() : '-'); - flags.append((f != null && f.omitPositions()) ? FieldFlag.OMIT_POSITIONS - .getAbbreviation() : '-'); - flags.append((f != null && f.storeOffsetsWithPositions()) ? FieldFlag.STORE_OFFSETS_WITH_POSITIONS - .getAbbreviation() : '-'); - flags.append((lazy) ? FieldFlag.LAZY.getAbbreviation() : '-'); - flags.append((binary) ? FieldFlag.BINARY.getAbbreviation() : '-'); - flags.append((f != null && f.sortMissingFirst()) ? FieldFlag.SORT_MISSING_FIRST - .getAbbreviation() : '-'); - flags.append((f != null && f.sortMissingLast()) ? FieldFlag.SORT_MISSING_LAST - .getAbbreviation() : '-'); - return flags.toString(); - } - - /** - * @return a key to what each character means - */ - public static SimpleOrderedMap getFieldFlagsKey() { - SimpleOrderedMap key = new SimpleOrderedMap<>(); - for (FieldFlag f : FieldFlag.values()) { - key.add(String.valueOf(f.getAbbreviation()), f.getDisplay()); - } - return key; - } - - private static SimpleOrderedMap getDocumentFieldsInfo(Document doc, - int docId, IndexReader reader, IndexSchema schema) - throws IOException { - final CharsRefBuilder spare = new CharsRefBuilder(); - SimpleOrderedMap finfo = new SimpleOrderedMap<>(); - for (Object o : doc.getFields()) { - Field field = (Field) o; - SimpleOrderedMap f = new SimpleOrderedMap<>(); - - SchemaField sfield = schema.getFieldOrNull(field.name()); - FieldType ftype = (sfield == null) ? null : sfield.getType(); - - f.add("type", (ftype == null) ? null : ftype.getTypeName()); - f.add("schema", getFieldFlags(sfield)); - f.add("flags", getFieldFlags(field)); - - Term t = new Term(field.name(), - ftype != null ? ftype.storedToIndexed(field) - : field.stringValue()); - - f.add("value", (ftype == null) ? null : ftype.toExternal(field)); - - // TODO: this really should be "stored" - f.add("internal", field.stringValue()); // may be a binary number - - BytesRef bytes = field.binaryValue(); - if (bytes != null) { - f.add("binary", Base64.byteArrayToBase64(bytes.bytes, - bytes.offset, bytes.length)); - } - f.add("boost", field.boost()); - f.add("docFreq", t.text() == null ? 0 : reader.docFreq(t)); // this - // can - // be 0 - // for - // non-indexed - // fields - - // If we have a term vector, return that - if (field.fieldType().storeTermVectors()) { - try { - Terms v = reader.getTermVector(docId, field.name()); - if (v != null) { - SimpleOrderedMap tfv = new SimpleOrderedMap<>(); - final TermsEnum termsEnum = v.iterator(); - BytesRef text; - while ((text = termsEnum.next()) != null) { - final int freq = (int) termsEnum.totalTermFreq(); - spare.copyUTF8Bytes(text); - tfv.add(spare.toString(), freq); - } - f.add("termVector", tfv); - } - } catch (Exception ex) { - log.warn("error writing term vector", ex); - } - } - - finfo.add(field.name(), f); - } - return finfo; - } - - private static SimpleOrderedMap getIndexedFieldsInfo( - SolrQueryRequest req) throws Exception { - - SolrIndexSearcher searcher = req.getSearcher(); - SolrParams params = req.getParams(); - - Set fields = null; - String fl = params.get(CommonParams.FL); - if (fl != null) { - fields = new TreeSet<>(Arrays.asList(fl.split("[,\\s]+"))); - } - - LeafReader reader = searcher.getSlowAtomicReader(); - IndexSchema schema = searcher.getSchema(); - - // Don't be tempted to put this in the loop below, the whole point here - // is to alphabetize the fields! - Set fieldNames = new TreeSet<>(); - for (FieldInfo fieldInfo : reader.getFieldInfos()) { - fieldNames.add(fieldInfo.name); - } - - // Walk the term enum and keep a priority queue for each map in our set - SimpleOrderedMap vInfo = new SimpleOrderedMap<>(); - SimpleOrderedMap aInfo = new SimpleOrderedMap<>(); - - for (String fieldName : fieldNames) { - if (fields != null && !fields.contains(fieldName) - && !fields.contains("*")) { - continue; // we're not interested in this field Still an issue - // here - } - - SimpleOrderedMap fieldMap = new SimpleOrderedMap<>(); - - SchemaField sfield = schema.getFieldOrNull(fieldName); - FieldType ftype = (sfield == null) ? null : sfield.getType(); - - fieldMap.add("type", (ftype == null) ? null : ftype.getTypeName()); - fieldMap.add("schema", getFieldFlags(sfield)); - if (sfield != null && schema.isDynamicField(sfield.getName()) - && schema.getDynamicPattern(sfield.getName()) != null) { - fieldMap.add("dynamicBase", - schema.getDynamicPattern(sfield.getName())); - } - Terms terms = reader.fields().terms(fieldName); - if (terms == null) { // Not indexed, so we need to report what we - // can (it made it through the fl param if - // specified) - vInfo.add(AlfrescoSolrDataModel.getInstance() - .getAlfrescoPropertyFromSchemaField(fieldName), - fieldMap); - aInfo.add(fieldName, fieldMap); - continue; - } - - if (sfield != null && sfield.indexed()) { - if (params.getBool(INCLUDE_INDEX_FIELD_FLAGS, true)) { - Document doc = getFirstLiveDoc(terms, reader); - - if (doc != null) { - // Found a document with this field - try { - IndexableField fld = doc.getField(fieldName); - if (fld != null) { - fieldMap.add("index", getFieldFlags(fld)); - } else { - // it is a non-stored field... - fieldMap.add("index", "(unstored field)"); - } - } catch (Exception ex) { - log.warn("error reading field: " + fieldName); - } - } - } - fieldMap.add("docs", terms.getDocCount()); - - } - if (fields != null - && (fields.contains(fieldName) || fields.contains("*"))) { - getDetailedFieldInfo(req, fieldName, fieldMap); - } - // Add the field - vInfo.add(fieldName, fieldMap); - aInfo.add(AlfrescoSolrDataModel.getInstance() - .getAlfrescoPropertyFromSchemaField(fieldName), fieldMap); - } - - SimpleOrderedMap finfo = new SimpleOrderedMap<>(); - finfo.addAll(vInfo); - // finfo.add("mimetype()", finfo.get("cm:content.mimetype")); - // finfo.add("contentSize()", finfo.get("cm:content.size")); - finfo.addAll(aInfo); - return finfo; - } - - // Just get a document with the term in it, the first one will do! - // Is there a better way to do this? Shouldn't actually be very costly - // to do it this way. - protected static Document getFirstLiveDoc(Terms terms, LeafReader reader) - throws IOException { - TermsEnum termsEnum = terms.iterator(); - if (termsEnum.next() == null) { // Ran off the end of the terms enum without finding any live docs with that field in them. - return null; - } - PostingsEnum postingsEnum = termsEnum.postings(null, PostingsEnum.NONE); - final Bits liveDocs = reader.getLiveDocs(); - if (postingsEnum.nextDoc() == DocIdSetIterator.NO_MORE_DOCS - || (liveDocs != null && liveDocs.get(postingsEnum.docID()))) { - return null; - } - return reader.document(postingsEnum.docID()); - } - - /** - * Return info from the index - */ - private static SimpleOrderedMap getSchemaInfo(IndexSchema schema) { - Map> typeusemap = new TreeMap<>(); - Map fields = new TreeMap<>(); - SchemaField uniqueField = schema.getUniqueKeyField(); - for (SchemaField f : schema.getFields().values()) { - populateFieldInfo(schema, typeusemap, fields, uniqueField, f); - } - - Map dynamicFields = new TreeMap<>(); - for (SchemaField f : schema.getDynamicFieldPrototypes()) { - populateFieldInfo(schema, typeusemap, dynamicFields, uniqueField, f); - } - SimpleOrderedMap types = new SimpleOrderedMap<>(); - Map sortedTypes = new TreeMap<>( - schema.getFieldTypes()); - for (FieldType ft : sortedTypes.values()) { - SimpleOrderedMap field = new SimpleOrderedMap<>(); - field.add("fields", typeusemap.get(ft.getTypeName())); - field.add("tokenized", ft.isTokenized()); - field.add("className", ft.getClass().getName()); - field.add("indexAnalyzer", getAnalyzerInfo(ft.getIndexAnalyzer())); - field.add("queryAnalyzer", getAnalyzerInfo(ft.getQueryAnalyzer())); - field.add("similarity", getSimilarityInfo(ft.getSimilarity())); - types.add(ft.getTypeName(), field); - } - - // Must go through this to maintain binary compatbility. Putting a - // TreeMap into a resp leads to casting errors - SimpleOrderedMap finfo = new SimpleOrderedMap<>(); - - SimpleOrderedMap fieldsSimple = new SimpleOrderedMap<>(); - for (Map.Entry ent : fields.entrySet()) { - fieldsSimple.add(ent.getKey(), ent.getValue()); - } - finfo.add("fields", fieldsSimple); - - SimpleOrderedMap dynamicSimple = new SimpleOrderedMap<>(); - for (Map.Entry ent : dynamicFields.entrySet()) { - dynamicSimple.add(ent.getKey(), ent.getValue()); - } - finfo.add("dynamicFields", dynamicSimple); - - finfo.add("uniqueKeyField", - null == uniqueField ? null : uniqueField.getName()); - finfo.add("defaultSearchField", schema.getDefaultSearchFieldName()); - finfo.add("types", types); - return finfo; - } - - private static SimpleOrderedMap getSimilarityInfo( - Similarity similarity) { - SimpleOrderedMap toReturn = new SimpleOrderedMap<>(); - if (similarity != null) { - toReturn.add("className", similarity.getClass().getName()); - toReturn.add("details", similarity.toString()); - } - return toReturn; - } - - private static SimpleOrderedMap getAnalyzerInfo(Analyzer analyzer) { - SimpleOrderedMap aninfo = new SimpleOrderedMap<>(); - aninfo.add("className", analyzer.getClass().getName()); - if (analyzer instanceof TokenizerChain) { - - TokenizerChain tchain = (TokenizerChain) analyzer; - - CharFilterFactory[] cfiltfacs = tchain.getCharFilterFactories(); - if (0 < cfiltfacs.length) { - SimpleOrderedMap> cfilters = new SimpleOrderedMap<>(); - for (CharFilterFactory cfiltfac : cfiltfacs) { - Map tok = new HashMap<>(); - String className = cfiltfac.getClass().getName(); - tok.put("className", className); - tok.put("args", cfiltfac.getOriginalArgs()); - cfilters.add( - className.substring(className.lastIndexOf('.') + 1), - tok); - } - aninfo.add("charFilters", cfilters); - } - - SimpleOrderedMap tokenizer = new SimpleOrderedMap<>(); - TokenizerFactory tfac = tchain.getTokenizerFactory(); - tokenizer.add("className", tfac.getClass().getName()); - tokenizer.add("args", tfac.getOriginalArgs()); - aninfo.add("tokenizer", tokenizer); - - TokenFilterFactory[] filtfacs = tchain.getTokenFilterFactories(); - if (0 < filtfacs.length) { - SimpleOrderedMap> filters = new SimpleOrderedMap<>(); - for (TokenFilterFactory filtfac : filtfacs) { - Map tok = new HashMap<>(); - String className = filtfac.getClass().getName(); - tok.put("className", className); - tok.put("args", filtfac.getOriginalArgs()); - filters.add( - className.substring(className.lastIndexOf('.') + 1), - tok); - } - aninfo.add("filters", filters); - } - } - return aninfo; - } - - private static void populateFieldInfo(IndexSchema schema, - Map> typeusemap, Map fields, - SchemaField uniqueField, SchemaField f) { - FieldType ft = f.getType(); - SimpleOrderedMap field = new SimpleOrderedMap<>(); - field.add("type", ft.getTypeName()); - field.add("flags", getFieldFlags(f)); - if (f.isRequired()) { - field.add("required", f.isRequired()); - } - if (f.getDefaultValue() != null) { - field.add("default", f.getDefaultValue()); - } - if (f == uniqueField) { - field.add("uniqueKey", true); - } - if (ft.getIndexAnalyzer().getPositionIncrementGap(f.getName()) != 0) { - field.add("positionIncrementGap", ft.getIndexAnalyzer() - .getPositionIncrementGap(f.getName())); - } - field.add("copyDests", - toListOfStringDests(schema.getCopyFieldsList(f.getName()))); - field.add("copySources", schema.getCopySources(f.getName())); - - fields.put(f.getName(), field); - - List v = typeusemap.get(ft.getTypeName()); - if (v == null) { - v = new ArrayList<>(); - } - v.add(f.getName()); - typeusemap.put(ft.getTypeName(), v); - } - - /** - * @deprecated use {@link #getIndexInfo(DirectoryReader)} since you now have - * to explicitly pass the "fl" prameter and this was always - * called with "false" anyway from CoreAdminHandler - */ - public static SimpleOrderedMap getIndexInfo(DirectoryReader reader, - boolean detail) throws IOException { - return getIndexInfo(reader); - } - - // This method just gets the top-most level of information. This was - // conflated with getting detailed info - // for *all* the fields, called from CoreAdminHandler etc. - - public static SimpleOrderedMap getIndexInfo(DirectoryReader reader) - throws IOException { - Directory dir = reader.directory(); - SimpleOrderedMap indexInfo = new SimpleOrderedMap<>(); - - indexInfo.add("numDocs", reader.numDocs()); - indexInfo.add("maxDoc", reader.maxDoc()); - indexInfo.add("deletedDocs", reader.maxDoc() - reader.numDocs()); - indexInfo.add("indexHeapUsageBytes", getIndexHeapUsed(reader)); - - indexInfo.add("version", reader.getVersion()); // TODO? Is this - // different then: - // IndexReader.getCurrentVersion( - // dir )? - indexInfo.add("segmentCount", reader.leaves().size()); - indexInfo.add("current", reader.isCurrent()); - indexInfo.add("hasDeletions", reader.hasDeletions()); - indexInfo.add("directory", dir); - indexInfo.add("userData", reader.getIndexCommit().getUserData()); - String s = reader.getIndexCommit().getUserData() - .get(SolrIndexWriter.COMMIT_TIME_MSEC_KEY); - if (s != null) { - indexInfo.add("lastModified", new Date(Long.parseLong(s))); - } - return indexInfo; - } - - /** Returns the sum of RAM bytes used by each segment */ - private static long getIndexHeapUsed(DirectoryReader reader) { - long indexHeapRamBytesUsed = 0; - for (LeafReaderContext leafReaderContext : reader.leaves()) { - LeafReader leafReader = leafReaderContext.reader(); - if (leafReader instanceof SegmentReader) { - indexHeapRamBytesUsed += ((SegmentReader) leafReader) - .ramBytesUsed(); - } else { - // Not supported for any reader that is not a SegmentReader - return -1; - } - } - return indexHeapRamBytesUsed; - } - - // Get terribly detailed information about a particular field. This is a - // very expensive call, use it with caution - // especially on large indexes! - @SuppressWarnings("unchecked") - private static void getDetailedFieldInfo(SolrQueryRequest req, - String field, SimpleOrderedMap fieldMap) throws IOException { - - SolrParams params = req.getParams(); - final int numTerms = params.getInt(NUMTERMS, DEFAULT_COUNT); - - TopTermQueue tiq = new TopTermQueue(numTerms + 1); // Something to - // collect the top N - // terms in. - - final CharsRefBuilder spare = new CharsRefBuilder(); - - Terms terms = MultiFields.getTerms(req.getSearcher().getIndexReader(), - field); - if (terms == null) { // field does not exist - return; - } - TermsEnum termsEnum = terms.iterator(); - BytesRef text; - int[] buckets = new int[HIST_ARRAY_SIZE]; - while ((text = termsEnum.next()) != null) { - ++tiq.distinctTerms; - int freq = termsEnum.docFreq(); // This calculation seems odd, but - // it gives the same results as it - // used to. - int slot = 32 - Integer.numberOfLeadingZeros(Math.max(0, freq - 1)); - buckets[slot] = buckets[slot] + 1; - if (numTerms > 0 && freq > tiq.minFreq) { - spare.copyUTF8Bytes(text); - String t = spare.toString(); - - tiq.add(new TopTermQueue.TermInfo(new Term(field, t), termsEnum - .docFreq())); - if (tiq.size() > numTerms) { // if tiq full - tiq.pop(); // remove lowest in tiq - tiq.minFreq = tiq.getTopTermInfo().docFreq; - } - } - } - tiq.histogram.add(buckets); - fieldMap.add("distinct", tiq.distinctTerms); - - // Include top terms - fieldMap.add("topTerms", tiq.toNamedList(req.getSearcher().getSchema())); - - // Add a histogram - fieldMap.add("histogram", tiq.histogram.toNamedList()); - } - - private static List toListOfStrings(SchemaField[] raw) { - List result = new ArrayList<>(raw.length); - for (SchemaField f : raw) { - result.add(f.getName()); - } - return result; - } - - private static List toListOfStringDests(List raw) { - List result = new ArrayList<>(raw.size()); - for (CopyField f : raw) { - result.add(f.getDestination().getName()); - } - return result; - } - - // ////////////////////// SolrInfoMBeans methods ////////////////////// - - @Override - public String getDescription() { - return "Lucene Index Browser. Inspired and modeled after Luke: http://www.getopt.org/luke/"; - } - - @Override - public URL[] getDocs() { - try { - return new URL[] { new URL( - "http://wiki.apache.org/solr/LukeRequestHandler") }; - } catch (MalformedURLException ex) { - return null; - } - } - - // ///////////////////////////////////////////////////////////////////////////////////// - - static class TermHistogram { - int _maxBucket = -1; - int _buckets[] = new int[HIST_ARRAY_SIZE]; - - public void add(int[] buckets) { - for (int idx = 0; idx < buckets.length; ++idx) { - if (buckets[idx] != 0) - _maxBucket = idx; - } - for (int idx = 0; idx <= _maxBucket; ++idx) { - _buckets[idx] = buckets[idx]; - } - } - - // TODO? should this be a list or a map? - public NamedList toNamedList() { - NamedList nl = new NamedList<>(); - for (int bucket = 0; bucket <= _maxBucket; bucket++) { - nl.add("" + (1 << bucket), _buckets[bucket]); - } - return nl; - } - } - - /** - * Private internal class that counts up frequent terms - */ - private static class TopTermQueue extends PriorityQueue { - static class TermInfo { - TermInfo(Term t, int df) { - term = t; - docFreq = df; - } - - int docFreq; - Term term; - } - - public int minFreq = 0; - public int distinctTerms = 0; - public TermHistogram histogram; - - TopTermQueue(int size) { - super(size); - histogram = new TermHistogram(); - } - - @Override - protected final boolean lessThan(Object a, Object b) { - TermInfo termInfoA = (TermInfo) a; - TermInfo termInfoB = (TermInfo) b; - return termInfoA.docFreq < termInfoB.docFreq; - } - - /** - * This is a destructive call... the queue is empty at the end - */ - public NamedList toNamedList(IndexSchema schema) { - // reverse the list.. - List aslist = new LinkedList<>(); - while (size() > 0) { - aslist.add(0, (TermInfo) pop()); - } - - NamedList list = new NamedList<>(); - for (TermInfo i : aslist) { - String txt = i.term.text(); - SchemaField ft = schema.getFieldOrNull(i.term.field()); - if (ft != null) { - txt = ft.getType().indexedToReadable(txt); - } - list.add(txt, i.docFreq); - } - return list; - } - - public TermInfo getTopTermInfo() { - return (TermInfo) top(); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; + +import static org.apache.lucene.index.IndexOptions.DOCS; +import static org.apache.lucene.index.IndexOptions.DOCS_AND_FREQS; +import static org.apache.lucene.index.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.util.CharFilterFactory; +import org.apache.lucene.analysis.util.TokenFilterFactory; +import org.apache.lucene.analysis.util.TokenizerFactory; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.DocValuesType; +import org.apache.lucene.index.FieldInfo; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.MultiFields; +import org.apache.lucene.index.PostingsEnum; +import org.apache.lucene.index.SegmentReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.index.Terms; +import org.apache.lucene.index.TermsEnum; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.similarities.Similarity; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.CharsRefBuilder; +import org.apache.lucene.util.PriorityQueue; +import org.apache.solr.analysis.TokenizerChain; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrException.ErrorCode; +import org.apache.solr.common.luke.FieldFlag; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.Base64; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.handler.RequestHandlerBase; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.schema.CopyField; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.update.SolrIndexWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class AlfrescoLukeRequestHandler extends RequestHandlerBase { + private static final Logger log = LoggerFactory.getLogger(MethodHandles + .lookup().lookupClass()); + + public static final String NUMTERMS = "numTerms"; + public static final String INCLUDE_INDEX_FIELD_FLAGS = "includeIndexFieldFlags"; + public static final String DOC_ID = "docId"; + public static final String ID = "id"; + public static final int DEFAULT_COUNT = 10; + + static final int HIST_ARRAY_SIZE = 33; + + private static enum ShowStyle { + ALL, DOC, SCHEMA, INDEX; + + public static ShowStyle get(String v) { + if (v == null) + return null; + if ("schema".equalsIgnoreCase(v)) + return SCHEMA; + if ("index".equalsIgnoreCase(v)) + return INDEX; + if ("doc".equalsIgnoreCase(v)) + return DOC; + if ("all".equalsIgnoreCase(v)) + return ALL; + throw new SolrException(ErrorCode.BAD_REQUEST, + "Unknown Show Style: " + v); + } + }; + + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) + throws Exception { + IndexSchema schema = req.getSchema(); + SolrIndexSearcher searcher = req.getSearcher(); + DirectoryReader reader = searcher.getIndexReader(); + SolrParams params = req.getParams(); + ShowStyle style = ShowStyle.get(params.get("show")); + + // If no doc is given, show all fields and top terms + + rsp.add("index", getIndexInfo(reader)); + + if (ShowStyle.INDEX == style) { + return; // that's all we need + } + + Integer docId = params.getInt(DOC_ID); + if (docId == null && params.get(ID) != null) { + // Look for something with a given solr ID + SchemaField uniqueKey = schema.getUniqueKeyField(); + String v = uniqueKey.getType().toInternal(params.get(ID)); + Term t = new Term(uniqueKey.getName(), v); + docId = searcher.getFirstMatch(t); + if (docId < 0) { + throw new SolrException(SolrException.ErrorCode.NOT_FOUND, + "Can't find document: " + params.get(ID)); + } + } + + // Read the document from the index + if (docId != null) { + if (style != null && style != ShowStyle.DOC) { + throw new SolrException(ErrorCode.BAD_REQUEST, + "missing doc param for doc style"); + } + Document doc = null; + try { + doc = reader.document(docId); + } catch (Exception ex) { + } + if (doc == null) { + throw new SolrException(SolrException.ErrorCode.NOT_FOUND, + "Can't find document: " + docId); + } + + SimpleOrderedMap info = getDocumentFieldsInfo(doc, docId, + reader, schema); + + SimpleOrderedMap docinfo = new SimpleOrderedMap<>(); + docinfo.add("docId", docId); + docinfo.add("lucene", info); + docinfo.add("solr", doc); + rsp.add("doc", docinfo); + } else if (ShowStyle.SCHEMA == style) { + rsp.add("schema", getSchemaInfo(req.getSchema())); + } else { + rsp.add("fields", getIndexedFieldsInfo(req)); + } + + // Add some generally helpful information + NamedList info = new SimpleOrderedMap<>(); + info.add("key", getFieldFlagsKey()); + info.add( + "NOTE", + "Document Frequency (df) is not updated when a document is marked for deletion. df values include deleted documents."); + rsp.add("info", info); + rsp.setHttpCaching(false); + } + + /** + * @return a string representing a IndexableField's flags. + */ + private static String getFieldFlags(IndexableField f) { + IndexOptions opts = (f == null) ? null : f.fieldType().indexOptions(); + + StringBuilder flags = new StringBuilder(); + + flags.append((f != null && f.fieldType().indexOptions() != IndexOptions.NONE) ? FieldFlag.INDEXED + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().tokenized()) ? FieldFlag.TOKENIZED + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().stored()) ? FieldFlag.STORED + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().docValuesType() != DocValuesType.NONE) ? FieldFlag.DOC_VALUES + .getAbbreviation() : "-"); + flags.append((false) ? FieldFlag.MULTI_VALUED.getAbbreviation() : '-'); // SchemaField + // Specific + flags.append((f != null && f.fieldType().storeTermVectors()) ? FieldFlag.TERM_VECTOR_STORED + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().storeTermVectorOffsets()) ? FieldFlag.TERM_VECTOR_OFFSET + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().storeTermVectorPositions()) ? FieldFlag.TERM_VECTOR_POSITION + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().storeTermVectorPayloads()) ? FieldFlag.TERM_VECTOR_PAYLOADS + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().omitNorms()) ? FieldFlag.OMIT_NORMS + .getAbbreviation() : '-'); + + flags.append((f != null && DOCS == opts) ? FieldFlag.OMIT_TF + .getAbbreviation() : '-'); + + flags.append((f != null && DOCS_AND_FREQS == opts) ? FieldFlag.OMIT_POSITIONS + .getAbbreviation() : '-'); + + flags.append((f != null && DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS == opts) ? FieldFlag.STORE_OFFSETS_WITH_POSITIONS + .getAbbreviation() : '-'); + + flags.append((f != null && f.getClass().getSimpleName() + .equals("LazyField")) ? FieldFlag.LAZY.getAbbreviation() : '-'); + flags.append((f != null && f.binaryValue() != null) ? FieldFlag.BINARY + .getAbbreviation() : '-'); + flags.append((false) ? FieldFlag.SORT_MISSING_FIRST.getAbbreviation() + : '-'); // SchemaField Specific + flags.append((false) ? FieldFlag.SORT_MISSING_LAST.getAbbreviation() + : '-'); // SchemaField Specific + return flags.toString(); + } + + /** + * @return a string representing a SchemaField's flags. + */ + private static String getFieldFlags(SchemaField f) { + FieldType t = (f == null) ? null : f.getType(); + + // see: + // http://www.nabble.com/schema-field-properties-tf3437753.html#a9585549 + boolean lazy = false; // "lazy" is purely a property of reading fields + boolean binary = false; // Currently not possible + + StringBuilder flags = new StringBuilder(); + flags.append((f != null && f.indexed()) ? FieldFlag.INDEXED + .getAbbreviation() : '-'); + flags.append((t != null && t.isTokenized()) ? FieldFlag.TOKENIZED + .getAbbreviation() : '-'); + flags.append((f != null && f.stored()) ? FieldFlag.STORED + .getAbbreviation() : '-'); + flags.append((f != null && f.hasDocValues()) ? FieldFlag.DOC_VALUES + .getAbbreviation() : "-"); + flags.append((f != null && f.multiValued()) ? FieldFlag.MULTI_VALUED + .getAbbreviation() : '-'); + flags.append((f != null && f.storeTermVector()) ? FieldFlag.TERM_VECTOR_STORED + .getAbbreviation() : '-'); + flags.append((f != null && f.storeTermOffsets()) ? FieldFlag.TERM_VECTOR_OFFSET + .getAbbreviation() : '-'); + flags.append((f != null && f.storeTermPositions()) ? FieldFlag.TERM_VECTOR_POSITION + .getAbbreviation() : '-'); + flags.append((f != null && f.storeTermPayloads()) ? FieldFlag.TERM_VECTOR_PAYLOADS + .getAbbreviation() : '-'); + flags.append((f != null && f.omitNorms()) ? FieldFlag.OMIT_NORMS + .getAbbreviation() : '-'); + flags.append((f != null && f.omitTermFreqAndPositions()) ? FieldFlag.OMIT_TF + .getAbbreviation() : '-'); + flags.append((f != null && f.omitPositions()) ? FieldFlag.OMIT_POSITIONS + .getAbbreviation() : '-'); + flags.append((f != null && f.storeOffsetsWithPositions()) ? FieldFlag.STORE_OFFSETS_WITH_POSITIONS + .getAbbreviation() : '-'); + flags.append((lazy) ? FieldFlag.LAZY.getAbbreviation() : '-'); + flags.append((binary) ? FieldFlag.BINARY.getAbbreviation() : '-'); + flags.append((f != null && f.sortMissingFirst()) ? FieldFlag.SORT_MISSING_FIRST + .getAbbreviation() : '-'); + flags.append((f != null && f.sortMissingLast()) ? FieldFlag.SORT_MISSING_LAST + .getAbbreviation() : '-'); + return flags.toString(); + } + + /** + * @return a key to what each character means + */ + public static SimpleOrderedMap getFieldFlagsKey() { + SimpleOrderedMap key = new SimpleOrderedMap<>(); + for (FieldFlag f : FieldFlag.values()) { + key.add(String.valueOf(f.getAbbreviation()), f.getDisplay()); + } + return key; + } + + private static SimpleOrderedMap getDocumentFieldsInfo(Document doc, + int docId, IndexReader reader, IndexSchema schema) + throws IOException { + final CharsRefBuilder spare = new CharsRefBuilder(); + SimpleOrderedMap finfo = new SimpleOrderedMap<>(); + for (Object o : doc.getFields()) { + Field field = (Field) o; + SimpleOrderedMap f = new SimpleOrderedMap<>(); + + SchemaField sfield = schema.getFieldOrNull(field.name()); + FieldType ftype = (sfield == null) ? null : sfield.getType(); + + f.add("type", (ftype == null) ? null : ftype.getTypeName()); + f.add("schema", getFieldFlags(sfield)); + f.add("flags", getFieldFlags(field)); + + Term t = new Term(field.name(), + ftype != null ? ftype.storedToIndexed(field) + : field.stringValue()); + + f.add("value", (ftype == null) ? null : ftype.toExternal(field)); + + // TODO: this really should be "stored" + f.add("internal", field.stringValue()); // may be a binary number + + BytesRef bytes = field.binaryValue(); + if (bytes != null) { + f.add("binary", Base64.byteArrayToBase64(bytes.bytes, + bytes.offset, bytes.length)); + } + f.add("boost", field.boost()); + f.add("docFreq", t.text() == null ? 0 : reader.docFreq(t)); // this + // can + // be 0 + // for + // non-indexed + // fields + + // If we have a term vector, return that + if (field.fieldType().storeTermVectors()) { + try { + Terms v = reader.getTermVector(docId, field.name()); + if (v != null) { + SimpleOrderedMap tfv = new SimpleOrderedMap<>(); + final TermsEnum termsEnum = v.iterator(); + BytesRef text; + while ((text = termsEnum.next()) != null) { + final int freq = (int) termsEnum.totalTermFreq(); + spare.copyUTF8Bytes(text); + tfv.add(spare.toString(), freq); + } + f.add("termVector", tfv); + } + } catch (Exception ex) { + log.warn("error writing term vector", ex); + } + } + + finfo.add(field.name(), f); + } + return finfo; + } + + private static SimpleOrderedMap getIndexedFieldsInfo( + SolrQueryRequest req) throws Exception { + + SolrIndexSearcher searcher = req.getSearcher(); + SolrParams params = req.getParams(); + + Set fields = null; + String fl = params.get(CommonParams.FL); + if (fl != null) { + fields = new TreeSet<>(Arrays.asList(fl.split("[,\\s]+"))); + } + + LeafReader reader = searcher.getSlowAtomicReader(); + IndexSchema schema = searcher.getSchema(); + + // Don't be tempted to put this in the loop below, the whole point here + // is to alphabetize the fields! + Set fieldNames = new TreeSet<>(); + for (FieldInfo fieldInfo : reader.getFieldInfos()) { + fieldNames.add(fieldInfo.name); + } + + // Walk the term enum and keep a priority queue for each map in our set + SimpleOrderedMap vInfo = new SimpleOrderedMap<>(); + SimpleOrderedMap aInfo = new SimpleOrderedMap<>(); + + for (String fieldName : fieldNames) { + if (fields != null && !fields.contains(fieldName) + && !fields.contains("*")) { + continue; // we're not interested in this field Still an issue + // here + } + + SimpleOrderedMap fieldMap = new SimpleOrderedMap<>(); + + SchemaField sfield = schema.getFieldOrNull(fieldName); + FieldType ftype = (sfield == null) ? null : sfield.getType(); + + fieldMap.add("type", (ftype == null) ? null : ftype.getTypeName()); + fieldMap.add("schema", getFieldFlags(sfield)); + if (sfield != null && schema.isDynamicField(sfield.getName()) + && schema.getDynamicPattern(sfield.getName()) != null) { + fieldMap.add("dynamicBase", + schema.getDynamicPattern(sfield.getName())); + } + Terms terms = reader.fields().terms(fieldName); + if (terms == null) { // Not indexed, so we need to report what we + // can (it made it through the fl param if + // specified) + vInfo.add(AlfrescoSolrDataModel.getInstance() + .getAlfrescoPropertyFromSchemaField(fieldName), + fieldMap); + aInfo.add(fieldName, fieldMap); + continue; + } + + if (sfield != null && sfield.indexed()) { + if (params.getBool(INCLUDE_INDEX_FIELD_FLAGS, true)) { + Document doc = getFirstLiveDoc(terms, reader); + + if (doc != null) { + // Found a document with this field + try { + IndexableField fld = doc.getField(fieldName); + if (fld != null) { + fieldMap.add("index", getFieldFlags(fld)); + } else { + // it is a non-stored field... + fieldMap.add("index", "(unstored field)"); + } + } catch (Exception ex) { + log.warn("error reading field: " + fieldName); + } + } + } + fieldMap.add("docs", terms.getDocCount()); + + } + if (fields != null + && (fields.contains(fieldName) || fields.contains("*"))) { + getDetailedFieldInfo(req, fieldName, fieldMap); + } + // Add the field + vInfo.add(fieldName, fieldMap); + aInfo.add(AlfrescoSolrDataModel.getInstance() + .getAlfrescoPropertyFromSchemaField(fieldName), fieldMap); + } + + SimpleOrderedMap finfo = new SimpleOrderedMap<>(); + finfo.addAll(vInfo); + // finfo.add("mimetype()", finfo.get("cm:content.mimetype")); + // finfo.add("contentSize()", finfo.get("cm:content.size")); + finfo.addAll(aInfo); + return finfo; + } + + // Just get a document with the term in it, the first one will do! + // Is there a better way to do this? Shouldn't actually be very costly + // to do it this way. + protected static Document getFirstLiveDoc(Terms terms, LeafReader reader) + throws IOException { + TermsEnum termsEnum = terms.iterator(); + if (termsEnum.next() == null) { // Ran off the end of the terms enum without finding any live docs with that field in them. + return null; + } + PostingsEnum postingsEnum = termsEnum.postings(null, PostingsEnum.NONE); + final Bits liveDocs = reader.getLiveDocs(); + if (postingsEnum.nextDoc() == DocIdSetIterator.NO_MORE_DOCS + || (liveDocs != null && liveDocs.get(postingsEnum.docID()))) { + return null; + } + return reader.document(postingsEnum.docID()); + } + + /** + * Return info from the index + */ + private static SimpleOrderedMap getSchemaInfo(IndexSchema schema) { + Map> typeusemap = new TreeMap<>(); + Map fields = new TreeMap<>(); + SchemaField uniqueField = schema.getUniqueKeyField(); + for (SchemaField f : schema.getFields().values()) { + populateFieldInfo(schema, typeusemap, fields, uniqueField, f); + } + + Map dynamicFields = new TreeMap<>(); + for (SchemaField f : schema.getDynamicFieldPrototypes()) { + populateFieldInfo(schema, typeusemap, dynamicFields, uniqueField, f); + } + SimpleOrderedMap types = new SimpleOrderedMap<>(); + Map sortedTypes = new TreeMap<>( + schema.getFieldTypes()); + for (FieldType ft : sortedTypes.values()) { + SimpleOrderedMap field = new SimpleOrderedMap<>(); + field.add("fields", typeusemap.get(ft.getTypeName())); + field.add("tokenized", ft.isTokenized()); + field.add("className", ft.getClass().getName()); + field.add("indexAnalyzer", getAnalyzerInfo(ft.getIndexAnalyzer())); + field.add("queryAnalyzer", getAnalyzerInfo(ft.getQueryAnalyzer())); + field.add("similarity", getSimilarityInfo(ft.getSimilarity())); + types.add(ft.getTypeName(), field); + } + + // Must go through this to maintain binary compatbility. Putting a + // TreeMap into a resp leads to casting errors + SimpleOrderedMap finfo = new SimpleOrderedMap<>(); + + SimpleOrderedMap fieldsSimple = new SimpleOrderedMap<>(); + for (Map.Entry ent : fields.entrySet()) { + fieldsSimple.add(ent.getKey(), ent.getValue()); + } + finfo.add("fields", fieldsSimple); + + SimpleOrderedMap dynamicSimple = new SimpleOrderedMap<>(); + for (Map.Entry ent : dynamicFields.entrySet()) { + dynamicSimple.add(ent.getKey(), ent.getValue()); + } + finfo.add("dynamicFields", dynamicSimple); + + finfo.add("uniqueKeyField", + null == uniqueField ? null : uniqueField.getName()); + finfo.add("defaultSearchField", schema.getDefaultSearchFieldName()); + finfo.add("types", types); + return finfo; + } + + private static SimpleOrderedMap getSimilarityInfo( + Similarity similarity) { + SimpleOrderedMap toReturn = new SimpleOrderedMap<>(); + if (similarity != null) { + toReturn.add("className", similarity.getClass().getName()); + toReturn.add("details", similarity.toString()); + } + return toReturn; + } + + private static SimpleOrderedMap getAnalyzerInfo(Analyzer analyzer) { + SimpleOrderedMap aninfo = new SimpleOrderedMap<>(); + aninfo.add("className", analyzer.getClass().getName()); + if (analyzer instanceof TokenizerChain) { + + TokenizerChain tchain = (TokenizerChain) analyzer; + + CharFilterFactory[] cfiltfacs = tchain.getCharFilterFactories(); + if (0 < cfiltfacs.length) { + SimpleOrderedMap> cfilters = new SimpleOrderedMap<>(); + for (CharFilterFactory cfiltfac : cfiltfacs) { + Map tok = new HashMap<>(); + String className = cfiltfac.getClass().getName(); + tok.put("className", className); + tok.put("args", cfiltfac.getOriginalArgs()); + cfilters.add( + className.substring(className.lastIndexOf('.') + 1), + tok); + } + aninfo.add("charFilters", cfilters); + } + + SimpleOrderedMap tokenizer = new SimpleOrderedMap<>(); + TokenizerFactory tfac = tchain.getTokenizerFactory(); + tokenizer.add("className", tfac.getClass().getName()); + tokenizer.add("args", tfac.getOriginalArgs()); + aninfo.add("tokenizer", tokenizer); + + TokenFilterFactory[] filtfacs = tchain.getTokenFilterFactories(); + if (0 < filtfacs.length) { + SimpleOrderedMap> filters = new SimpleOrderedMap<>(); + for (TokenFilterFactory filtfac : filtfacs) { + Map tok = new HashMap<>(); + String className = filtfac.getClass().getName(); + tok.put("className", className); + tok.put("args", filtfac.getOriginalArgs()); + filters.add( + className.substring(className.lastIndexOf('.') + 1), + tok); + } + aninfo.add("filters", filters); + } + } + return aninfo; + } + + private static void populateFieldInfo(IndexSchema schema, + Map> typeusemap, Map fields, + SchemaField uniqueField, SchemaField f) { + FieldType ft = f.getType(); + SimpleOrderedMap field = new SimpleOrderedMap<>(); + field.add("type", ft.getTypeName()); + field.add("flags", getFieldFlags(f)); + if (f.isRequired()) { + field.add("required", f.isRequired()); + } + if (f.getDefaultValue() != null) { + field.add("default", f.getDefaultValue()); + } + if (f == uniqueField) { + field.add("uniqueKey", true); + } + if (ft.getIndexAnalyzer().getPositionIncrementGap(f.getName()) != 0) { + field.add("positionIncrementGap", ft.getIndexAnalyzer() + .getPositionIncrementGap(f.getName())); + } + field.add("copyDests", + toListOfStringDests(schema.getCopyFieldsList(f.getName()))); + field.add("copySources", schema.getCopySources(f.getName())); + + fields.put(f.getName(), field); + + List v = typeusemap.get(ft.getTypeName()); + if (v == null) { + v = new ArrayList<>(); + } + v.add(f.getName()); + typeusemap.put(ft.getTypeName(), v); + } + + /** + * @deprecated use {@link #getIndexInfo(DirectoryReader)} since you now have + * to explicitly pass the "fl" prameter and this was always + * called with "false" anyway from CoreAdminHandler + */ + public static SimpleOrderedMap getIndexInfo(DirectoryReader reader, + boolean detail) throws IOException { + return getIndexInfo(reader); + } + + // This method just gets the top-most level of information. This was + // conflated with getting detailed info + // for *all* the fields, called from CoreAdminHandler etc. + + public static SimpleOrderedMap getIndexInfo(DirectoryReader reader) + throws IOException { + Directory dir = reader.directory(); + SimpleOrderedMap indexInfo = new SimpleOrderedMap<>(); + + indexInfo.add("numDocs", reader.numDocs()); + indexInfo.add("maxDoc", reader.maxDoc()); + indexInfo.add("deletedDocs", reader.maxDoc() - reader.numDocs()); + indexInfo.add("indexHeapUsageBytes", getIndexHeapUsed(reader)); + + indexInfo.add("version", reader.getVersion()); // TODO? Is this + // different then: + // IndexReader.getCurrentVersion( + // dir )? + indexInfo.add("segmentCount", reader.leaves().size()); + indexInfo.add("current", reader.isCurrent()); + indexInfo.add("hasDeletions", reader.hasDeletions()); + indexInfo.add("directory", dir); + indexInfo.add("userData", reader.getIndexCommit().getUserData()); + String s = reader.getIndexCommit().getUserData() + .get(SolrIndexWriter.COMMIT_TIME_MSEC_KEY); + if (s != null) { + indexInfo.add("lastModified", new Date(Long.parseLong(s))); + } + return indexInfo; + } + + /** Returns the sum of RAM bytes used by each segment */ + private static long getIndexHeapUsed(DirectoryReader reader) { + long indexHeapRamBytesUsed = 0; + for (LeafReaderContext leafReaderContext : reader.leaves()) { + LeafReader leafReader = leafReaderContext.reader(); + if (leafReader instanceof SegmentReader) { + indexHeapRamBytesUsed += ((SegmentReader) leafReader) + .ramBytesUsed(); + } else { + // Not supported for any reader that is not a SegmentReader + return -1; + } + } + return indexHeapRamBytesUsed; + } + + // Get terribly detailed information about a particular field. This is a + // very expensive call, use it with caution + // especially on large indexes! + @SuppressWarnings("unchecked") + private static void getDetailedFieldInfo(SolrQueryRequest req, + String field, SimpleOrderedMap fieldMap) throws IOException { + + SolrParams params = req.getParams(); + final int numTerms = params.getInt(NUMTERMS, DEFAULT_COUNT); + + TopTermQueue tiq = new TopTermQueue(numTerms + 1); // Something to + // collect the top N + // terms in. + + final CharsRefBuilder spare = new CharsRefBuilder(); + + Terms terms = MultiFields.getTerms(req.getSearcher().getIndexReader(), + field); + if (terms == null) { // field does not exist + return; + } + TermsEnum termsEnum = terms.iterator(); + BytesRef text; + int[] buckets = new int[HIST_ARRAY_SIZE]; + while ((text = termsEnum.next()) != null) { + ++tiq.distinctTerms; + int freq = termsEnum.docFreq(); // This calculation seems odd, but + // it gives the same results as it + // used to. + int slot = 32 - Integer.numberOfLeadingZeros(Math.max(0, freq - 1)); + buckets[slot] = buckets[slot] + 1; + if (numTerms > 0 && freq > tiq.minFreq) { + spare.copyUTF8Bytes(text); + String t = spare.toString(); + + tiq.add(new TopTermQueue.TermInfo(new Term(field, t), termsEnum + .docFreq())); + if (tiq.size() > numTerms) { // if tiq full + tiq.pop(); // remove lowest in tiq + tiq.minFreq = tiq.getTopTermInfo().docFreq; + } + } + } + tiq.histogram.add(buckets); + fieldMap.add("distinct", tiq.distinctTerms); + + // Include top terms + fieldMap.add("topTerms", tiq.toNamedList(req.getSearcher().getSchema())); + + // Add a histogram + fieldMap.add("histogram", tiq.histogram.toNamedList()); + } + + private static List toListOfStrings(SchemaField[] raw) { + List result = new ArrayList<>(raw.length); + for (SchemaField f : raw) { + result.add(f.getName()); + } + return result; + } + + private static List toListOfStringDests(List raw) { + List result = new ArrayList<>(raw.size()); + for (CopyField f : raw) { + result.add(f.getDestination().getName()); + } + return result; + } + + // ////////////////////// SolrInfoMBeans methods ////////////////////// + + @Override + public String getDescription() { + return "Lucene Index Browser. Inspired and modeled after Luke: http://www.getopt.org/luke/"; + } + + @Override + public URL[] getDocs() { + try { + return new URL[] { new URL( + "http://wiki.apache.org/solr/LukeRequestHandler") }; + } catch (MalformedURLException ex) { + return null; + } + } + + // ///////////////////////////////////////////////////////////////////////////////////// + + static class TermHistogram { + int _maxBucket = -1; + int _buckets[] = new int[HIST_ARRAY_SIZE]; + + public void add(int[] buckets) { + for (int idx = 0; idx < buckets.length; ++idx) { + if (buckets[idx] != 0) + _maxBucket = idx; + } + for (int idx = 0; idx <= _maxBucket; ++idx) { + _buckets[idx] = buckets[idx]; + } + } + + // TODO? should this be a list or a map? + public NamedList toNamedList() { + NamedList nl = new NamedList<>(); + for (int bucket = 0; bucket <= _maxBucket; bucket++) { + nl.add("" + (1 << bucket), _buckets[bucket]); + } + return nl; + } + } + + /** + * Private internal class that counts up frequent terms + */ + private static class TopTermQueue extends PriorityQueue { + static class TermInfo { + TermInfo(Term t, int df) { + term = t; + docFreq = df; + } + + int docFreq; + Term term; + } + + public int minFreq = 0; + public int distinctTerms = 0; + public TermHistogram histogram; + + TopTermQueue(int size) { + super(size); + histogram = new TermHistogram(); + } + + @Override + protected final boolean lessThan(Object a, Object b) { + TermInfo termInfoA = (TermInfo) a; + TermInfo termInfoB = (TermInfo) b; + return termInfoA.docFreq < termInfoB.docFreq; + } + + /** + * This is a destructive call... the queue is empty at the end + */ + public NamedList toNamedList(IndexSchema schema) { + // reverse the list.. + List aslist = new LinkedList<>(); + while (size() > 0) { + aslist.add(0, (TermInfo) pop()); + } + + NamedList list = new NamedList<>(); + for (TermInfo i : aslist) { + String txt = i.term.text(); + SchemaField ft = schema.getFieldOrNull(i.term.field()); + if (ft != null) { + txt = ft.getType().indexedToReadable(txt); + } + list.add(txt, i.docFreq); + } + return list; + } + + public TermInfo getTopTermInfo() { + return (TermInfo) top(); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoQueryRequest.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoQueryRequest.java index 7bd150380..c9f5c6006 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoQueryRequest.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoQueryRequest.java @@ -1,77 +1,77 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; - -import java.util.Collection; -import java.util.Collections; - -import org.apache.solr.client.solrj.request.QueryRequest; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.ContentStream; - -/** - * - * - * @since solr 1.3 - */ -public class AlfrescoQueryRequest extends QueryRequest -{ - private ContentStream contentStream = null; - - public AlfrescoQueryRequest() - { - super(); - } - - public AlfrescoQueryRequest( SolrParams q ) - { - super(q); - } - - public AlfrescoQueryRequest( SolrParams q, METHOD method ) - { - super(q, method); - } - - - //--------------------------------------------------------------------------------- - //--------------------------------------------------------------------------------- - - @Override - public Collection getContentStreams() { - return contentStream == null ? null : Collections.singletonList(contentStream); - } - - /** - * @param contentStream the contentStream to set - */ -public void setContentStream(ContentStream contentStream) -{ - this.contentStream = contentStream; -} -} - +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; + +import java.util.Collection; +import java.util.Collections; + +import org.apache.solr.client.solrj.request.QueryRequest; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.ContentStream; + +/** + * + * + * @since solr 1.3 + */ +public class AlfrescoQueryRequest extends QueryRequest +{ + private ContentStream contentStream = null; + + public AlfrescoQueryRequest() + { + super(); + } + + public AlfrescoQueryRequest( SolrParams q ) + { + super(q); + } + + public AlfrescoQueryRequest( SolrParams q, METHOD method ) + { + super(q, method); + } + + + //--------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------- + + @Override + public Collection getContentStreams() { + return contentStream == null ? null : Collections.singletonList(contentStream); + } + + /** + * @param contentStream the contentStream to set + */ +public void setContentStream(ContentStream contentStream) +{ + this.contentStream = contentStream; +} +} + diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSearchHandler.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSearchHandler.java index 62f0341f7..6ed3d58d8 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSearchHandler.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSearchHandler.java @@ -1,715 +1,715 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; - -import static java.util.Optional.of; -import static java.util.Optional.ofNullable; -import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.FACET; -import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.FTS; -import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.ID; -import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.SORT; -import static org.apache.solr.common.params.CommonParams.PATH; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.io.Reader; -import java.io.StringWriter; -import java.lang.invoke.MethodHandles; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.query.AbstractQParser; -import org.apache.cxf.transport.http.auth.HttpAuthHeader; -import org.apache.lucene.document.Document; -import org.apache.lucene.index.ExitableDirectoryReader; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.util.Version; -import org.apache.solr.client.solrj.SolrServerException; -import org.apache.solr.common.SolrDocument; -import org.apache.solr.common.SolrDocumentList; -import org.apache.solr.common.SolrException; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.ContentStream; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.core.CloseHook; -import org.apache.solr.core.PluginInfo; -import org.apache.solr.core.SolrCore; -import org.apache.solr.handler.RequestHandlerBase; -import org.apache.solr.handler.component.FacetComponent.FacetContext; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.BasicResultContext; -import org.apache.solr.response.ResultContext; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocList; -import org.apache.solr.search.SolrQueryTimeoutImpl; -import org.apache.solr.search.SolrReturnFields; -import org.apache.solr.search.facet.FacetModule; -import org.apache.solr.util.RTimerTree; -import org.apache.solr.util.SolrPluginUtils; -import org.apache.solr.util.plugin.PluginInfoInitialized; -import org.apache.solr.util.plugin.SolrCoreAware; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.noggit.ObjectBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class AlfrescoSearchHandler extends RequestHandlerBase implements - SolrCoreAware, PluginInfoInitialized { - - static final String INIT_COMPONENTS = "components"; - static final String INIT_FIRST_COMPONENTS = "first-components"; - static final String INIT_LAST_COMPONENTS = "last-components"; - - private static final Logger log = LoggerFactory.getLogger(MethodHandles - .lookup().lookupClass()); - - protected volatile List components; - private ShardHandlerFactory shardHandlerFactory; - private PluginInfo shfInfo; - private SolrCore core; - - protected List getDefaultComponents() { - ArrayList names = new ArrayList<>(8); - names.add(QueryComponent.COMPONENT_NAME); - names.add(FacetComponent.COMPONENT_NAME); - names.add(FacetModule.COMPONENT_NAME); - names.add(MoreLikeThisComponent.COMPONENT_NAME); - names.add(HighlightComponent.COMPONENT_NAME); - names.add(StatsComponent.COMPONENT_NAME); - names.add(DebugComponent.COMPONENT_NAME); - names.add(ExpandComponent.COMPONENT_NAME); - return names; - } - - @Override - public void init(PluginInfo info) { - init(info.initArgs); - for (PluginInfo child : info.children) { - if ("shardHandlerFactory".equals(child.type)) { - this.shfInfo = child; - break; - } - } - } - - /** - * Initialize the components based on name. Note, if using - * INIT_FIRST_COMPONENTS or INIT_LAST_COMPONENTS, - * then the {@link DebugComponent} will always occur last. If this is not - * desired, then one must explicitly declare all components using the - * INIT_COMPONENTS syntax. - */ - @Override - @SuppressWarnings("unchecked") - public void inform(SolrCore core) { - this.core = core; - Set missing = new HashSet<>(); - List c = (List) initArgs.get(INIT_COMPONENTS); - missing.addAll(core.getSearchComponents().checkContains(c)); - List first = (List) initArgs.get(INIT_FIRST_COMPONENTS); - missing.addAll(core.getSearchComponents().checkContains(first)); - List last = (List) initArgs.get(INIT_LAST_COMPONENTS); - missing.addAll(core.getSearchComponents().checkContains(last)); - if (!missing.isEmpty()) - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, - "Missing SearchComponents named : " + missing); - if (c != null && (first != null || last != null)) - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, - "First/Last components only valid if you do not declare 'components'"); - - if (shfInfo == null) { - shardHandlerFactory = core.getCoreContainer().getShardHandlerFactory(); - } else { - shardHandlerFactory = core.createInitInstance(shfInfo, - ShardHandlerFactory.class, null, null); - core.addCloseHook(new CloseHook() { - @Override - public void preClose(SolrCore core) { - shardHandlerFactory.close(); - } - - @Override - public void postClose(SolrCore core) { - } - }); - } - - } - - private void initComponents() { - Object declaredComponents = initArgs.get(INIT_COMPONENTS); - List first = (List) initArgs.get(INIT_FIRST_COMPONENTS); - List last = (List) initArgs.get(INIT_LAST_COMPONENTS); - - List list = null; - boolean makeDebugLast = true; - if (declaredComponents == null) { - // Use the default component list - list = getDefaultComponents(); - - if (first != null) { - List clist = first; - clist.addAll(list); - list = clist; - } - - if (last != null) { - list.addAll(last); - } - } else { - list = (List) declaredComponents; - if (first != null || last != null) { - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, - "First/Last components only valid if you do not declare 'components'"); - } - makeDebugLast = false; - } - - // Build the component list - List components = new ArrayList<>(list.size()); - DebugComponent dbgCmp = null; - for (String c : list) { - SearchComponent comp = core.getSearchComponent(c); - if (comp instanceof DebugComponent && makeDebugLast == true) { - dbgCmp = (DebugComponent) comp; - } else { - components.add(comp); - log.debug("Adding component:" + comp); - } - } - if (makeDebugLast == true && dbgCmp != null) { - components.add(dbgCmp); - log.debug("Adding debug component:" + dbgCmp); - } - this.components = components; - } - - public List getComponents() { - List result = components; // volatile read - if (result == null) { - synchronized (this) { - if (components == null) { - initComponents(); - } - result = components; - } - } - return result; - } - - private void readJsonIntoContent(SolrQueryRequest req) throws IOException { - SolrParams solrParams = req.getParams(); - String jsonFacet = solrParams.get("json.facet"); - if(jsonFacet != null) { - Object o = ObjectBuilder.fromJSON(jsonFacet); - Map json = new HashMap(); - json.put("facet", o); - req.setJSON(json); - } - Iterable streams = req.getContentStreams(); - - JSONObject json = (JSONObject) req.getContext().get(AbstractQParser.ALFRESCO_JSON); - - if (json == null) { - if (streams != null) { - try { - Reader reader = null; - for (ContentStream stream : streams) { - reader = new BufferedReader(new InputStreamReader( - stream.getStream(), "UTF-8")); - } - - // TODO - replace with streaming-based solution e.g. - // SimpleJSON ContentHandler - if (reader != null) { - json = new JSONObject(new JSONTokener(reader)); - req.getContext().put(AbstractQParser.ALFRESCO_JSON, - json); - } - } catch (JSONException e) { - // This is expected when there is no json element to the - // request - } catch (IOException e) { - throw new AlfrescoRuntimeException( - "IO Error parsing query parameters", e); - } - } else if(req.getParams().get(AbstractQParser.ALFRESCO_JSON) != null) { - //json is in the params. - req.getContext().put(AbstractQParser.ALFRESCO_JSON, new JSONObject(req.getParams().get(AbstractQParser.ALFRESCO_JSON))); - } - } - } - - private ShardHandler getAndPrepShardHandler(SolrQueryRequest req, - ResponseBuilder rb) { - ShardHandler shardHandler = null; - - rb.isDistrib = req.getParams().getBool( - "distrib", - req.getCore().getCoreContainer().isZooKeeperAware()); - if (!rb.isDistrib) { - // for back compat, a shards param with URLs like - // localhost:8983/solr will mean that this - // search is distributed. - final String shards = req.getParams().get(ShardParams.SHARDS); - rb.isDistrib = ((shards != null) && (shards.indexOf('/') > 0)); - } - - if (rb.isDistrib) { - shardHandler = shardHandlerFactory.getShardHandler(); - shardHandler.prepDistributed(rb); - if (!rb.isDistrib) { - shardHandler = null; // request is not distributed after all and - // so the shard handler is not needed - } - } - - return shardHandler; - } - - - - - @Override - public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) - throws Exception { - readJsonIntoContent(req); - - - List components = getComponents(); - ResponseBuilder rb = new ResponseBuilder(req, rsp, components); - if (rb.requestInfo != null) { - rb.requestInfo.setResponseBuilder(rb); - } - - boolean dbg = req.getParams().getBool(CommonParams.DEBUG_QUERY, false); - rb.setDebug(dbg); - if (dbg == false) {// if it's true, we are doing everything anyway. - SolrPluginUtils.getDebugInterests( - req.getParams().getParams(CommonParams.DEBUG), rb); - } - - final RTimerTree timer = rb.isDebug() ? req.getRequestTimer() : null; - - final ShardHandler shardHandler1 = getAndPrepShardHandler(req, rb); // creates - // a - // ShardHandler - // object - // only - // if - // it's - // needed - - if (timer == null) { - // non-debugging prepare phase - - for (SearchComponent c : components) { - c.prepare(rb); - } - } else { - // debugging prepare phase - RTimerTree subt = timer.sub("prepare"); - for (SearchComponent c : components) { - rb.setTimer(subt.sub(c.getName())); - c.prepare(rb); - rb.getTimer().stop(); - } - subt.stop(); - } - - if (!rb.isDistrib) { - // a normal non-distributed request - - long timeAllowed = req.getParams().getLong( - CommonParams.TIME_ALLOWED, -1L); - if (timeAllowed > 0L) { - SolrQueryTimeoutImpl.set(timeAllowed); - } - try { - // The semantics of debugging vs not debugging are different - // enough that - // it makes sense to have two control loops - if (!rb.isDebug()) { - // Process - for (SearchComponent c : components) { - c.process(rb); - } - } else { - // Process - RTimerTree subt = timer.sub("process"); - for (SearchComponent c : components) { - rb.setTimer(subt.sub(c.getName())); - c.process(rb); - rb.getTimer().stop(); - } - subt.stop(); - - // add the timing info - if (rb.isDebugTimings()) { - rb.addDebugInfo("timing", timer.asNamedList()); - } - } - } catch (ExitableDirectoryReader.ExitingReaderException ex) { - log.warn("Query: " + req.getParamString() + "; " - + ex.getMessage()); - SolrDocumentList r = (SolrDocumentList) rb.rsp.getValues().get( - "response"); - if (r == null) - r = new SolrDocumentList(); - r.setNumFound(0); - rb.rsp.add("response", r); - if (rb.isDebug()) { - NamedList debug = new NamedList(); - debug.add("explain", new NamedList()); - rb.rsp.add("debug", debug); - } - rb.rsp.getResponseHeader().add("partialResults", Boolean.TRUE); - } finally { - SolrQueryTimeoutImpl.reset(); - } - - if (req.getParams().getBool("alfresco.getSolrDocumentList", false)) { - NamedList values = rsp.getValues(); - ResultContext response = (ResultContext) values.get("response"); - SolrDocumentList newResponse = new SolrDocumentList(); - DocList docs = response.getDocList(); - for (DocIterator it = docs.iterator(); it.hasNext(); /**/) { - newResponse.add(toSolrDocument( - req.getSearcher().doc(it.nextDoc()), - req.getSchema())); - } - values.add("responseSolrDocumentList", newResponse); - } - } else { - // a distributed request - - if (rb.outgoing == null) { - rb.outgoing = new LinkedList<>(); - } - rb.finished = new ArrayList<>(); - - int nextStage = 0; - do { - rb.stage = nextStage; - nextStage = ResponseBuilder.STAGE_DONE; - - // call all components - for (SearchComponent c : components) { - // the next stage is the minimum of what all components - // report - nextStage = Math.min(nextStage, c.distributedProcess(rb)); - } - - // check the outgoing queue and send requests - while (rb.outgoing.size() > 0) { - - // submit all current request tasks at once - while (rb.outgoing.size() > 0) { - ShardRequest sreq = rb.outgoing.remove(0); - sreq.actualShards = sreq.shards; - if (sreq.actualShards == ShardRequest.ALL_SHARDS) { - sreq.actualShards = rb.shards; - } - sreq.responses = new ArrayList<>( - sreq.actualShards.length); // presume we'll get - // a response from - // each shard we - // send to - - // TODO: map from shard to address[] - for (String shard : sreq.actualShards) { - ModifiableSolrParams params = new ModifiableSolrParams( - sreq.params); - params.remove(ShardParams.SHARDS); // not a - // top-level - // request - params.set(CommonParams.DISTRIB, "false"); // not a - // top-level - // request - params.remove("indent"); - params.remove(CommonParams.HEADER_ECHO_PARAMS); - params.set(ShardParams.IS_SHARD, true); // a sub - // (shard) - // request - params.set(ShardParams.SHARDS_PURPOSE, sreq.purpose); - params.set(ShardParams.SHARD_URL, shard); // so the - // shard - // knows - // what - // was - // asked - if (req.getContext().get(AbstractQParser.ALFRESCO_JSON) != null) { - //This will add the Alfresco JSON as a parameter, overwriting the parameter if it already exists. - params.set(AbstractQParser.ALFRESCO_JSON, - req.getContext().get(AbstractQParser.ALFRESCO_JSON).toString()); - } - if (rb.requestInfo != null) { - // we could try and detect when this is needed, - // but it could be tricky - params.set("NOW", Long.toString(rb.requestInfo - .getNOW().getTime())); - } - String shardQt = params.get(ShardParams.SHARDS_QT); - if (shardQt != null) { - params.set(CommonParams.QT, shardQt); - } else { - // for distributed queries that don't include - // shards.qt, use the original path - // as the default but operators need to update - // their luceneMatchVersion to enable - // this behavior since it did not work this way - // prior to 5.1 - if (req.getCore().getSolrConfig().luceneMatchVersion - .onOrAfter(Version.LATEST)) { - String reqPath = (String) req.getContext() - .get(PATH); - if (!"/select".equals(reqPath)) { - params.set(CommonParams.QT, reqPath); - } // else if path is /select, then the qt - // gets passed thru if set - } else { - // this is the pre-5.1 behavior, which - // translates to sending the shard request - // to /select - params.remove(CommonParams.QT); - } - } - shardHandler1.submit(sreq, shard, params); - } - } - - // now wait for replies, but if anyone puts more requests on - // the outgoing queue, send them out immediately (by exiting - // this loop) - boolean tolerant = rb.req.getParams().getBool( - ShardParams.SHARDS_TOLERANT, false); - while (rb.outgoing.size() == 0) { - ShardResponse srsp = tolerant ? shardHandler1 - .takeCompletedIncludingErrors() : shardHandler1 - .takeCompletedOrError(); - if (srsp == null) - break; // no more requests to wait for - - // Was there an exception? - if (srsp.getException() != null) { - // If things are not tolerant, abort everything and - // rethrow - if (!tolerant) { - shardHandler1.cancelAll(); - if (srsp.getException() instanceof SolrException) { - throw (SolrException) srsp.getException(); - } else { - throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, - srsp.getException()); - } - } else { - if (rsp.getResponseHeader().get( - "partialResults") == null) { - rsp.getResponseHeader().add( - "partialResults", Boolean.TRUE); - } - } - } - - rb.finished.add(srsp.getShardRequest()); - - // let the components see the responses to the request - for (SearchComponent c : components) { - c.handleResponses(rb, srsp.getShardRequest()); - } - } - } - - for (SearchComponent c : components) { - c.finishStage(rb); - } - - // we are done when the next stage is MAX_VALUE - } while (nextStage != Integer.MAX_VALUE); - } - - // SOLR-5550: still provide shards.info if requested even for a short - // circuited distrib request - if (!rb.isDistrib - && req.getParams().getBool(ShardParams.SHARDS_INFO, false) - && rb.shortCircuitedURL != null) { - NamedList shardInfo = new SimpleOrderedMap(); - SimpleOrderedMap nl = new SimpleOrderedMap(); - if (rsp.getException() != null) { - Throwable cause = rsp.getException(); - if (cause instanceof SolrServerException) { - cause = ((SolrServerException) cause).getRootCause(); - } else { - if (cause.getCause() != null) { - cause = cause.getCause(); - } - } - nl.add("error", cause.toString()); - StringWriter trace = new StringWriter(); - cause.printStackTrace(new PrintWriter(trace)); - nl.add("trace", trace.toString()); - } else { - nl.add("numFound", rb.getResults().docList.matches()); - nl.add("maxScore", rb.getResults().docList.maxScore()); - } - nl.add("shardAddress", rb.shortCircuitedURL); - nl.add("time", req.getRequestTimer().getTime()); // elapsed time of - // this request - // so far - - int pos = rb.shortCircuitedURL.indexOf("://"); - String shardInfoName = pos != -1 ? rb.shortCircuitedURL - .substring(pos + 3) : rb.shortCircuitedURL; - shardInfo.add(shardInfoName, nl); - rsp.getValues().add(ShardParams.SHARDS_INFO, shardInfo); - } - - removeFacetQueriesWithCountZero(rsp); - - } - - /** - * Facet SOLR Queries are omitting facet results with count equals to 0 as general rule. - * SOLR Queries executed on a Shard environment, don't include these results but the same - * query executed on a single server environment are adding these elements to the response. - * This method removes every facet query having count equals to 0 to provide the same - * behaviour in both cases. - * - * A query like the following, will return only Facet Queries having elements: - * "facetQueries" : [ - * { "query" : "content.size:[0 TO 102400]", "label" : "small"}, - * { "query" : "content.size:[102400 TO 1048576]", "label" : "medium"}, - * { "query" : "content.size:[1048576 TO 16777216]", "label" : "large"} - * ] - * - * For instance, if there are only results with "small" key, the result will be: - * "facetQueries": [ - * { - * "label": "small", - * "filterQuery": "content.size:[0 TO 102400]", - * "count": 5 - * } - * ] - * - */ - public static final String FACET_COUNTS_KEY = "facet_counts"; - public static final String FACET_CONTEXT_KEY = "_facet.context"; - - @SuppressWarnings("unchecked") - public static void removeFacetQueriesWithCountZero(SolrQueryResponse rsp) - { - NamedList facetCounts = (NamedList) rsp.getValues().get(FACET_COUNTS_KEY); - if (facetCounts != null) - { - NamedList facetQueries = (NamedList) facetCounts.get(FacetComponent.FACET_QUERY_KEY); - if (facetQueries != null) - { - List keyCountsToRemove = new ArrayList<>(); - - facetQueries.forEach(facetQuery -> { - if ((Integer) facetQuery.getValue() == 0) - { - keyCountsToRemove.add(facetQuery.getKey()); - } - }); - - if (!keyCountsToRemove.isEmpty()) - { - keyCountsToRemove.forEach(key -> facetQueries.remove(key)); - - ((NamedList) rsp.getValues().get(FACET_COUNTS_KEY)).remove(FacetComponent.FACET_QUERY_KEY); - ((NamedList) rsp.getValues().get(FACET_COUNTS_KEY)).add(FacetComponent.FACET_QUERY_KEY, - facetQueries); - - BasicResultContext result = (BasicResultContext) rsp.getResponse(); - FacetContext facetContext = (FacetContext) result.getRequest().getContext() - .get(FACET_CONTEXT_KEY); - facetContext.getAllQueryFacets() - .removeIf(queryFacet -> keyCountsToRemove.contains(queryFacet.getKey())); - result.getRequest().getContext().put(FACET_CONTEXT_KEY, facetContext); - - log.debug("In SOLR query '" + result.getRequest() + "', Facet Queries results having labels " - + keyCountsToRemove + " have been removed from results"); - } - } - } - } - - // ////////////////////// SolrInfoMBeans methods ////////////////////// - - @Override - public String getDescription() { - StringBuilder sb = new StringBuilder(); - sb.append("Search using components: "); - if (components != null) { - for (SearchComponent c : components) { - sb.append(c.getName()); - sb.append(","); - } - } - return sb.toString(); - } - - public final SolrDocument toSolrDocument(Document doc, IndexSchema schema) { - SolrDocument out = new SolrDocument(); - for (IndexableField f : doc) { - // Make sure multivalued fields are represented as lists - Object existing = out.get(f.name()); - if (existing == null) { - SchemaField sf = schema.getFieldOrNull(f.name()); - if (sf != null && sf.multiValued()) { - List vals = new ArrayList<>(); - vals.add(f); - out.setField(f.name(), vals); - } else { - out.setField(f.name(), f); - } - } else { - out.addField(f.name(), f); - } - } - return out; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; + +import static java.util.Optional.of; +import static java.util.Optional.ofNullable; +import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.FACET; +import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.FTS; +import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.ID; +import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.SORT; +import static org.apache.solr.common.params.CommonParams.PATH; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringWriter; +import java.lang.invoke.MethodHandles; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.query.AbstractQParser; +import org.apache.cxf.transport.http.auth.HttpAuthHeader; +import org.apache.lucene.document.Document; +import org.apache.lucene.index.ExitableDirectoryReader; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.util.Version; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.ContentStream; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.CloseHook; +import org.apache.solr.core.PluginInfo; +import org.apache.solr.core.SolrCore; +import org.apache.solr.handler.RequestHandlerBase; +import org.apache.solr.handler.component.FacetComponent.FacetContext; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.apache.solr.response.ResultContext; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrQueryTimeoutImpl; +import org.apache.solr.search.SolrReturnFields; +import org.apache.solr.search.facet.FacetModule; +import org.apache.solr.util.RTimerTree; +import org.apache.solr.util.SolrPluginUtils; +import org.apache.solr.util.plugin.PluginInfoInitialized; +import org.apache.solr.util.plugin.SolrCoreAware; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.noggit.ObjectBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class AlfrescoSearchHandler extends RequestHandlerBase implements + SolrCoreAware, PluginInfoInitialized { + + static final String INIT_COMPONENTS = "components"; + static final String INIT_FIRST_COMPONENTS = "first-components"; + static final String INIT_LAST_COMPONENTS = "last-components"; + + private static final Logger log = LoggerFactory.getLogger(MethodHandles + .lookup().lookupClass()); + + protected volatile List components; + private ShardHandlerFactory shardHandlerFactory; + private PluginInfo shfInfo; + private SolrCore core; + + protected List getDefaultComponents() { + ArrayList names = new ArrayList<>(8); + names.add(QueryComponent.COMPONENT_NAME); + names.add(FacetComponent.COMPONENT_NAME); + names.add(FacetModule.COMPONENT_NAME); + names.add(MoreLikeThisComponent.COMPONENT_NAME); + names.add(HighlightComponent.COMPONENT_NAME); + names.add(StatsComponent.COMPONENT_NAME); + names.add(DebugComponent.COMPONENT_NAME); + names.add(ExpandComponent.COMPONENT_NAME); + return names; + } + + @Override + public void init(PluginInfo info) { + init(info.initArgs); + for (PluginInfo child : info.children) { + if ("shardHandlerFactory".equals(child.type)) { + this.shfInfo = child; + break; + } + } + } + + /** + * Initialize the components based on name. Note, if using + * INIT_FIRST_COMPONENTS or INIT_LAST_COMPONENTS, + * then the {@link DebugComponent} will always occur last. If this is not + * desired, then one must explicitly declare all components using the + * INIT_COMPONENTS syntax. + */ + @Override + @SuppressWarnings("unchecked") + public void inform(SolrCore core) { + this.core = core; + Set missing = new HashSet<>(); + List c = (List) initArgs.get(INIT_COMPONENTS); + missing.addAll(core.getSearchComponents().checkContains(c)); + List first = (List) initArgs.get(INIT_FIRST_COMPONENTS); + missing.addAll(core.getSearchComponents().checkContains(first)); + List last = (List) initArgs.get(INIT_LAST_COMPONENTS); + missing.addAll(core.getSearchComponents().checkContains(last)); + if (!missing.isEmpty()) + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, + "Missing SearchComponents named : " + missing); + if (c != null && (first != null || last != null)) + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, + "First/Last components only valid if you do not declare 'components'"); + + if (shfInfo == null) { + shardHandlerFactory = core.getCoreContainer().getShardHandlerFactory(); + } else { + shardHandlerFactory = core.createInitInstance(shfInfo, + ShardHandlerFactory.class, null, null); + core.addCloseHook(new CloseHook() { + @Override + public void preClose(SolrCore core) { + shardHandlerFactory.close(); + } + + @Override + public void postClose(SolrCore core) { + } + }); + } + + } + + private void initComponents() { + Object declaredComponents = initArgs.get(INIT_COMPONENTS); + List first = (List) initArgs.get(INIT_FIRST_COMPONENTS); + List last = (List) initArgs.get(INIT_LAST_COMPONENTS); + + List list = null; + boolean makeDebugLast = true; + if (declaredComponents == null) { + // Use the default component list + list = getDefaultComponents(); + + if (first != null) { + List clist = first; + clist.addAll(list); + list = clist; + } + + if (last != null) { + list.addAll(last); + } + } else { + list = (List) declaredComponents; + if (first != null || last != null) { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, + "First/Last components only valid if you do not declare 'components'"); + } + makeDebugLast = false; + } + + // Build the component list + List components = new ArrayList<>(list.size()); + DebugComponent dbgCmp = null; + for (String c : list) { + SearchComponent comp = core.getSearchComponent(c); + if (comp instanceof DebugComponent && makeDebugLast == true) { + dbgCmp = (DebugComponent) comp; + } else { + components.add(comp); + log.debug("Adding component:" + comp); + } + } + if (makeDebugLast == true && dbgCmp != null) { + components.add(dbgCmp); + log.debug("Adding debug component:" + dbgCmp); + } + this.components = components; + } + + public List getComponents() { + List result = components; // volatile read + if (result == null) { + synchronized (this) { + if (components == null) { + initComponents(); + } + result = components; + } + } + return result; + } + + private void readJsonIntoContent(SolrQueryRequest req) throws IOException { + SolrParams solrParams = req.getParams(); + String jsonFacet = solrParams.get("json.facet"); + if(jsonFacet != null) { + Object o = ObjectBuilder.fromJSON(jsonFacet); + Map json = new HashMap(); + json.put("facet", o); + req.setJSON(json); + } + Iterable streams = req.getContentStreams(); + + JSONObject json = (JSONObject) req.getContext().get(AbstractQParser.ALFRESCO_JSON); + + if (json == null) { + if (streams != null) { + try { + Reader reader = null; + for (ContentStream stream : streams) { + reader = new BufferedReader(new InputStreamReader( + stream.getStream(), "UTF-8")); + } + + // TODO - replace with streaming-based solution e.g. + // SimpleJSON ContentHandler + if (reader != null) { + json = new JSONObject(new JSONTokener(reader)); + req.getContext().put(AbstractQParser.ALFRESCO_JSON, + json); + } + } catch (JSONException e) { + // This is expected when there is no json element to the + // request + } catch (IOException e) { + throw new AlfrescoRuntimeException( + "IO Error parsing query parameters", e); + } + } else if(req.getParams().get(AbstractQParser.ALFRESCO_JSON) != null) { + //json is in the params. + req.getContext().put(AbstractQParser.ALFRESCO_JSON, new JSONObject(req.getParams().get(AbstractQParser.ALFRESCO_JSON))); + } + } + } + + private ShardHandler getAndPrepShardHandler(SolrQueryRequest req, + ResponseBuilder rb) { + ShardHandler shardHandler = null; + + rb.isDistrib = req.getParams().getBool( + "distrib", + req.getCore().getCoreContainer().isZooKeeperAware()); + if (!rb.isDistrib) { + // for back compat, a shards param with URLs like + // localhost:8983/solr will mean that this + // search is distributed. + final String shards = req.getParams().get(ShardParams.SHARDS); + rb.isDistrib = ((shards != null) && (shards.indexOf('/') > 0)); + } + + if (rb.isDistrib) { + shardHandler = shardHandlerFactory.getShardHandler(); + shardHandler.prepDistributed(rb); + if (!rb.isDistrib) { + shardHandler = null; // request is not distributed after all and + // so the shard handler is not needed + } + } + + return shardHandler; + } + + + + + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) + throws Exception { + readJsonIntoContent(req); + + + List components = getComponents(); + ResponseBuilder rb = new ResponseBuilder(req, rsp, components); + if (rb.requestInfo != null) { + rb.requestInfo.setResponseBuilder(rb); + } + + boolean dbg = req.getParams().getBool(CommonParams.DEBUG_QUERY, false); + rb.setDebug(dbg); + if (dbg == false) {// if it's true, we are doing everything anyway. + SolrPluginUtils.getDebugInterests( + req.getParams().getParams(CommonParams.DEBUG), rb); + } + + final RTimerTree timer = rb.isDebug() ? req.getRequestTimer() : null; + + final ShardHandler shardHandler1 = getAndPrepShardHandler(req, rb); // creates + // a + // ShardHandler + // object + // only + // if + // it's + // needed + + if (timer == null) { + // non-debugging prepare phase + + for (SearchComponent c : components) { + c.prepare(rb); + } + } else { + // debugging prepare phase + RTimerTree subt = timer.sub("prepare"); + for (SearchComponent c : components) { + rb.setTimer(subt.sub(c.getName())); + c.prepare(rb); + rb.getTimer().stop(); + } + subt.stop(); + } + + if (!rb.isDistrib) { + // a normal non-distributed request + + long timeAllowed = req.getParams().getLong( + CommonParams.TIME_ALLOWED, -1L); + if (timeAllowed > 0L) { + SolrQueryTimeoutImpl.set(timeAllowed); + } + try { + // The semantics of debugging vs not debugging are different + // enough that + // it makes sense to have two control loops + if (!rb.isDebug()) { + // Process + for (SearchComponent c : components) { + c.process(rb); + } + } else { + // Process + RTimerTree subt = timer.sub("process"); + for (SearchComponent c : components) { + rb.setTimer(subt.sub(c.getName())); + c.process(rb); + rb.getTimer().stop(); + } + subt.stop(); + + // add the timing info + if (rb.isDebugTimings()) { + rb.addDebugInfo("timing", timer.asNamedList()); + } + } + } catch (ExitableDirectoryReader.ExitingReaderException ex) { + log.warn("Query: " + req.getParamString() + "; " + + ex.getMessage()); + SolrDocumentList r = (SolrDocumentList) rb.rsp.getValues().get( + "response"); + if (r == null) + r = new SolrDocumentList(); + r.setNumFound(0); + rb.rsp.add("response", r); + if (rb.isDebug()) { + NamedList debug = new NamedList(); + debug.add("explain", new NamedList()); + rb.rsp.add("debug", debug); + } + rb.rsp.getResponseHeader().add("partialResults", Boolean.TRUE); + } finally { + SolrQueryTimeoutImpl.reset(); + } + + if (req.getParams().getBool("alfresco.getSolrDocumentList", false)) { + NamedList values = rsp.getValues(); + ResultContext response = (ResultContext) values.get("response"); + SolrDocumentList newResponse = new SolrDocumentList(); + DocList docs = response.getDocList(); + for (DocIterator it = docs.iterator(); it.hasNext(); /**/) { + newResponse.add(toSolrDocument( + req.getSearcher().doc(it.nextDoc()), + req.getSchema())); + } + values.add("responseSolrDocumentList", newResponse); + } + } else { + // a distributed request + + if (rb.outgoing == null) { + rb.outgoing = new LinkedList<>(); + } + rb.finished = new ArrayList<>(); + + int nextStage = 0; + do { + rb.stage = nextStage; + nextStage = ResponseBuilder.STAGE_DONE; + + // call all components + for (SearchComponent c : components) { + // the next stage is the minimum of what all components + // report + nextStage = Math.min(nextStage, c.distributedProcess(rb)); + } + + // check the outgoing queue and send requests + while (rb.outgoing.size() > 0) { + + // submit all current request tasks at once + while (rb.outgoing.size() > 0) { + ShardRequest sreq = rb.outgoing.remove(0); + sreq.actualShards = sreq.shards; + if (sreq.actualShards == ShardRequest.ALL_SHARDS) { + sreq.actualShards = rb.shards; + } + sreq.responses = new ArrayList<>( + sreq.actualShards.length); // presume we'll get + // a response from + // each shard we + // send to + + // TODO: map from shard to address[] + for (String shard : sreq.actualShards) { + ModifiableSolrParams params = new ModifiableSolrParams( + sreq.params); + params.remove(ShardParams.SHARDS); // not a + // top-level + // request + params.set(CommonParams.DISTRIB, "false"); // not a + // top-level + // request + params.remove("indent"); + params.remove(CommonParams.HEADER_ECHO_PARAMS); + params.set(ShardParams.IS_SHARD, true); // a sub + // (shard) + // request + params.set(ShardParams.SHARDS_PURPOSE, sreq.purpose); + params.set(ShardParams.SHARD_URL, shard); // so the + // shard + // knows + // what + // was + // asked + if (req.getContext().get(AbstractQParser.ALFRESCO_JSON) != null) { + //This will add the Alfresco JSON as a parameter, overwriting the parameter if it already exists. + params.set(AbstractQParser.ALFRESCO_JSON, + req.getContext().get(AbstractQParser.ALFRESCO_JSON).toString()); + } + if (rb.requestInfo != null) { + // we could try and detect when this is needed, + // but it could be tricky + params.set("NOW", Long.toString(rb.requestInfo + .getNOW().getTime())); + } + String shardQt = params.get(ShardParams.SHARDS_QT); + if (shardQt != null) { + params.set(CommonParams.QT, shardQt); + } else { + // for distributed queries that don't include + // shards.qt, use the original path + // as the default but operators need to update + // their luceneMatchVersion to enable + // this behavior since it did not work this way + // prior to 5.1 + if (req.getCore().getSolrConfig().luceneMatchVersion + .onOrAfter(Version.LATEST)) { + String reqPath = (String) req.getContext() + .get(PATH); + if (!"/select".equals(reqPath)) { + params.set(CommonParams.QT, reqPath); + } // else if path is /select, then the qt + // gets passed thru if set + } else { + // this is the pre-5.1 behavior, which + // translates to sending the shard request + // to /select + params.remove(CommonParams.QT); + } + } + shardHandler1.submit(sreq, shard, params); + } + } + + // now wait for replies, but if anyone puts more requests on + // the outgoing queue, send them out immediately (by exiting + // this loop) + boolean tolerant = rb.req.getParams().getBool( + ShardParams.SHARDS_TOLERANT, false); + while (rb.outgoing.size() == 0) { + ShardResponse srsp = tolerant ? shardHandler1 + .takeCompletedIncludingErrors() : shardHandler1 + .takeCompletedOrError(); + if (srsp == null) + break; // no more requests to wait for + + // Was there an exception? + if (srsp.getException() != null) { + // If things are not tolerant, abort everything and + // rethrow + if (!tolerant) { + shardHandler1.cancelAll(); + if (srsp.getException() instanceof SolrException) { + throw (SolrException) srsp.getException(); + } else { + throw new SolrException( + SolrException.ErrorCode.SERVER_ERROR, + srsp.getException()); + } + } else { + if (rsp.getResponseHeader().get( + "partialResults") == null) { + rsp.getResponseHeader().add( + "partialResults", Boolean.TRUE); + } + } + } + + rb.finished.add(srsp.getShardRequest()); + + // let the components see the responses to the request + for (SearchComponent c : components) { + c.handleResponses(rb, srsp.getShardRequest()); + } + } + } + + for (SearchComponent c : components) { + c.finishStage(rb); + } + + // we are done when the next stage is MAX_VALUE + } while (nextStage != Integer.MAX_VALUE); + } + + // SOLR-5550: still provide shards.info if requested even for a short + // circuited distrib request + if (!rb.isDistrib + && req.getParams().getBool(ShardParams.SHARDS_INFO, false) + && rb.shortCircuitedURL != null) { + NamedList shardInfo = new SimpleOrderedMap(); + SimpleOrderedMap nl = new SimpleOrderedMap(); + if (rsp.getException() != null) { + Throwable cause = rsp.getException(); + if (cause instanceof SolrServerException) { + cause = ((SolrServerException) cause).getRootCause(); + } else { + if (cause.getCause() != null) { + cause = cause.getCause(); + } + } + nl.add("error", cause.toString()); + StringWriter trace = new StringWriter(); + cause.printStackTrace(new PrintWriter(trace)); + nl.add("trace", trace.toString()); + } else { + nl.add("numFound", rb.getResults().docList.matches()); + nl.add("maxScore", rb.getResults().docList.maxScore()); + } + nl.add("shardAddress", rb.shortCircuitedURL); + nl.add("time", req.getRequestTimer().getTime()); // elapsed time of + // this request + // so far + + int pos = rb.shortCircuitedURL.indexOf("://"); + String shardInfoName = pos != -1 ? rb.shortCircuitedURL + .substring(pos + 3) : rb.shortCircuitedURL; + shardInfo.add(shardInfoName, nl); + rsp.getValues().add(ShardParams.SHARDS_INFO, shardInfo); + } + + removeFacetQueriesWithCountZero(rsp); + + } + + /** + * Facet SOLR Queries are omitting facet results with count equals to 0 as general rule. + * SOLR Queries executed on a Shard environment, don't include these results but the same + * query executed on a single server environment are adding these elements to the response. + * This method removes every facet query having count equals to 0 to provide the same + * behaviour in both cases. + * + * A query like the following, will return only Facet Queries having elements: + * "facetQueries" : [ + * { "query" : "content.size:[0 TO 102400]", "label" : "small"}, + * { "query" : "content.size:[102400 TO 1048576]", "label" : "medium"}, + * { "query" : "content.size:[1048576 TO 16777216]", "label" : "large"} + * ] + * + * For instance, if there are only results with "small" key, the result will be: + * "facetQueries": [ + * { + * "label": "small", + * "filterQuery": "content.size:[0 TO 102400]", + * "count": 5 + * } + * ] + * + */ + public static final String FACET_COUNTS_KEY = "facet_counts"; + public static final String FACET_CONTEXT_KEY = "_facet.context"; + + @SuppressWarnings("unchecked") + public static void removeFacetQueriesWithCountZero(SolrQueryResponse rsp) + { + NamedList facetCounts = (NamedList) rsp.getValues().get(FACET_COUNTS_KEY); + if (facetCounts != null) + { + NamedList facetQueries = (NamedList) facetCounts.get(FacetComponent.FACET_QUERY_KEY); + if (facetQueries != null) + { + List keyCountsToRemove = new ArrayList<>(); + + facetQueries.forEach(facetQuery -> { + if ((Integer) facetQuery.getValue() == 0) + { + keyCountsToRemove.add(facetQuery.getKey()); + } + }); + + if (!keyCountsToRemove.isEmpty()) + { + keyCountsToRemove.forEach(key -> facetQueries.remove(key)); + + ((NamedList) rsp.getValues().get(FACET_COUNTS_KEY)).remove(FacetComponent.FACET_QUERY_KEY); + ((NamedList) rsp.getValues().get(FACET_COUNTS_KEY)).add(FacetComponent.FACET_QUERY_KEY, + facetQueries); + + BasicResultContext result = (BasicResultContext) rsp.getResponse(); + FacetContext facetContext = (FacetContext) result.getRequest().getContext() + .get(FACET_CONTEXT_KEY); + facetContext.getAllQueryFacets() + .removeIf(queryFacet -> keyCountsToRemove.contains(queryFacet.getKey())); + result.getRequest().getContext().put(FACET_CONTEXT_KEY, facetContext); + + log.debug("In SOLR query '" + result.getRequest() + "', Facet Queries results having labels " + + keyCountsToRemove + " have been removed from results"); + } + } + } + } + + // ////////////////////// SolrInfoMBeans methods ////////////////////// + + @Override + public String getDescription() { + StringBuilder sb = new StringBuilder(); + sb.append("Search using components: "); + if (components != null) { + for (SearchComponent c : components) { + sb.append(c.getName()); + sb.append(","); + } + } + return sb.toString(); + } + + public final SolrDocument toSolrDocument(Document doc, IndexSchema schema) { + SolrDocument out = new SolrDocument(); + for (IndexableField f : doc) { + // Make sure multivalued fields are represented as lists + Object existing = out.get(f.name()); + if (existing == null) { + SchemaField sf = schema.getFieldOrNull(f.name()); + if (sf != null && sf.multiValued()) { + List vals = new ArrayList<>(); + vals.add(f); + out.setField(f.name(), vals); + } else { + out.setField(f.name(), f); + } + } else { + out.addField(f.name(), f); + } + } + return out; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSolrHighlighter.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSolrHighlighter.java index 360de7842..7ab0ea24f 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSolrHighlighter.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSolrHighlighter.java @@ -1,462 +1,462 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; - -import static java.lang.String.join; -import static java.util.Arrays.stream; -import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.document.Document; -import org.apache.lucene.index.Terms; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.highlight.Highlighter; -import org.apache.lucene.search.highlight.QueryScorer; -import org.apache.lucene.search.highlight.WeightedSpanTerm; -import org.apache.lucene.search.highlight.WeightedSpanTermExtractor; -import org.apache.solr.common.params.HighlightParams; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.core.SolrCore; -import org.apache.solr.highlight.DefaultSolrHighlighter; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.DocList; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.util.plugin.PluginInfoInitialized; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.StreamSupport; - -/** - * The Alfresco customisation of the original (or somewhere called default) Solr highlighter. - * In this latest revision, the core Highlighting logic has been delegated to the Solr highlighter. - * The main difference introduced in this customisation is related with the fields mappings (i.e. mappings between - * the Alfresco and Solr fields names). - * - * The purpose of the delegation to the built-in Solr logic is to allow (later) an easier refactoring/removal. - * Specifically, when the mapping logic will be extracted in an external (calling) layer this component will be removed - * in favour of the built-in Solr (Default) Highlighter. - * - * The {@link #doHighlighting(DocList, Query, SolrQueryRequest, String[])} core method, which has been overriden, - * provides the required mapping between the requested highlighting fields in the hl.fl parameters (which follow the - * Alfresco semantic) and the Solr fields we have in the schema. - * - * At the end of the highlighting process the same mappings are used for doing the reverse process (replace the Solr - * fields with the original requested fields). - * - * Additionally, since the Solr ID has no meaning outside Solr, the Highlighter adds the DBID to each highlighting - * snippets: - * - *
- *  <lst name="_DEFAULT_!8000016f66a1a298!8000016f66a1a29e">
- *      <str name="DBID">1577974866590</str>
- *      <arr name="name">
- *          <str>some very <em>long</em> name</str>
- *      </arr>
- *      <arr name="title">
- *          <str>This the <em>long</em> french version of of the</str>
- *          <str>This the <em>long</em> english version of the</str>
- *      </arr>
- *  </lst>
- * 
- * - * @see SEARCH-2033 - */ -public class AlfrescoSolrHighlighter extends DefaultSolrHighlighter implements PluginInfoInitialized -{ - private static final Logger LOGGER = LoggerFactory.getLogger(AlfrescoSolrHighlighter.class); - - private static class DocumentIdentifiers - { - final String solrId; - final String dbid; - - private DocumentIdentifiers(String solrId, String dbid) - { - this.solrId = solrId; - this.dbid = dbid; - } - - String dbid() - { - return dbid; - } - - @Override - public boolean equals(Object obj) - { - return obj instanceof DocumentIdentifiers - && ((DocumentIdentifiers)obj).solrId.equals(solrId); - } - - @Override - public int hashCode() - { - return Objects.hash(solrId); - } - } - - private final Predicate> notNullAndNotEmpty = response -> response != null && response.size() > 0; - - public AlfrescoSolrHighlighter(SolrCore core) - { - super(core); - } - - @Override - protected Highlighter getHighlighter(Query query, String requestFieldname, SolrQueryRequest request) - { - String schemaFieldName = - AlfrescoSolrDataModel.getInstance() - .mapProperty(requestFieldname, FieldUse.HIGHLIGHT, request); - - Highlighter highlighter = - new Highlighter( - getFormatter(requestFieldname, request.getParams()), - getEncoder(requestFieldname, request.getParams()), - getQueryScorer(query,schemaFieldName, request)); - - highlighter.setTextFragmenter(getFragmenter(requestFieldname, request.getParams())); - return highlighter; - } - - /** - * We need to override this method because here Solr manages the {@link HighlightParams#FIELD_MATCH} parameter. - * In the last iteration, we improved the Solr schema by using one single stored field (used in this context for the - * highlighting) which is copied across multiple search fields (e.g. text@s__t,text@s_lt,mltext@m__lt). - * - * The input requestFieldName parameter is the stored field mentioned above; in case {@link HighlightParams#FIELD_MATCH} - * is set to true only query terms aligning with the field being highlighted will in turn be highlighted. - * - * The reverse field mapping you'll find in this method is because the stored field is not indexed, and therefore - * there won't be any "alignment" as described above, never. - * - * So starting from the stored field we need to query {@link AlfrescoSolrDataModel} for retrieving the corresponding - * cross-locale field (text|mltext@m|s__t@) and inject it into the {@link QueryScorer} that is in charge to score only - * those terms that contributed in generating the 'hit' on the document. - * - * TODO: this is one thing that can be improved together with the multilanguage improvement/implementation tasks - * - * @see SEARCH-1665 - * @see SEARCH-2056 - */ - @Override - protected QueryScorer getSpanQueryScorer(Query query, String requestFieldname, TokenStream tokenStream, SolrQueryRequest request) - { - String localFieldName = requestFieldname.substring(requestFieldname.lastIndexOf("}") + 1); - String schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty(localFieldName, FieldUse.FTS, request); - if (!schemaFieldName.contains("_t@{")) - { - schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty(localFieldName, FieldUse.FTS, request, 1); - } - - - // The query scorer purpose is to give a score to the text fragments by the number of unique query terms found. - // - // Fields that use the Alfresco MLAnalyser have a locale marker at the beginning of each token. For example, - // the text "My name is Gazza" becomes (locale = en): - // - // {en}my {en}name {en}is {en}gazza - // - // while this is not an issue for searching, those tokens never find a match during the highlighting because - // my != {en}my - // - // The purpose of this custom query scorer is to replace the collected weighted terms with their corresponding - // un-localised version. - QueryScorer scorer = new QueryScorer(query,request.getParams().getFieldBool(requestFieldname, HighlightParams.FIELD_MATCH, false) ? schemaFieldName : null) - { - @Override - protected WeightedSpanTermExtractor newTermExtractor(String defaultField) - { - return new WeightedSpanTermExtractor(defaultField) - { - @Override - protected void extractWeightedTerms(Map terms, Query query, float boost) throws IOException - { - super.extractWeightedTerms(terms, query, boost); - List termsWithoutLocale = - terms.values() - .stream() - .peek(term -> term.setTerm(withoutLocalePrefixMarker(term.getTerm()))) - .collect(toList()); - - terms.clear(); - termsWithoutLocale.forEach(term -> terms.put(term.getTerm(), term)); - } - }; - } - - /** - * Removes the locale marker from the given text. - * - * @param text the input text. - * @return the text without the beginning locale marker, or the same text is the marker cannot be found. - */ - private String withoutLocalePrefixMarker(String text) - { - if (text == null) return null; - - int startIndexOfMarker = text.indexOf("{"); - if (startIndexOfMarker == 0) - { - int endIndexOfMarker = text.indexOf("}"); - if (endIndexOfMarker != -1 && text.length() > (endIndexOfMarker + 1)) - { - return text.substring(endIndexOfMarker + 1); - } - } - - return text; - } - }; - scorer.setExpandMultiTermQuery(request.getParams().getBool(HighlightParams.HIGHLIGHT_MULTI_TERM, true)); - - boolean defaultPayloads = true;// overwritten below - try - { - // It'd be nice to know if payloads are on the tokenStream but the - // presence of the attribute isn't a good - // indicator. - final Terms terms = request.getSearcher().getSlowAtomicReader().fields().terms(schemaFieldName); - if (terms != null) - { - defaultPayloads = terms.hasPayloads(); - } - } - catch (IOException e) - { - LOGGER.error("Couldn't check for existence of payloads", e); - } - scorer.setUsePayloads(request.getParams().getFieldBool(requestFieldname, HighlightParams.PAYLOADS, defaultPayloads)); - return scorer; - } - - @SuppressWarnings("unchecked") - @Override - public NamedList doHighlighting(DocList docs, Query query, SolrQueryRequest request, String[] defaultFields) throws IOException - { - final String idFieldName = request.getSchema().getUniqueKeyField().getName(); - final Set idFields = Set.of(idFieldName, "DBID"); - final SolrParams originalRequestParameters = request.getParams(); - - // raw fields in the hl.fl parameter (e.g. hl.fl=content, name, title) - List highlightFields = stream(super.getHighlightFields(query, request, defaultFields)).collect(toList()); - - /* - For each field name collected in the previous step, we need to query the AlfrescoSolrDataModel in order to - retrieve the corresponding field that will be used for the current highlighting request. - - e.g. - { - name => text@s_stored_lt@{http://www.alfresco.org/model/content/1.0}name, - title => mltext@m__stored_lt@{http://www.alfresco.org/model/content/1.0}title, - content => content@s_stored_lt@{http://www.alfresco.org/model/content/1.0}content - } - - Since at the end we need to restore (in the response) the original request(ed) fields names (e.g. content, name) used on client side - we need to maintain a map which associates each schema field (e.g. text@s_stored_lt@{http://www.alfresco.org/model/content/1.0}name) - with the corresponding request(ed) field (e.g. name). - */ - Map mappings = withDebug(createInitialFieldMappings(request, highlightFields)); - - // The identifiers map collects two documents identifiers for each document (Solr "id" and "DBID"). - // Keys of the identifiers map are Solr "id", while values are simple value objects encapsulating all those two identifiers (for a specific document). - Iterable iterable = docs::iterator; - Map identifiers = - StreamSupport.stream(iterable.spliterator(), false) - .map(docid -> identifiersEntry(request.getSearcher(), docid, idFields, idFieldName)) - .filter(Objects::nonNull) - .collect(toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); - - // First round: call the Solr highlighting procedure using the current fields mappings. - request.setParams(rewrite(originalRequestParameters, mappings, join(",", mappings.keySet()))); - NamedList highlightingResponse = super.doHighlighting(docs, query, request, defaultFields); - - // Final step: under each document section, highlight snippets are associated with Solr field names, - // so we need to replace them with fields actually requested - // In addition, beside the snippets we want to have the document DBID as well. - NamedList response = new SimpleOrderedMap<>(); - highlightingResponse.forEach( entry -> { - String id = entry.getKey(); - NamedList documentHighlighting = (NamedList) entry.getValue(); - NamedList renamedDocumentHighlighting = new SimpleOrderedMap<>(); - if (notNullAndNotEmpty.test(documentHighlighting)) - { - ofNullable(identifiers.get(id)) - .map(DocumentIdentifiers::dbid) - .ifPresent(dbid -> renamedDocumentHighlighting.add("DBID", dbid)); - } - - documentHighlighting.forEach(fieldEntry -> { - detectAndRemoveLocalePrefix(fieldEntry); - - String solrFieldName = fieldEntry.getKey(); - String requestFieldName = mappings.get(solrFieldName); - renamedDocumentHighlighting.add(requestFieldName, fieldEntry.getValue()); - }); - - response.add(id, renamedDocumentHighlighting); - }); - - return response; - } - - /** - * Remember the stored field, used for highlighting, contains a locale marker prefix which has to be removed - * before returning back the response. - * - * @param highlightFieldEntry the response highlight entry for a specific field. - */ - private void detectAndRemoveLocalePrefix(Map.Entry highlightFieldEntry) - { - if (highlightFieldEntry.getValue() instanceof String[]) - { - String [] snippets = (String[])highlightFieldEntry.getValue(); - if (snippets.length > 0) - { - snippets[0] = snippets[0].charAt(3) == '\u0000' ? snippets[0].substring(4) : snippets[0].substring(5); - } - } - } - - private AbstractMap.SimpleEntry identifiersEntry(SolrIndexSearcher searcher, int docid, Set idFields, String idFieldName) - { - try - { - Document doc = searcher.doc(docid, idFields); - String solrId = doc.get(idFieldName); - return new AbstractMap.SimpleEntry<>(solrId, new DocumentIdentifiers(solrId, doc.get("DBID"))); - } - catch (Exception exception) - { - return null; - } - } - - private void rewriteLocalFieldParameters(ModifiableSolrParams newParams, SolrParams previousParams, String fieldName, String schemaFieldName) - { - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SIMPLE_PRE, fieldName, schemaFieldName); - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SIMPLE_POST, fieldName, schemaFieldName); - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.FRAGSIZE, fieldName, schemaFieldName); - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.MERGE_CONTIGUOUS_FRAGMENTS, fieldName, schemaFieldName); - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SNIPPETS, fieldName, schemaFieldName); - } - - private void rewriteHighlightFieldOptions(ModifiableSolrParams fixed, SolrParams params, String paramName, String oldField, String newField) - { - for(Iterator it = params.getParameterNamesIterator(); it.hasNext();) - { - String name = it.next(); - if(name.startsWith("f.")) - { - if(name.endsWith("." + paramName)) - { - - String source = name.substring(2, name.length() - paramName.length() - 1); - if(source.equals(oldField)) - { - fixed.set("f." + newField + "." + paramName, params.getParams(name)); - } - else - { - fixed.set(name, params.getParams(name)); - } - } - else - { - fixed.set(name, params.getParams(name)); - } - } - } - } - - private SolrParams rewrite(SolrParams params, Map mappings, String fields) - { - ModifiableSolrParams rewrittenParams = new ModifiableSolrParams(params).set(HighlightParams.FIELDS, fields); - mappings.forEach((key, value) -> rewriteLocalFieldParameters(rewrittenParams, params, value, key)); - return rewrittenParams; - } - - /** - * Debugs the content of the given mappings. - * - * @param mappings the fields mapping. - * @return the same input mappings instance. - */ - private Map withDebug(Map mappings) - { - if (LOGGER.isDebugEnabled()) - { - mappings.forEach( (solrField, requestField) -> LOGGER.debug("Request field {} has been mapped to {}", requestField, solrField)); - } - return mappings; - } - - /** - * Starting from the input requested highlight fields (i.e. fields listed in {@link HighlightParams#FIELDS} parameter) - * we create a map which associates each member with the corresponding field in the Solr schema. - * For example: - * - *
-	 * 	name 	=>	text@s___t@{http://www.alfresco.org/model/content/1.0}name,
-	 * 	title	=>	mltext@m___t@{http://www.alfresco.org/model/content/1.0}title,
-	 * 	content	=>	content@s___t@{http://www.alfresco.org/model/content/1.0}content
-	 * 
- * - * IMPORTANT: although returned as {@link Map} interface, the returned data structure IS MUTABLE. This is needed - * because during the highlighting workflow we need to change its content by adding fields. - * - * @param request the current incoming client request. - * @param requestedHighlightFields a list of raw fields listed in {@link HighlightParams#FIELDS} parameter - * @return a map associating request(ed) fields with the corresponding schema fields. - */ - private Map createInitialFieldMappings(SolrQueryRequest request, List requestedHighlightFields) - { - return requestedHighlightFields.stream() - .map(requestFieldName -> - new AbstractMap.SimpleEntry<>( - AlfrescoSolrDataModel.getInstance().mapProperty(requestFieldName, FieldUse.HIGHLIGHT, request), - requestFieldName)) - .collect(toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, (prev, next) -> next, HashMap::new)); - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; + +import static java.lang.String.join; +import static java.util.Arrays.stream; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.document.Document; +import org.apache.lucene.index.Terms; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.highlight.Highlighter; +import org.apache.lucene.search.highlight.QueryScorer; +import org.apache.lucene.search.highlight.WeightedSpanTerm; +import org.apache.lucene.search.highlight.WeightedSpanTermExtractor; +import org.apache.solr.common.params.HighlightParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.SolrCore; +import org.apache.solr.highlight.DefaultSolrHighlighter; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.util.plugin.PluginInfoInitialized; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.StreamSupport; + +/** + * The Alfresco customisation of the original (or somewhere called default) Solr highlighter. + * In this latest revision, the core Highlighting logic has been delegated to the Solr highlighter. + * The main difference introduced in this customisation is related with the fields mappings (i.e. mappings between + * the Alfresco and Solr fields names). + * + * The purpose of the delegation to the built-in Solr logic is to allow (later) an easier refactoring/removal. + * Specifically, when the mapping logic will be extracted in an external (calling) layer this component will be removed + * in favour of the built-in Solr (Default) Highlighter. + * + * The {@link #doHighlighting(DocList, Query, SolrQueryRequest, String[])} core method, which has been overriden, + * provides the required mapping between the requested highlighting fields in the hl.fl parameters (which follow the + * Alfresco semantic) and the Solr fields we have in the schema. + * + * At the end of the highlighting process the same mappings are used for doing the reverse process (replace the Solr + * fields with the original requested fields). + * + * Additionally, since the Solr ID has no meaning outside Solr, the Highlighter adds the DBID to each highlighting + * snippets: + * + *
+ *  <lst name="_DEFAULT_!8000016f66a1a298!8000016f66a1a29e">
+ *      <str name="DBID">1577974866590</str>
+ *      <arr name="name">
+ *          <str>some very <em>long</em> name</str>
+ *      </arr>
+ *      <arr name="title">
+ *          <str>This the <em>long</em> french version of of the</str>
+ *          <str>This the <em>long</em> english version of the</str>
+ *      </arr>
+ *  </lst>
+ * 
+ * + * @see SEARCH-2033 + */ +public class AlfrescoSolrHighlighter extends DefaultSolrHighlighter implements PluginInfoInitialized +{ + private static final Logger LOGGER = LoggerFactory.getLogger(AlfrescoSolrHighlighter.class); + + private static class DocumentIdentifiers + { + final String solrId; + final String dbid; + + private DocumentIdentifiers(String solrId, String dbid) + { + this.solrId = solrId; + this.dbid = dbid; + } + + String dbid() + { + return dbid; + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof DocumentIdentifiers + && ((DocumentIdentifiers)obj).solrId.equals(solrId); + } + + @Override + public int hashCode() + { + return Objects.hash(solrId); + } + } + + private final Predicate> notNullAndNotEmpty = response -> response != null && response.size() > 0; + + public AlfrescoSolrHighlighter(SolrCore core) + { + super(core); + } + + @Override + protected Highlighter getHighlighter(Query query, String requestFieldname, SolrQueryRequest request) + { + String schemaFieldName = + AlfrescoSolrDataModel.getInstance() + .mapProperty(requestFieldname, FieldUse.HIGHLIGHT, request); + + Highlighter highlighter = + new Highlighter( + getFormatter(requestFieldname, request.getParams()), + getEncoder(requestFieldname, request.getParams()), + getQueryScorer(query,schemaFieldName, request)); + + highlighter.setTextFragmenter(getFragmenter(requestFieldname, request.getParams())); + return highlighter; + } + + /** + * We need to override this method because here Solr manages the {@link HighlightParams#FIELD_MATCH} parameter. + * In the last iteration, we improved the Solr schema by using one single stored field (used in this context for the + * highlighting) which is copied across multiple search fields (e.g. text@s__t,text@s_lt,mltext@m__lt). + * + * The input requestFieldName parameter is the stored field mentioned above; in case {@link HighlightParams#FIELD_MATCH} + * is set to true only query terms aligning with the field being highlighted will in turn be highlighted. + * + * The reverse field mapping you'll find in this method is because the stored field is not indexed, and therefore + * there won't be any "alignment" as described above, never. + * + * So starting from the stored field we need to query {@link AlfrescoSolrDataModel} for retrieving the corresponding + * cross-locale field (text|mltext@m|s__t@) and inject it into the {@link QueryScorer} that is in charge to score only + * those terms that contributed in generating the 'hit' on the document. + * + * TODO: this is one thing that can be improved together with the multilanguage improvement/implementation tasks + * + * @see SEARCH-1665 + * @see SEARCH-2056 + */ + @Override + protected QueryScorer getSpanQueryScorer(Query query, String requestFieldname, TokenStream tokenStream, SolrQueryRequest request) + { + String localFieldName = requestFieldname.substring(requestFieldname.lastIndexOf("}") + 1); + String schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty(localFieldName, FieldUse.FTS, request); + if (!schemaFieldName.contains("_t@{")) + { + schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty(localFieldName, FieldUse.FTS, request, 1); + } + + + // The query scorer purpose is to give a score to the text fragments by the number of unique query terms found. + // + // Fields that use the Alfresco MLAnalyser have a locale marker at the beginning of each token. For example, + // the text "My name is Gazza" becomes (locale = en): + // + // {en}my {en}name {en}is {en}gazza + // + // while this is not an issue for searching, those tokens never find a match during the highlighting because + // my != {en}my + // + // The purpose of this custom query scorer is to replace the collected weighted terms with their corresponding + // un-localised version. + QueryScorer scorer = new QueryScorer(query,request.getParams().getFieldBool(requestFieldname, HighlightParams.FIELD_MATCH, false) ? schemaFieldName : null) + { + @Override + protected WeightedSpanTermExtractor newTermExtractor(String defaultField) + { + return new WeightedSpanTermExtractor(defaultField) + { + @Override + protected void extractWeightedTerms(Map terms, Query query, float boost) throws IOException + { + super.extractWeightedTerms(terms, query, boost); + List termsWithoutLocale = + terms.values() + .stream() + .peek(term -> term.setTerm(withoutLocalePrefixMarker(term.getTerm()))) + .collect(toList()); + + terms.clear(); + termsWithoutLocale.forEach(term -> terms.put(term.getTerm(), term)); + } + }; + } + + /** + * Removes the locale marker from the given text. + * + * @param text the input text. + * @return the text without the beginning locale marker, or the same text is the marker cannot be found. + */ + private String withoutLocalePrefixMarker(String text) + { + if (text == null) return null; + + int startIndexOfMarker = text.indexOf("{"); + if (startIndexOfMarker == 0) + { + int endIndexOfMarker = text.indexOf("}"); + if (endIndexOfMarker != -1 && text.length() > (endIndexOfMarker + 1)) + { + return text.substring(endIndexOfMarker + 1); + } + } + + return text; + } + }; + scorer.setExpandMultiTermQuery(request.getParams().getBool(HighlightParams.HIGHLIGHT_MULTI_TERM, true)); + + boolean defaultPayloads = true;// overwritten below + try + { + // It'd be nice to know if payloads are on the tokenStream but the + // presence of the attribute isn't a good + // indicator. + final Terms terms = request.getSearcher().getSlowAtomicReader().fields().terms(schemaFieldName); + if (terms != null) + { + defaultPayloads = terms.hasPayloads(); + } + } + catch (IOException e) + { + LOGGER.error("Couldn't check for existence of payloads", e); + } + scorer.setUsePayloads(request.getParams().getFieldBool(requestFieldname, HighlightParams.PAYLOADS, defaultPayloads)); + return scorer; + } + + @SuppressWarnings("unchecked") + @Override + public NamedList doHighlighting(DocList docs, Query query, SolrQueryRequest request, String[] defaultFields) throws IOException + { + final String idFieldName = request.getSchema().getUniqueKeyField().getName(); + final Set idFields = Set.of(idFieldName, "DBID"); + final SolrParams originalRequestParameters = request.getParams(); + + // raw fields in the hl.fl parameter (e.g. hl.fl=content, name, title) + List highlightFields = stream(super.getHighlightFields(query, request, defaultFields)).collect(toList()); + + /* + For each field name collected in the previous step, we need to query the AlfrescoSolrDataModel in order to + retrieve the corresponding field that will be used for the current highlighting request. + + e.g. + { + name => text@s_stored_lt@{http://www.alfresco.org/model/content/1.0}name, + title => mltext@m__stored_lt@{http://www.alfresco.org/model/content/1.0}title, + content => content@s_stored_lt@{http://www.alfresco.org/model/content/1.0}content + } + + Since at the end we need to restore (in the response) the original request(ed) fields names (e.g. content, name) used on client side + we need to maintain a map which associates each schema field (e.g. text@s_stored_lt@{http://www.alfresco.org/model/content/1.0}name) + with the corresponding request(ed) field (e.g. name). + */ + Map mappings = withDebug(createInitialFieldMappings(request, highlightFields)); + + // The identifiers map collects two documents identifiers for each document (Solr "id" and "DBID"). + // Keys of the identifiers map are Solr "id", while values are simple value objects encapsulating all those two identifiers (for a specific document). + Iterable iterable = docs::iterator; + Map identifiers = + StreamSupport.stream(iterable.spliterator(), false) + .map(docid -> identifiersEntry(request.getSearcher(), docid, idFields, idFieldName)) + .filter(Objects::nonNull) + .collect(toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); + + // First round: call the Solr highlighting procedure using the current fields mappings. + request.setParams(rewrite(originalRequestParameters, mappings, join(",", mappings.keySet()))); + NamedList highlightingResponse = super.doHighlighting(docs, query, request, defaultFields); + + // Final step: under each document section, highlight snippets are associated with Solr field names, + // so we need to replace them with fields actually requested + // In addition, beside the snippets we want to have the document DBID as well. + NamedList response = new SimpleOrderedMap<>(); + highlightingResponse.forEach( entry -> { + String id = entry.getKey(); + NamedList documentHighlighting = (NamedList) entry.getValue(); + NamedList renamedDocumentHighlighting = new SimpleOrderedMap<>(); + if (notNullAndNotEmpty.test(documentHighlighting)) + { + ofNullable(identifiers.get(id)) + .map(DocumentIdentifiers::dbid) + .ifPresent(dbid -> renamedDocumentHighlighting.add("DBID", dbid)); + } + + documentHighlighting.forEach(fieldEntry -> { + detectAndRemoveLocalePrefix(fieldEntry); + + String solrFieldName = fieldEntry.getKey(); + String requestFieldName = mappings.get(solrFieldName); + renamedDocumentHighlighting.add(requestFieldName, fieldEntry.getValue()); + }); + + response.add(id, renamedDocumentHighlighting); + }); + + return response; + } + + /** + * Remember the stored field, used for highlighting, contains a locale marker prefix which has to be removed + * before returning back the response. + * + * @param highlightFieldEntry the response highlight entry for a specific field. + */ + private void detectAndRemoveLocalePrefix(Map.Entry highlightFieldEntry) + { + if (highlightFieldEntry.getValue() instanceof String[]) + { + String [] snippets = (String[])highlightFieldEntry.getValue(); + if (snippets.length > 0) + { + snippets[0] = snippets[0].charAt(3) == '\u0000' ? snippets[0].substring(4) : snippets[0].substring(5); + } + } + } + + private AbstractMap.SimpleEntry identifiersEntry(SolrIndexSearcher searcher, int docid, Set idFields, String idFieldName) + { + try + { + Document doc = searcher.doc(docid, idFields); + String solrId = doc.get(idFieldName); + return new AbstractMap.SimpleEntry<>(solrId, new DocumentIdentifiers(solrId, doc.get("DBID"))); + } + catch (Exception exception) + { + return null; + } + } + + private void rewriteLocalFieldParameters(ModifiableSolrParams newParams, SolrParams previousParams, String fieldName, String schemaFieldName) + { + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SIMPLE_PRE, fieldName, schemaFieldName); + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SIMPLE_POST, fieldName, schemaFieldName); + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.FRAGSIZE, fieldName, schemaFieldName); + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.MERGE_CONTIGUOUS_FRAGMENTS, fieldName, schemaFieldName); + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SNIPPETS, fieldName, schemaFieldName); + } + + private void rewriteHighlightFieldOptions(ModifiableSolrParams fixed, SolrParams params, String paramName, String oldField, String newField) + { + for(Iterator it = params.getParameterNamesIterator(); it.hasNext();) + { + String name = it.next(); + if(name.startsWith("f.")) + { + if(name.endsWith("." + paramName)) + { + + String source = name.substring(2, name.length() - paramName.length() - 1); + if(source.equals(oldField)) + { + fixed.set("f." + newField + "." + paramName, params.getParams(name)); + } + else + { + fixed.set(name, params.getParams(name)); + } + } + else + { + fixed.set(name, params.getParams(name)); + } + } + } + } + + private SolrParams rewrite(SolrParams params, Map mappings, String fields) + { + ModifiableSolrParams rewrittenParams = new ModifiableSolrParams(params).set(HighlightParams.FIELDS, fields); + mappings.forEach((key, value) -> rewriteLocalFieldParameters(rewrittenParams, params, value, key)); + return rewrittenParams; + } + + /** + * Debugs the content of the given mappings. + * + * @param mappings the fields mapping. + * @return the same input mappings instance. + */ + private Map withDebug(Map mappings) + { + if (LOGGER.isDebugEnabled()) + { + mappings.forEach( (solrField, requestField) -> LOGGER.debug("Request field {} has been mapped to {}", requestField, solrField)); + } + return mappings; + } + + /** + * Starting from the input requested highlight fields (i.e. fields listed in {@link HighlightParams#FIELDS} parameter) + * we create a map which associates each member with the corresponding field in the Solr schema. + * For example: + * + *
+	 * 	name 	=>	text@s___t@{http://www.alfresco.org/model/content/1.0}name,
+	 * 	title	=>	mltext@m___t@{http://www.alfresco.org/model/content/1.0}title,
+	 * 	content	=>	content@s___t@{http://www.alfresco.org/model/content/1.0}content
+	 * 
+ * + * IMPORTANT: although returned as {@link Map} interface, the returned data structure IS MUTABLE. This is needed + * because during the highlighting workflow we need to change its content by adding fields. + * + * @param request the current incoming client request. + * @param requestedHighlightFields a list of raw fields listed in {@link HighlightParams#FIELDS} parameter + * @return a map associating request(ed) fields with the corresponding schema fields. + */ + private Map createInitialFieldMappings(SolrQueryRequest request, List requestedHighlightFields) + { + return requestedHighlightFields.stream() + .map(requestFieldName -> + new AbstractMap.SimpleEntry<>( + AlfrescoSolrDataModel.getInstance().mapProperty(requestFieldName, FieldUse.HIGHLIGHT, request), + requestFieldName)) + .collect(toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, (prev, next) -> next, HashMap::new)); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayserTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayserTest.java index e38000599..3401a07c6 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayserTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayserTest.java @@ -1,182 +1,182 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.namespace.QName; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; - -/** - * Retrofitted tests for the {@link MLAnalayser} class. These - * should not be considered comprehensive, but are here to aid - * refactoring during move to SOLR4. - * - * @author Matt Ward - */ -@RunWith(MockitoJUnitRunner.class) -public class MLAnalayserTest -{ - private static final String PROPERTY_NAME = "@{http://www.alfresco.org/model/content/1.0}propertyName"; - private MLAnalayser analyser; - private @Mock DictionaryService dictionaryService; - private MLAnalysisMode mlAnalaysisMode = MLAnalysisMode.EXACT_COUNRTY; - - - @Before - public void setUp() throws Exception - { - analyser = new MLAnalayser(mlAnalaysisMode); - PropertyDefinition propDef = Mockito.mock(PropertyDefinition.class); - when(dictionaryService.getProperty(any(QName.class))).thenReturn(propDef); - } - - @Ignore - @Test - public void testTokenStreamForLanguageAndCountry() throws IOException - { - final String inputStr = "\u0000fr_FR\u0000Ceci n'est pas Française"; - final Reader reader = new StringReader(inputStr); - - List expectedTokens = new ArrayList<>(); - expectedTokens.add("{fr_FR}ceci"); - expectedTokens.add("{fr_FR}n'est"); - expectedTokens.add("{fr_FR}pas"); - expectedTokens.add("{fr_FR}francaise"); // normalised 'c'. - - TokenStream ts = analyser.tokenStream(PROPERTY_NAME, reader); - verifyTokenStream(ts, expectedTokens); - } - - @Ignore - @Test - public void testTokenStreamForLanguage() throws IOException - { - final String inputStr = "\u0000fr\u0000Ceci n'est pas Française"; - final Reader reader = new StringReader(inputStr); - - List expectedTokens = new ArrayList<>(); - expectedTokens.add("{fr}ceci"); - expectedTokens.add("{fr_CH}ceci"); - expectedTokens.add("{fr_LU}ceci"); - expectedTokens.add("{fr_FR}ceci"); - expectedTokens.add("{fr_BE}ceci"); - expectedTokens.add("{fr_CA}ceci"); - - expectedTokens.add("{fr}n'est"); - expectedTokens.add("{fr_CH}n'est"); - expectedTokens.add("{fr_LU}n'est"); - expectedTokens.add("{fr_FR}n'est"); - expectedTokens.add("{fr_BE}n'est"); - expectedTokens.add("{fr_CA}n'est"); - - expectedTokens.add("{fr}pas"); - expectedTokens.add("{fr_CH}pas"); - expectedTokens.add("{fr_LU}pas"); - expectedTokens.add("{fr_FR}pas"); - expectedTokens.add("{fr_BE}pas"); - expectedTokens.add("{fr_CA}pas"); - - expectedTokens.add("{fr}francaise"); - expectedTokens.add("{fr_CH}francaise"); - expectedTokens.add("{fr_LU}francaise"); - expectedTokens.add("{fr_FR}francaise"); - expectedTokens.add("{fr_BE}francaise"); - expectedTokens.add("{fr_CA}francaise"); - - TokenStream ts = analyser.tokenStream(PROPERTY_NAME, reader); - verifyTokenStream(ts, expectedTokens); - } - - /** - * Check that the TokenStream yields the exact tokens specified. - * Note that order is not checked, since the map of locales will not provide a - * predictable ordering when enumerated. - * - * The expected list of tokens may contain the same token more than once and - * the number of instances will have to match the number found in the stream. - * - * @param ts TokenStream to inspect. - * @param expectedTokens List of tokens in the order expected from the stream. - * @throws IOException - */ - private void verifyTokenStream(TokenStream ts, List expectedTokens) throws IOException - { - final int expectedCount = expectedTokens.size(); - int count = 0; - - CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); - - try - { - ts.reset(); - while (ts.incrementToken()) - { - count++; - System.out.println("Token: " + termAtt.toString()); - if (expectedTokens.contains(termAtt.toString())) - { - // remove an instance of the term text so that it is not matched again - expectedTokens.remove(termAtt.toString()); - } - else - { - fail("Unexpected token: " + termAtt.toString()); - } - } - ts.end(); - } - finally - { - ts.close(); - } - - assertEquals("Incorrect number of tokens generated.", expectedCount, count); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.QName; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * Retrofitted tests for the {@link MLAnalayser} class. These + * should not be considered comprehensive, but are here to aid + * refactoring during move to SOLR4. + * + * @author Matt Ward + */ +@RunWith(MockitoJUnitRunner.class) +public class MLAnalayserTest +{ + private static final String PROPERTY_NAME = "@{http://www.alfresco.org/model/content/1.0}propertyName"; + private MLAnalayser analyser; + private @Mock DictionaryService dictionaryService; + private MLAnalysisMode mlAnalaysisMode = MLAnalysisMode.EXACT_COUNRTY; + + + @Before + public void setUp() throws Exception + { + analyser = new MLAnalayser(mlAnalaysisMode); + PropertyDefinition propDef = Mockito.mock(PropertyDefinition.class); + when(dictionaryService.getProperty(any(QName.class))).thenReturn(propDef); + } + + @Ignore + @Test + public void testTokenStreamForLanguageAndCountry() throws IOException + { + final String inputStr = "\u0000fr_FR\u0000Ceci n'est pas Française"; + final Reader reader = new StringReader(inputStr); + + List expectedTokens = new ArrayList<>(); + expectedTokens.add("{fr_FR}ceci"); + expectedTokens.add("{fr_FR}n'est"); + expectedTokens.add("{fr_FR}pas"); + expectedTokens.add("{fr_FR}francaise"); // normalised 'c'. + + TokenStream ts = analyser.tokenStream(PROPERTY_NAME, reader); + verifyTokenStream(ts, expectedTokens); + } + + @Ignore + @Test + public void testTokenStreamForLanguage() throws IOException + { + final String inputStr = "\u0000fr\u0000Ceci n'est pas Française"; + final Reader reader = new StringReader(inputStr); + + List expectedTokens = new ArrayList<>(); + expectedTokens.add("{fr}ceci"); + expectedTokens.add("{fr_CH}ceci"); + expectedTokens.add("{fr_LU}ceci"); + expectedTokens.add("{fr_FR}ceci"); + expectedTokens.add("{fr_BE}ceci"); + expectedTokens.add("{fr_CA}ceci"); + + expectedTokens.add("{fr}n'est"); + expectedTokens.add("{fr_CH}n'est"); + expectedTokens.add("{fr_LU}n'est"); + expectedTokens.add("{fr_FR}n'est"); + expectedTokens.add("{fr_BE}n'est"); + expectedTokens.add("{fr_CA}n'est"); + + expectedTokens.add("{fr}pas"); + expectedTokens.add("{fr_CH}pas"); + expectedTokens.add("{fr_LU}pas"); + expectedTokens.add("{fr_FR}pas"); + expectedTokens.add("{fr_BE}pas"); + expectedTokens.add("{fr_CA}pas"); + + expectedTokens.add("{fr}francaise"); + expectedTokens.add("{fr_CH}francaise"); + expectedTokens.add("{fr_LU}francaise"); + expectedTokens.add("{fr_FR}francaise"); + expectedTokens.add("{fr_BE}francaise"); + expectedTokens.add("{fr_CA}francaise"); + + TokenStream ts = analyser.tokenStream(PROPERTY_NAME, reader); + verifyTokenStream(ts, expectedTokens); + } + + /** + * Check that the TokenStream yields the exact tokens specified. + * Note that order is not checked, since the map of locales will not provide a + * predictable ordering when enumerated. + * + * The expected list of tokens may contain the same token more than once and + * the number of instances will have to match the number found in the stream. + * + * @param ts TokenStream to inspect. + * @param expectedTokens List of tokens in the order expected from the stream. + * @throws IOException + */ + private void verifyTokenStream(TokenStream ts, List expectedTokens) throws IOException + { + final int expectedCount = expectedTokens.size(); + int count = 0; + + CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); + + try + { + ts.reset(); + while (ts.incrementToken()) + { + count++; + System.out.println("Token: " + termAtt.toString()); + if (expectedTokens.contains(termAtt.toString())) + { + // remove an instance of the term text so that it is not matched again + expectedTokens.remove(termAtt.toString()); + } + else + { + fail("Unexpected token: " + termAtt.toString()); + } + } + ts.end(); + } + finally + { + ts.close(); + } + + assertEquals("Incorrect number of tokens generated.", expectedCount, count); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilterTest.java index a9d1aeaa2..9c7350aab 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilterTest.java @@ -1,220 +1,220 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; - -import java.io.IOException; -import java.io.StringReader; - -import junit.framework.TestCase; - -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; - -public class PathTokenFilterTest extends TestCase -{ - - public PathTokenFilterTest() - { - super(); - } - - public PathTokenFilterTest(String arg0) - { - super(arg0); - } - - - public void testFullPath() throws IOException - { - tokenise("{uri1}one", new String[]{"uri1", "one"}); - tokenise("/{uri1}one", new String[]{"uri1", "one"}); - tokenise("{uri1}one/{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("/{uri1}one/{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("{uri1}one/{uri2}two/{uri3}three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - tokenise("/{uri1}one/{uri2}two/{uri3}three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - try - { - tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - } - catch(IllegalStateException ise) - { - - } - - } - - - public void testPrefixPath() throws IOException - { - tokenise("uri1:one", new String[]{"uri1", "one"}); - tokenise("/uri1:one", new String[]{"uri1", "one"}); - tokenise("uri1:one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("/uri1:one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("uri1:one/uri2:two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - tokenise("/uri1:one/uri2:two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - try - { - tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - } - catch(IllegalStateException ise) - { - - } - - } - - - public void testMixedPath() throws IOException - { - - tokenise("{uri1}one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("/{uri1}one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("uri1:one/{uri2}two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - tokenise("/uri1:one/{uri2}two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - try - { - tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - } - catch(IllegalStateException ise) - { - - } - - } - - public void testTokenizerReuse() throws IOException - { - // We should be able to use the same Tokenizer twice. - final String path = "uri1:one"; - StringReader reader = new StringReader(path); - PathTokenFilter ts = new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, - PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, - PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); - ts.setReader(reader); - - OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class); - - // First use - tokenise(ts, new String[]{"uri1", "one"}); - assertEquals(path.length(), offsetAtt.startOffset()); - assertEquals(path.length(), offsetAtt.endOffset()); - - // Second use - final String path2 = "/{uri1}one/uri2:two/"; - StringReader reader2 = new StringReader(path2); - ts.setReader(reader2); - tokenise(ts, new String[]{"uri1", "one", "uri2", "two"}); - assertEquals(path2.length(), offsetAtt.startOffset()); - assertEquals(path2.length(), offsetAtt.endOffset()); - } - - - public void testAttributesAfterStreamEnd() throws IOException - { - final String path = "uri1:one"; - StringReader reader = new StringReader(path); - PathTokenFilter ts = new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, - PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, - PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); - ts.setReader(reader); - - CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); - TypeAttribute typeAtt = ts.addAttribute(TypeAttribute.class); - OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class); - PositionIncrementAttribute posIncAtt = ts.addAttribute(PositionIncrementAttribute.class); - - // PathTokenFilter.end() will be called after all tokens consumed. - tokenise(ts, new String[]{"uri1", "one"}); - - // Check attributes cleaned up - assertEquals("", termAtt.toString()); - assertEquals("word", typeAtt.type()); // the default - assertEquals(0, posIncAtt.getPositionIncrement()); - // Final offset... - assertEquals(path.length(), offsetAtt.startOffset()); - assertEquals(path.length(), offsetAtt.endOffset()); - } - - private void tokenise(String path, String[] tokens) throws IOException - { - StringReader reader = new StringReader(path); - PathTokenFilter ts = new PathTokenFilter( PathTokenFilter.PATH_SEPARATOR, - PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, - PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); - ts.setReader(reader); - - tokenise(ts, tokens); - } - - private void tokenise(TokenStream ts, String[] tokens) throws IOException - { - int i = 0; - - CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); - TypeAttribute typeAtt = ts.addAttribute(TypeAttribute.class); - - try - { - ts.reset(); - while (ts.incrementToken()) - { - System.out.println("token: " + ts.reflectAsString(true)); - - String termText = termAtt.toString(); - - if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAMESPACE)) - { - assert(i % 2 == 0); - assertEquals(termText, tokens[i++]); - } - else if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX)) - { - assert(i % 2 == 0); - assertEquals(termText, tokens[i++]); - } - else if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAME)) - { - assert(i % 2 == 1); - assertEquals(termText, tokens[i++]); - } - } - ts.end(); - } - finally - { - ts.close(); - } - - if(i != tokens.length) - { - fail("Invalid number of tokens, found "+i+" and expected "+tokens.length); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; + +import java.io.IOException; +import java.io.StringReader; + +import junit.framework.TestCase; + +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; + +public class PathTokenFilterTest extends TestCase +{ + + public PathTokenFilterTest() + { + super(); + } + + public PathTokenFilterTest(String arg0) + { + super(arg0); + } + + + public void testFullPath() throws IOException + { + tokenise("{uri1}one", new String[]{"uri1", "one"}); + tokenise("/{uri1}one", new String[]{"uri1", "one"}); + tokenise("{uri1}one/{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("/{uri1}one/{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("{uri1}one/{uri2}two/{uri3}three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + tokenise("/{uri1}one/{uri2}two/{uri3}three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + try + { + tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + } + catch(IllegalStateException ise) + { + + } + + } + + + public void testPrefixPath() throws IOException + { + tokenise("uri1:one", new String[]{"uri1", "one"}); + tokenise("/uri1:one", new String[]{"uri1", "one"}); + tokenise("uri1:one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("/uri1:one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("uri1:one/uri2:two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + tokenise("/uri1:one/uri2:two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + try + { + tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + } + catch(IllegalStateException ise) + { + + } + + } + + + public void testMixedPath() throws IOException + { + + tokenise("{uri1}one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("/{uri1}one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("uri1:one/{uri2}two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + tokenise("/uri1:one/{uri2}two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + try + { + tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + } + catch(IllegalStateException ise) + { + + } + + } + + public void testTokenizerReuse() throws IOException + { + // We should be able to use the same Tokenizer twice. + final String path = "uri1:one"; + StringReader reader = new StringReader(path); + PathTokenFilter ts = new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, + PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, + PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); + ts.setReader(reader); + + OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class); + + // First use + tokenise(ts, new String[]{"uri1", "one"}); + assertEquals(path.length(), offsetAtt.startOffset()); + assertEquals(path.length(), offsetAtt.endOffset()); + + // Second use + final String path2 = "/{uri1}one/uri2:two/"; + StringReader reader2 = new StringReader(path2); + ts.setReader(reader2); + tokenise(ts, new String[]{"uri1", "one", "uri2", "two"}); + assertEquals(path2.length(), offsetAtt.startOffset()); + assertEquals(path2.length(), offsetAtt.endOffset()); + } + + + public void testAttributesAfterStreamEnd() throws IOException + { + final String path = "uri1:one"; + StringReader reader = new StringReader(path); + PathTokenFilter ts = new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, + PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, + PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); + ts.setReader(reader); + + CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); + TypeAttribute typeAtt = ts.addAttribute(TypeAttribute.class); + OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class); + PositionIncrementAttribute posIncAtt = ts.addAttribute(PositionIncrementAttribute.class); + + // PathTokenFilter.end() will be called after all tokens consumed. + tokenise(ts, new String[]{"uri1", "one"}); + + // Check attributes cleaned up + assertEquals("", termAtt.toString()); + assertEquals("word", typeAtt.type()); // the default + assertEquals(0, posIncAtt.getPositionIncrement()); + // Final offset... + assertEquals(path.length(), offsetAtt.startOffset()); + assertEquals(path.length(), offsetAtt.endOffset()); + } + + private void tokenise(String path, String[] tokens) throws IOException + { + StringReader reader = new StringReader(path); + PathTokenFilter ts = new PathTokenFilter( PathTokenFilter.PATH_SEPARATOR, + PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, + PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); + ts.setReader(reader); + + tokenise(ts, tokens); + } + + private void tokenise(TokenStream ts, String[] tokens) throws IOException + { + int i = 0; + + CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); + TypeAttribute typeAtt = ts.addAttribute(TypeAttribute.class); + + try + { + ts.reset(); + while (ts.incrementToken()) + { + System.out.println("token: " + ts.reflectAsString(true)); + + String termText = termAtt.toString(); + + if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAMESPACE)) + { + assert(i % 2 == 0); + assertEquals(termText, tokens[i++]); + } + else if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX)) + { + assert(i % 2 == 0); + assertEquals(termText, tokens[i++]); + } + else if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAME)) + { + assert(i % 2 == 1); + assertEquals(termText, tokens[i++]); + } + } + ts.end(); + } + finally + { + ts.close(); + } + + if(i != tokens.length) + { + fail("Invalid number of tokens, found "+i+" and expected "+tokens.length); + } + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoDistributedIT.java index 552e2416a..c5d83b196 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -899,4 +899,4 @@ public abstract class AbstractAlfrescoDistributedIT extends SolrITInitializer // Nothing to be done here } } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoSolrIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoSolrIT.java index d73d2638b..51e1587a4 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoSolrIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoSolrIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerDistributedIT.java index d157e14b8..98819e172 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -92,4 +92,4 @@ public class AdminHandlerDistributedIT extends AbstractAlfrescoDistributedIT props = testingCore.getCoreDescriptor().getSubstitutableProperties(); assertEquals(ACL_ID.toString(), props.get("shard.method")); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerIT.java index 56887c032..b86619db5 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldTypeTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldTypeTest.java index cd8470d82..1d9a92883 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldTypeTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldTypeTest.java @@ -1,208 +1,208 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import java.text.Collator; -import java.util.Locale; - -import org.alfresco.solr.AlfrescoCollatableMLTextFieldType.MLTextSortFieldComparator; -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -/** Unit tests for {@link AlfrescoCollatableMLTextFieldType}. */ -public class AlfrescoCollatableMLTextFieldTypeTest -{ - private static final int NUM_HITS = 3; - private static final String FIELD = "field"; - private static final Locale LOCALE = Locale.getDefault(); - /** A document id. */ - private static final int DOC = 0; - /** A value for the current bottom document. */ - private static final String BOTTOM_STRING = "Bottom"; - - @InjectMocks - MLTextSortFieldComparator textSortFieldComparator = new MLTextSortFieldComparator(NUM_HITS, FIELD, LOCALE); - @Mock - BinaryDocValues mockDocTerms; - @Mock - Bits mockDocsWithField; - @Mock - Collator mockCollator; - - @Before - public void setUp() - { - initMocks(this); - reset(mockDocTerms, mockDocsWithField); - textSortFieldComparator.bottom = BOTTOM_STRING; - } - - /** Check that a zero length term is sorted before a populated field. */ - @Test - public void testCompareBottom_termLengthZeroAndDocDoesntHaveField() - { - // Set up the document to have an empty term. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - int result = textSortFieldComparator.compareBottom(DOC); - - assertEquals("Expected value for doc to be null, and so it to be sorted before BOTTOM_TERM", 1, result); - } - - /** - * Check the behaviour of compareBottom when docsWithField is null (this happens when all documents contain the - * field). - */ - @Test - public void testCompareBottom_nullDocsWithField() - { - // Set docsWithField to null to simulate all documents containing the field. - Bits oldValue = textSortFieldComparator.docsWithField; - textSortFieldComparator.docsWithField = null; - - // Set up the document to have an empty term. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - // Expect the EMPTY_TERM to be compared - verify(mockCollator).compare(BOTTOM_STRING, ""); - - // Reset docsWithField with the mock after the test. - textSortFieldComparator.docsWithField = oldValue; - } - - /** Check that if the doc has a value then it is compared with the existing value. */ - @Test - public void testCompareBottom_populatedTerm() - { - // Set up the document to have "Some value" for the field. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("Some value")); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - verify(mockCollator).compare(BOTTOM_STRING, "Some value"); - } - - /** Check the behaviour if the multilanguage term is encoded. */ - @Test - public void testCompareBottom_encodedTerm_localeFound() - { - // Create an encoded multilanguage string with Russian, US English and Thai with Thai digits. - String mlText = "\u0000ru\u0000First\u0000Ignored" + - "\u0000en_US\u0000Second\u0000IgnoredToo" + - "\u0000th_TH_TH\u0000Third\u0000AlsoIgnored"; - // Set up the document to have an encoded value for the field. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef(mlText)); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Check that the Russian text can be extracted. - textSortFieldComparator.collatorLocale = Locale.forLanguageTag("ru"); - textSortFieldComparator.compareBottom(DOC); - verify(mockCollator).compare(BOTTOM_STRING, "First"); - - // Check that the English text can be extracted. - textSortFieldComparator.collatorLocale = Locale.forLanguageTag("en"); - textSortFieldComparator.compareBottom(DOC); - verify(mockCollator).compare(BOTTOM_STRING, "Second"); - - // Check that the Thai text can be extracted. - textSortFieldComparator.collatorLocale = Locale.forLanguageTag("th"); - textSortFieldComparator.compareBottom(DOC); - verify(mockCollator).compare(BOTTOM_STRING, "Third"); - - // Reset the locale for other tests. - textSortFieldComparator.collatorLocale = LOCALE; - } - - /** Check the behaviour if the term has a locale but no text. */ - @Test - public void testCompareBottom_badlyEncodedTerm() - { - // Set the value to have a locale but no text. - String mlText = "\u0000ru"; - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef(mlText)); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - // Check that an empty string is assumed. - verify(mockCollator).compare(BOTTOM_STRING, ""); - } - - @Test - public void testCompareValues_nullLessThanString() - { - int result = textSortFieldComparator.compareValues(null, "NotNull"); - assertEquals("Expected null to be 'less' than string.", -1, result); - } - - @Test - public void testCompareValues_stringGreaterThanNull() - { - int result = textSortFieldComparator.compareValues("NotNull", null); - assertEquals("Expected string to be 'greater' than null.", 1, result); - } - - @Test - public void testCompareValues_nullEqualToNull() - { - int result = textSortFieldComparator.compareValues(null, null); - assertEquals("Expected two null values to be equal.", 0, result); - } - - /** Check that when two non-null strings are compared then the underlying collator is used to get the result. */ - @Test - public void testCompareValues_twoStringsCompared() - { - // An arbitrary value to be returned by the collator. - int comparisonResult = 10; - when(mockCollator.compare("NotNull1", "NotNull2")).thenReturn(comparisonResult); - - // Call the method under test. - int result = textSortFieldComparator.compareValues("NotNull1", "NotNull2"); - - verify(mockCollator).compare("NotNull1", "NotNull2"); - assertEquals("Expected result to be obtained from collator.", comparisonResult, result); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.text.Collator; +import java.util.Locale; + +import org.alfresco.solr.AlfrescoCollatableMLTextFieldType.MLTextSortFieldComparator; +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** Unit tests for {@link AlfrescoCollatableMLTextFieldType}. */ +public class AlfrescoCollatableMLTextFieldTypeTest +{ + private static final int NUM_HITS = 3; + private static final String FIELD = "field"; + private static final Locale LOCALE = Locale.getDefault(); + /** A document id. */ + private static final int DOC = 0; + /** A value for the current bottom document. */ + private static final String BOTTOM_STRING = "Bottom"; + + @InjectMocks + MLTextSortFieldComparator textSortFieldComparator = new MLTextSortFieldComparator(NUM_HITS, FIELD, LOCALE); + @Mock + BinaryDocValues mockDocTerms; + @Mock + Bits mockDocsWithField; + @Mock + Collator mockCollator; + + @Before + public void setUp() + { + initMocks(this); + reset(mockDocTerms, mockDocsWithField); + textSortFieldComparator.bottom = BOTTOM_STRING; + } + + /** Check that a zero length term is sorted before a populated field. */ + @Test + public void testCompareBottom_termLengthZeroAndDocDoesntHaveField() + { + // Set up the document to have an empty term. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + int result = textSortFieldComparator.compareBottom(DOC); + + assertEquals("Expected value for doc to be null, and so it to be sorted before BOTTOM_TERM", 1, result); + } + + /** + * Check the behaviour of compareBottom when docsWithField is null (this happens when all documents contain the + * field). + */ + @Test + public void testCompareBottom_nullDocsWithField() + { + // Set docsWithField to null to simulate all documents containing the field. + Bits oldValue = textSortFieldComparator.docsWithField; + textSortFieldComparator.docsWithField = null; + + // Set up the document to have an empty term. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + // Expect the EMPTY_TERM to be compared + verify(mockCollator).compare(BOTTOM_STRING, ""); + + // Reset docsWithField with the mock after the test. + textSortFieldComparator.docsWithField = oldValue; + } + + /** Check that if the doc has a value then it is compared with the existing value. */ + @Test + public void testCompareBottom_populatedTerm() + { + // Set up the document to have "Some value" for the field. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("Some value")); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + verify(mockCollator).compare(BOTTOM_STRING, "Some value"); + } + + /** Check the behaviour if the multilanguage term is encoded. */ + @Test + public void testCompareBottom_encodedTerm_localeFound() + { + // Create an encoded multilanguage string with Russian, US English and Thai with Thai digits. + String mlText = "\u0000ru\u0000First\u0000Ignored" + + "\u0000en_US\u0000Second\u0000IgnoredToo" + + "\u0000th_TH_TH\u0000Third\u0000AlsoIgnored"; + // Set up the document to have an encoded value for the field. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef(mlText)); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Check that the Russian text can be extracted. + textSortFieldComparator.collatorLocale = Locale.forLanguageTag("ru"); + textSortFieldComparator.compareBottom(DOC); + verify(mockCollator).compare(BOTTOM_STRING, "First"); + + // Check that the English text can be extracted. + textSortFieldComparator.collatorLocale = Locale.forLanguageTag("en"); + textSortFieldComparator.compareBottom(DOC); + verify(mockCollator).compare(BOTTOM_STRING, "Second"); + + // Check that the Thai text can be extracted. + textSortFieldComparator.collatorLocale = Locale.forLanguageTag("th"); + textSortFieldComparator.compareBottom(DOC); + verify(mockCollator).compare(BOTTOM_STRING, "Third"); + + // Reset the locale for other tests. + textSortFieldComparator.collatorLocale = LOCALE; + } + + /** Check the behaviour if the term has a locale but no text. */ + @Test + public void testCompareBottom_badlyEncodedTerm() + { + // Set the value to have a locale but no text. + String mlText = "\u0000ru"; + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef(mlText)); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + // Check that an empty string is assumed. + verify(mockCollator).compare(BOTTOM_STRING, ""); + } + + @Test + public void testCompareValues_nullLessThanString() + { + int result = textSortFieldComparator.compareValues(null, "NotNull"); + assertEquals("Expected null to be 'less' than string.", -1, result); + } + + @Test + public void testCompareValues_stringGreaterThanNull() + { + int result = textSortFieldComparator.compareValues("NotNull", null); + assertEquals("Expected string to be 'greater' than null.", 1, result); + } + + @Test + public void testCompareValues_nullEqualToNull() + { + int result = textSortFieldComparator.compareValues(null, null); + assertEquals("Expected two null values to be equal.", 0, result); + } + + /** Check that when two non-null strings are compared then the underlying collator is used to get the result. */ + @Test + public void testCompareValues_twoStringsCompared() + { + // An arbitrary value to be returned by the collator. + int comparisonResult = 10; + when(mockCollator.compare("NotNull1", "NotNull2")).thenReturn(comparisonResult); + + // Call the method under test. + int result = textSortFieldComparator.compareValues("NotNull1", "NotNull2"); + + verify(mockCollator).compare("NotNull1", "NotNull2"); + assertEquals("Expected result to be obtained from collator.", comparisonResult, result); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableTextFieldTypeTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableTextFieldTypeTest.java index d3424bb5a..5093889e7 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableTextFieldTypeTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableTextFieldTypeTest.java @@ -1,176 +1,176 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import java.text.Collator; -import java.util.Locale; - -import org.alfresco.solr.AlfrescoCollatableTextFieldType.TextSortFieldComparator; -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -/** Unit tests for {@link AlfrescoCollatableTextFieldType}. */ -public class AlfrescoCollatableTextFieldTypeTest -{ - private static final int NUM_HITS = 3; - private static final String FIELD = "field"; - private static final Locale LOCALE = Locale.getDefault(); - /** A document id. */ - private static final int DOC = 0; - /** A value for the current bottom document. */ - private static final String BOTTOM_STRING = "Bottom"; - - @InjectMocks - TextSortFieldComparator textSortFieldComparator = new TextSortFieldComparator(NUM_HITS, FIELD, LOCALE); - @Mock - BinaryDocValues mockDocTerms; - @Mock - Bits mockDocsWithField; - @Mock - Collator mockCollator; - - @Before - public void setUp() - { - initMocks(this); - reset(mockDocTerms, mockDocsWithField); - textSortFieldComparator.bottom = BOTTOM_STRING; - } - - /** Check that a zero length term is sorted before a populated field. */ - @Test - public void testCompareBottom_termLengthZeroAndDocDoesntHaveField() - { - // Set up the document to have an empty term. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - int result = textSortFieldComparator.compareBottom(DOC); - - assertEquals("Expected value for doc to be null, and so it to be sorted before BOTTOM_TERM", 1, result); - } - - /** - * Check the behaviour of compareBottom when docsWithField is null (this happens when all documents contain the - * field). - */ - @Test - public void testCompareBottom_nullDocsWithField() - { - // Set docsWithField to null to simulate all documents containing the field. - Bits oldValue = textSortFieldComparator.docsWithField; - textSortFieldComparator.docsWithField = null; - - // Set up the document to have an empty term. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - // Expect the EMPTY_TERM to be compared - verify(mockCollator).compare(BOTTOM_STRING, ""); - - // Reset docsWithField with the mock after the test. - textSortFieldComparator.docsWithField = oldValue; - } - - /** Check that if the doc has a value then it is compared with the existing value. */ - @Test - public void testCompareBottom_populatedTerm() - { - // Set up the document to have "Some value" for the field. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("Some value")); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - verify(mockCollator).compare(BOTTOM_STRING, "Some value"); - } - - /** Check the behaviour if the term is encoded. */ - @Test - public void testCompareBottom_encodedTerm() - { - // Set up the document to have an encoded value for the field. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("\u0000Ignored\u0000Value")); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - verify(mockCollator).compare(BOTTOM_STRING, "Value"); - } - - @Test - public void testCompareValues_nullLessThanString() - { - int result = textSortFieldComparator.compareValues(null, "NotNull"); - assertEquals("Expected null to be 'less' than string.", -1, result); - } - - @Test - public void testCompareValues_stringGreaterThanNull() - { - int result = textSortFieldComparator.compareValues("NotNull", null); - assertEquals("Expected string to be 'greater' than null.", 1, result); - } - - @Test - public void testCompareValues_nullEqualToNull() - { - int result = textSortFieldComparator.compareValues(null, null); - assertEquals("Expected two null values to be equal.", 0, result); - } - - /** Check that when two non-null strings are compared then the underlying collator is used to get the result. */ - @Test - public void testCompareValues_twoStringsCompared() - { - // An arbitrary value to be returned by the collator. - int comparisonResult = 10; - when(mockCollator.compare("NotNull1", "NotNull2")).thenReturn(comparisonResult); - - // Call the method under test. - int result = textSortFieldComparator.compareValues("NotNull1", "NotNull2"); - - verify(mockCollator).compare("NotNull1", "NotNull2"); - assertEquals("Expected result to be obtained from collator.", comparisonResult, result); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.text.Collator; +import java.util.Locale; + +import org.alfresco.solr.AlfrescoCollatableTextFieldType.TextSortFieldComparator; +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** Unit tests for {@link AlfrescoCollatableTextFieldType}. */ +public class AlfrescoCollatableTextFieldTypeTest +{ + private static final int NUM_HITS = 3; + private static final String FIELD = "field"; + private static final Locale LOCALE = Locale.getDefault(); + /** A document id. */ + private static final int DOC = 0; + /** A value for the current bottom document. */ + private static final String BOTTOM_STRING = "Bottom"; + + @InjectMocks + TextSortFieldComparator textSortFieldComparator = new TextSortFieldComparator(NUM_HITS, FIELD, LOCALE); + @Mock + BinaryDocValues mockDocTerms; + @Mock + Bits mockDocsWithField; + @Mock + Collator mockCollator; + + @Before + public void setUp() + { + initMocks(this); + reset(mockDocTerms, mockDocsWithField); + textSortFieldComparator.bottom = BOTTOM_STRING; + } + + /** Check that a zero length term is sorted before a populated field. */ + @Test + public void testCompareBottom_termLengthZeroAndDocDoesntHaveField() + { + // Set up the document to have an empty term. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + int result = textSortFieldComparator.compareBottom(DOC); + + assertEquals("Expected value for doc to be null, and so it to be sorted before BOTTOM_TERM", 1, result); + } + + /** + * Check the behaviour of compareBottom when docsWithField is null (this happens when all documents contain the + * field). + */ + @Test + public void testCompareBottom_nullDocsWithField() + { + // Set docsWithField to null to simulate all documents containing the field. + Bits oldValue = textSortFieldComparator.docsWithField; + textSortFieldComparator.docsWithField = null; + + // Set up the document to have an empty term. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + // Expect the EMPTY_TERM to be compared + verify(mockCollator).compare(BOTTOM_STRING, ""); + + // Reset docsWithField with the mock after the test. + textSortFieldComparator.docsWithField = oldValue; + } + + /** Check that if the doc has a value then it is compared with the existing value. */ + @Test + public void testCompareBottom_populatedTerm() + { + // Set up the document to have "Some value" for the field. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("Some value")); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + verify(mockCollator).compare(BOTTOM_STRING, "Some value"); + } + + /** Check the behaviour if the term is encoded. */ + @Test + public void testCompareBottom_encodedTerm() + { + // Set up the document to have an encoded value for the field. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("\u0000Ignored\u0000Value")); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + verify(mockCollator).compare(BOTTOM_STRING, "Value"); + } + + @Test + public void testCompareValues_nullLessThanString() + { + int result = textSortFieldComparator.compareValues(null, "NotNull"); + assertEquals("Expected null to be 'less' than string.", -1, result); + } + + @Test + public void testCompareValues_stringGreaterThanNull() + { + int result = textSortFieldComparator.compareValues("NotNull", null); + assertEquals("Expected string to be 'greater' than null.", 1, result); + } + + @Test + public void testCompareValues_nullEqualToNull() + { + int result = textSortFieldComparator.compareValues(null, null); + assertEquals("Expected two null values to be equal.", 0, result); + } + + /** Check that when two non-null strings are compared then the underlying collator is used to get the result. */ + @Test + public void testCompareValues_twoStringsCompared() + { + // An arbitrary value to be returned by the collator. + int comparisonResult = 10; + when(mockCollator.compare("NotNull1", "NotNull2")).thenReturn(comparisonResult); + + // Call the method under test. + int result = textSortFieldComparator.compareValues("NotNull1", "NotNull2"); + + verify(mockCollator).compare("NotNull1", "NotNull2"); + assertEquals("Expected result to be obtained from collator.", comparisonResult, result); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java index d380eeeb2..c9018c8db 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -426,4 +426,4 @@ public class AlfrescoCoreAdminHandlerIT verify(spy).newCore(eq(ALFRESCO_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ALFRESCO_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); verify(spy).newCore(eq(VERSION_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(VERSION_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerTest.java index 4eb97a2e5..6982c4db9 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -1158,4 +1158,4 @@ public class AlfrescoCoreAdminHandlerTest NamedList actionResponse = admin.actionFIX(params); assertEquals(parameterValue, actionResponse.get(parameterName)); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoIndexingStatePersistenceAcrossReloadsIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoIndexingStatePersistenceAcrossReloadsIT.java index 6a7f8c9b6..e3e98f412 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoIndexingStatePersistenceAcrossReloadsIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoIndexingStatePersistenceAcrossReloadsIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -177,4 +177,4 @@ public class AlfrescoIndexingStatePersistenceAcrossReloadsIT extends AbstractAlf { return admin.getScheduler().getJobsCount(); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoJsonQueryRequest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoJsonQueryRequest.java index 9dea4b66b..fe79c42aa 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoJsonQueryRequest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoJsonQueryRequest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrConstants.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrConstants.java index fc745aebe..9ccc2d3b4 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrConstants.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrConstants.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -78,4 +78,4 @@ public interface AlfrescoSolrConstants QName MULTIPLE_DATE = QName.createQName(CMIS_TEST_NAMESPACE, "multipleDate"); QName SINGLE_DATETIME = QName.createQName(CMIS_TEST_NAMESPACE, "singleDatetime"); QName MULTIPLE_DATETIME = QName.createQName(CMIS_TEST_NAMESPACE, "multipleDatetime"); -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrReloadIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrReloadIT.java index 0b13baafa..241516e3b 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrReloadIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrReloadIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -153,4 +153,4 @@ public class AlfrescoSolrReloadIT extends AbstractAlfrescoSolrIT { return admin.getScheduler().getJobsCount(); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java index 30eb6e3a6..02b4f7bac 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -968,4 +968,4 @@ public class AlfrescoSolrUtils .map(AlfrescoCoreAdminHandler.class::cast) .orElseThrow(() -> new IllegalStateException("Cannot retrieve the Core Admin Handler on this test core.")); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoTrackerRegistrationIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoTrackerRegistrationIT.java index ef84250d3..9232488f4 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoTrackerRegistrationIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoTrackerRegistrationIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CMISDataCreatorTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CMISDataCreatorTest.java index 77278b0d3..74bf1eac7 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CMISDataCreatorTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CMISDataCreatorTest.java @@ -1,450 +1,450 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import java.io.ByteArrayInputStream; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import junit.framework.TestCase; - -import org.alfresco.util.GUID; -import org.alfresco.util.ISO8601DateFormat; -import org.apache.chemistry.opencmis.client.api.CmisObject; -import org.apache.chemistry.opencmis.client.api.Document; -import org.apache.chemistry.opencmis.client.api.Folder; -import org.apache.chemistry.opencmis.client.api.ItemIterable; -import org.apache.chemistry.opencmis.client.api.ObjectId; -import org.apache.chemistry.opencmis.client.api.QueryResult; -import org.apache.chemistry.opencmis.client.api.Repository; -import org.apache.chemistry.opencmis.client.api.Session; -import org.apache.chemistry.opencmis.client.api.SessionFactory; -import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl; -import org.apache.chemistry.opencmis.commons.BasicPermissions; -import org.apache.chemistry.opencmis.commons.PropertyIds; -import org.apache.chemistry.opencmis.commons.SessionParameter; -import org.apache.chemistry.opencmis.commons.data.Ace; -import org.apache.chemistry.opencmis.commons.enums.AclPropagation; -import org.apache.chemistry.opencmis.commons.enums.BindingType; -import org.apache.chemistry.opencmis.commons.enums.VersioningState; -import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlEntryImpl; -import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl; -import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; - -/** - * @author Andy - * - */ -public class CMISDataCreatorTest extends TestCase -{ - private static Session getSession(String user, String pwd) - { - String url = "http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/atom"; - - SessionFactory factory = SessionFactoryImpl.newInstance(); - Map parameter = new HashMap(); - parameter.put(SessionParameter.USER, user); - parameter.put(SessionParameter.PASSWORD, pwd); - parameter.put(SessionParameter.ATOMPUB_URL, url); - parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value()); - - List repositories = factory.getRepositories(parameter); - Session session = repositories.get(0).createSession(); - - return session; - } - - private String getRootFolderName() - { - return "CMIS-Data-Creator-Test-"+System.currentTimeMillis(); - } - - private String getUniqueName() - { - return "CMIS-Data-Creator-Test-"+GUID.generate(); - } - - public void testCreate() - { - Session session = getSession("admin", "admin"); - - String folderName = getRootFolderName(); - Folder root = session.getRootFolder(); - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderName); - - // create the folder - Folder newFolder = root.createFolder(properties); - - for(int i = 0; i < 50; i++) - { - AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl("user"+i); - List permissions = new ArrayList(1); - permissions.add(BasicPermissions.READ); - List addAces = new ArrayList(1); - addAces.add(new AccessControlEntryImpl(principal, permissions)); - newFolder.addAcl(addAces, AclPropagation.PROPAGATE); - - Map updateProperties = new HashMap(); - updateProperties.put("cm:title", "Update title "+i); - newFolder.updateProperties(properties); - - if(i % 10 == 0) - { - System.out.println("@ "+i); - } - } - ItemIterable result = session.query("select * from cmis:folder", false); - assertTrue(result.getTotalNumItems() > 0); - - result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - assertTrue(result.getTotalNumItems() > 0); - - } - - public void testCreateLots() throws Exception - { - Session session = getSession("admin", "admin"); - - Folder root = session.getRootFolder(); - String folderNameBase = getRootFolderName(); - - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderNameBase); - - Folder base = root.createFolder(properties); - for(int i = 0; i < 10; i++) - { - AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl(""+i+i+i); - List permissions = new ArrayList(1); - permissions.add(BasicPermissions.ALL); - List addAces = new ArrayList(1); - addAces.add(new AccessControlEntryImpl(principal, permissions)); - base.addAcl(addAces, AclPropagation.PROPAGATE); - } - - - Thread last = null; - - for(int i = 0; i < 10; i++) - { - Creator creator = new Creator(base.getPath(), i); - Thread thread = new Thread(creator); - thread.start(); - last = thread; - } - - if(last != null) - { - last.join(); - } - - ItemIterable result = session.query("select * from cmis:folder", false); - assertTrue(result.getTotalNumItems() > 0); - - //result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - //assertTrue(result.getTotalNumItems() > 0); - - } - - private static class Creator implements Runnable - { - int id; - - String path; - - Creator(String path, int id) - { - this.path = path; - this.id = id; - } - - /* (non-Javadoc) - * @see java.lang.Runnable#run() - */ - @Override - public void run() - { - Session session = getSession(""+id+id+id, ""+id+id+id); - - CmisObject object = session.getObjectByPath(path); - - Map baseProps = new HashMap(); - baseProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - baseProps.put(PropertyIds.NAME, "Thread"+id); - - - ObjectId base = session.createFolder(baseProps, object); - - for(int i = 0; i < 100; i++) - { - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, "Folder-"+i); - - ObjectId folder = session.createFolder(properties, base); - - System.out.println("Thread "+id +" @Folder "+i); - - for(int j = 0; j < 1000; j++) - { - Map folderProps = new HashMap(); - folderProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - folderProps.put(PropertyIds.NAME, "Folder-"+i); - - ObjectId child = session.createFolder(folderProps, folder); - - -// Map docProps = new HashMap(); -// docProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); -// docProps.put(PropertyIds.NAME, "Doc-"+j); -// -// ContentStreamImpl contentSream = new ContentStreamImpl(); -// contentSream.setFileName(GUID.generate()); -// contentSream.setLength(BigInteger.valueOf(10)); -// contentSream.setMimeType("text/plain"); -// contentSream.setStream(new StringInputStream("abcdefghij")); -// -// ObjectId document = session.createDocument(docProps, folder, contentSream, VersioningState.MAJOR); - - if(j % 20 == 0) - { - System.out.println(id+" @ "+j); - } - } - } - - } - - } - - - public void testQueryFolderProperties() - { - Session session = getSession("admin", "admin"); - - String folderName = getRootFolderName(); - Folder root = session.getRootFolder(); - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderName); - - // create the folder - Folder newFolder = root.createFolder(properties); - - ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - assertEquals(1, result.getTotalNumItems()); - - - String uniqueName = getUniqueName(); - Map uProperties = new HashMap(); - uProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - uProperties.put(PropertyIds.NAME, uniqueName); - Folder uniqueFolder = newFolder.createFolder(uProperties); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"'", false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND IN_FOLDER('"+ uniqueFolder.getParentId() + "')" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where IN_FOLDER('"+ uniqueFolder.getParentId() + "')" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:createdBy = '"+ uniqueFolder.getCreatedBy()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:objectId = '"+ uniqueFolder.getId()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:lastModifiedBy = '"+ uniqueFolder.getLastModifiedBy()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+ uniqueFolder.getName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - String creationDate = ISO8601DateFormat.format(uniqueFolder.getCreationDate().getTime()); - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:creationDate = '"+ creationDate +"'" , false); - assertEquals(1, result.getTotalNumItems()); - - String modificationDate = ISO8601DateFormat.format(uniqueFolder.getLastModificationDate().getTime()); - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:lastModificationDate = '"+ modificationDate+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:objectTypeId = '"+ uniqueFolder.getType().getQueryName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:baseTypeId = '"+ uniqueFolder.getBaseType().getQueryName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - } - - public void testQueryDocumentProperties() throws Exception - { - Session session = getSession("admin", "admin"); - - String folderName = getRootFolderName(); - Folder root = session.getRootFolder(); - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderName); - - // create the folder - Folder newFolder = root.createFolder(properties); - - ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - assertEquals(1, result.getTotalNumItems()); - - - Document uniqueDocument = createUniqueDocument(newFolder); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"'", false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND IN_FOLDER('"+ newFolder.getId() + "')" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where IN_FOLDER('"+ newFolder.getId() + "')" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:createdBy = '"+ uniqueDocument.getCreatedBy()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:objectId = '"+ uniqueDocument.getId()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:lastModifiedBy = '"+ uniqueDocument.getLastModifiedBy()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+ uniqueDocument.getName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - String creationDate = ISO8601DateFormat.format(uniqueDocument.getCreationDate().getTime()); - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:creationDate = '"+ creationDate +"'" , false); - assertEquals(1, result.getTotalNumItems()); - - String modificationDate = ISO8601DateFormat.format(uniqueDocument.getLastModificationDate().getTime()); - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:lastModificationDate = '"+ modificationDate+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:objectTypeId = '"+ uniqueDocument.getType().getQueryName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:baseTypeId = '"+ uniqueDocument.getBaseType().getQueryName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamFileName = '"+ uniqueDocument.getContentStreamFileName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamLength = '"+ uniqueDocument.getContentStreamLength()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamMimeType = '"+ uniqueDocument.getContentStreamMimeType()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - } - - public void testQueryForMultipleDocumentProperties() throws Exception - { - Session session = getSession("admin", "admin"); - - String folderName = getRootFolderName(); - Folder root = session.getRootFolder(); - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderName); - - // create the folder - Folder newFolder = root.createFolder(properties); - - ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - assertEquals(1, result.getTotalNumItems()); - - - Document document1 = createUniqueDocument(newFolder); - Document document2 = createUniqueDocument(newFolder); - - //result = session.query("SELECT * FROM cmis:folder d join sys:hidden t on d.cmis:objectId = t.cmis:objectId", false); - //assertTrue(0 < result.getTotalNumItems()); - - //result = session.query("SELECT * FROM cmis:folder d join sys:hidden t on d.cmis:objectId = t.cmis:objectId where t.sys:cascadeHidden = false", false); - //assertTrue(0 < result.getTotalNumItems()); - - result = session.query("SELECT * FROM cmis:document d join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); - long count = result.getTotalNumItems(); - - Map props = new HashMap(); - props.put("exif:focalLength", 10.33d); - ArrayList aspects = new ArrayList(); - aspects.add("P:exif:exif"); - props.put("cmis:secondaryObjectTypeIds", aspects); - document1.updateProperties(props); - - result = session.query("SELECT * FROM cmis:document d join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); - assertEquals(count+1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+document1.getName()+"' OR cmis:name = '"+document2.getName()+"'", false); - assertEquals(2, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+document1.getName()+"' AND cmis:contentStreamLength = 5 AND cmis:contentStreamMimeType = 'text/plain'", false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("SELECT * FROM cmis:document", false); - long docCount = result.getTotalNumItems(); - - result = session.query("SELECT * FROM cmis:document d left outer join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); - assertEquals(docCount, result.getTotalNumItems()); - } - - private Document createUniqueDocument(Folder newFolder) - throws UnsupportedEncodingException { - String uniqueName = getUniqueName(); - Map uProperties = new HashMap(); - uProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); - uProperties.put(PropertyIds.NAME, uniqueName); - - ContentStreamImpl contentStream = new ContentStreamImpl(); - contentStream.setFileName("bob"); - String shortString = "short"; - contentStream.setStream(new ByteArrayInputStream(shortString.getBytes("UTF-8"))); - contentStream.setLength(new BigInteger("5")); - contentStream.setMimeType("text/plain"); - - Document uniqueDocument = newFolder.createDocument(uProperties, contentStream, VersioningState.MAJOR); - return uniqueDocument; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.alfresco.util.GUID; +import org.alfresco.util.ISO8601DateFormat; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.chemistry.opencmis.client.api.Document; +import org.apache.chemistry.opencmis.client.api.Folder; +import org.apache.chemistry.opencmis.client.api.ItemIterable; +import org.apache.chemistry.opencmis.client.api.ObjectId; +import org.apache.chemistry.opencmis.client.api.QueryResult; +import org.apache.chemistry.opencmis.client.api.Repository; +import org.apache.chemistry.opencmis.client.api.Session; +import org.apache.chemistry.opencmis.client.api.SessionFactory; +import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl; +import org.apache.chemistry.opencmis.commons.BasicPermissions; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.SessionParameter; +import org.apache.chemistry.opencmis.commons.data.Ace; +import org.apache.chemistry.opencmis.commons.enums.AclPropagation; +import org.apache.chemistry.opencmis.commons.enums.BindingType; +import org.apache.chemistry.opencmis.commons.enums.VersioningState; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlEntryImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; + +/** + * @author Andy + * + */ +public class CMISDataCreatorTest extends TestCase +{ + private static Session getSession(String user, String pwd) + { + String url = "http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/atom"; + + SessionFactory factory = SessionFactoryImpl.newInstance(); + Map parameter = new HashMap(); + parameter.put(SessionParameter.USER, user); + parameter.put(SessionParameter.PASSWORD, pwd); + parameter.put(SessionParameter.ATOMPUB_URL, url); + parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value()); + + List repositories = factory.getRepositories(parameter); + Session session = repositories.get(0).createSession(); + + return session; + } + + private String getRootFolderName() + { + return "CMIS-Data-Creator-Test-"+System.currentTimeMillis(); + } + + private String getUniqueName() + { + return "CMIS-Data-Creator-Test-"+GUID.generate(); + } + + public void testCreate() + { + Session session = getSession("admin", "admin"); + + String folderName = getRootFolderName(); + Folder root = session.getRootFolder(); + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderName); + + // create the folder + Folder newFolder = root.createFolder(properties); + + for(int i = 0; i < 50; i++) + { + AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl("user"+i); + List permissions = new ArrayList(1); + permissions.add(BasicPermissions.READ); + List addAces = new ArrayList(1); + addAces.add(new AccessControlEntryImpl(principal, permissions)); + newFolder.addAcl(addAces, AclPropagation.PROPAGATE); + + Map updateProperties = new HashMap(); + updateProperties.put("cm:title", "Update title "+i); + newFolder.updateProperties(properties); + + if(i % 10 == 0) + { + System.out.println("@ "+i); + } + } + ItemIterable result = session.query("select * from cmis:folder", false); + assertTrue(result.getTotalNumItems() > 0); + + result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + assertTrue(result.getTotalNumItems() > 0); + + } + + public void testCreateLots() throws Exception + { + Session session = getSession("admin", "admin"); + + Folder root = session.getRootFolder(); + String folderNameBase = getRootFolderName(); + + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderNameBase); + + Folder base = root.createFolder(properties); + for(int i = 0; i < 10; i++) + { + AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl(""+i+i+i); + List permissions = new ArrayList(1); + permissions.add(BasicPermissions.ALL); + List addAces = new ArrayList(1); + addAces.add(new AccessControlEntryImpl(principal, permissions)); + base.addAcl(addAces, AclPropagation.PROPAGATE); + } + + + Thread last = null; + + for(int i = 0; i < 10; i++) + { + Creator creator = new Creator(base.getPath(), i); + Thread thread = new Thread(creator); + thread.start(); + last = thread; + } + + if(last != null) + { + last.join(); + } + + ItemIterable result = session.query("select * from cmis:folder", false); + assertTrue(result.getTotalNumItems() > 0); + + //result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + //assertTrue(result.getTotalNumItems() > 0); + + } + + private static class Creator implements Runnable + { + int id; + + String path; + + Creator(String path, int id) + { + this.path = path; + this.id = id; + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() + { + Session session = getSession(""+id+id+id, ""+id+id+id); + + CmisObject object = session.getObjectByPath(path); + + Map baseProps = new HashMap(); + baseProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + baseProps.put(PropertyIds.NAME, "Thread"+id); + + + ObjectId base = session.createFolder(baseProps, object); + + for(int i = 0; i < 100; i++) + { + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, "Folder-"+i); + + ObjectId folder = session.createFolder(properties, base); + + System.out.println("Thread "+id +" @Folder "+i); + + for(int j = 0; j < 1000; j++) + { + Map folderProps = new HashMap(); + folderProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + folderProps.put(PropertyIds.NAME, "Folder-"+i); + + ObjectId child = session.createFolder(folderProps, folder); + + +// Map docProps = new HashMap(); +// docProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); +// docProps.put(PropertyIds.NAME, "Doc-"+j); +// +// ContentStreamImpl contentSream = new ContentStreamImpl(); +// contentSream.setFileName(GUID.generate()); +// contentSream.setLength(BigInteger.valueOf(10)); +// contentSream.setMimeType("text/plain"); +// contentSream.setStream(new StringInputStream("abcdefghij")); +// +// ObjectId document = session.createDocument(docProps, folder, contentSream, VersioningState.MAJOR); + + if(j % 20 == 0) + { + System.out.println(id+" @ "+j); + } + } + } + + } + + } + + + public void testQueryFolderProperties() + { + Session session = getSession("admin", "admin"); + + String folderName = getRootFolderName(); + Folder root = session.getRootFolder(); + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderName); + + // create the folder + Folder newFolder = root.createFolder(properties); + + ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + assertEquals(1, result.getTotalNumItems()); + + + String uniqueName = getUniqueName(); + Map uProperties = new HashMap(); + uProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + uProperties.put(PropertyIds.NAME, uniqueName); + Folder uniqueFolder = newFolder.createFolder(uProperties); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"'", false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND IN_FOLDER('"+ uniqueFolder.getParentId() + "')" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where IN_FOLDER('"+ uniqueFolder.getParentId() + "')" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:createdBy = '"+ uniqueFolder.getCreatedBy()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:objectId = '"+ uniqueFolder.getId()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:lastModifiedBy = '"+ uniqueFolder.getLastModifiedBy()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+ uniqueFolder.getName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + String creationDate = ISO8601DateFormat.format(uniqueFolder.getCreationDate().getTime()); + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:creationDate = '"+ creationDate +"'" , false); + assertEquals(1, result.getTotalNumItems()); + + String modificationDate = ISO8601DateFormat.format(uniqueFolder.getLastModificationDate().getTime()); + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:lastModificationDate = '"+ modificationDate+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:objectTypeId = '"+ uniqueFolder.getType().getQueryName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:baseTypeId = '"+ uniqueFolder.getBaseType().getQueryName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + } + + public void testQueryDocumentProperties() throws Exception + { + Session session = getSession("admin", "admin"); + + String folderName = getRootFolderName(); + Folder root = session.getRootFolder(); + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderName); + + // create the folder + Folder newFolder = root.createFolder(properties); + + ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + assertEquals(1, result.getTotalNumItems()); + + + Document uniqueDocument = createUniqueDocument(newFolder); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"'", false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND IN_FOLDER('"+ newFolder.getId() + "')" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where IN_FOLDER('"+ newFolder.getId() + "')" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:createdBy = '"+ uniqueDocument.getCreatedBy()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:objectId = '"+ uniqueDocument.getId()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:lastModifiedBy = '"+ uniqueDocument.getLastModifiedBy()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+ uniqueDocument.getName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + String creationDate = ISO8601DateFormat.format(uniqueDocument.getCreationDate().getTime()); + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:creationDate = '"+ creationDate +"'" , false); + assertEquals(1, result.getTotalNumItems()); + + String modificationDate = ISO8601DateFormat.format(uniqueDocument.getLastModificationDate().getTime()); + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:lastModificationDate = '"+ modificationDate+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:objectTypeId = '"+ uniqueDocument.getType().getQueryName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:baseTypeId = '"+ uniqueDocument.getBaseType().getQueryName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamFileName = '"+ uniqueDocument.getContentStreamFileName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamLength = '"+ uniqueDocument.getContentStreamLength()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamMimeType = '"+ uniqueDocument.getContentStreamMimeType()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + } + + public void testQueryForMultipleDocumentProperties() throws Exception + { + Session session = getSession("admin", "admin"); + + String folderName = getRootFolderName(); + Folder root = session.getRootFolder(); + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderName); + + // create the folder + Folder newFolder = root.createFolder(properties); + + ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + assertEquals(1, result.getTotalNumItems()); + + + Document document1 = createUniqueDocument(newFolder); + Document document2 = createUniqueDocument(newFolder); + + //result = session.query("SELECT * FROM cmis:folder d join sys:hidden t on d.cmis:objectId = t.cmis:objectId", false); + //assertTrue(0 < result.getTotalNumItems()); + + //result = session.query("SELECT * FROM cmis:folder d join sys:hidden t on d.cmis:objectId = t.cmis:objectId where t.sys:cascadeHidden = false", false); + //assertTrue(0 < result.getTotalNumItems()); + + result = session.query("SELECT * FROM cmis:document d join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); + long count = result.getTotalNumItems(); + + Map props = new HashMap(); + props.put("exif:focalLength", 10.33d); + ArrayList aspects = new ArrayList(); + aspects.add("P:exif:exif"); + props.put("cmis:secondaryObjectTypeIds", aspects); + document1.updateProperties(props); + + result = session.query("SELECT * FROM cmis:document d join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); + assertEquals(count+1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+document1.getName()+"' OR cmis:name = '"+document2.getName()+"'", false); + assertEquals(2, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+document1.getName()+"' AND cmis:contentStreamLength = 5 AND cmis:contentStreamMimeType = 'text/plain'", false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("SELECT * FROM cmis:document", false); + long docCount = result.getTotalNumItems(); + + result = session.query("SELECT * FROM cmis:document d left outer join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); + assertEquals(docCount, result.getTotalNumItems()); + } + + private Document createUniqueDocument(Folder newFolder) + throws UnsupportedEncodingException { + String uniqueName = getUniqueName(); + Map uProperties = new HashMap(); + uProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); + uProperties.put(PropertyIds.NAME, uniqueName); + + ContentStreamImpl contentStream = new ContentStreamImpl(); + contentStream.setFileName("bob"); + String shortString = "short"; + contentStream.setStream(new ByteArrayInputStream(shortString.getBytes("UTF-8"))); + contentStream.setLength(new BigInteger("5")); + contentStream.setMimeType("text/plain"); + + Document uniqueDocument = newFolder.createDocument(uProperties, contentStream, VersioningState.MAJOR); + return uniqueDocument; + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateUpdateDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateUpdateDistributedIT.java index 5eb36a952..6a528ab60 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateUpdateDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateUpdateDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -217,4 +217,4 @@ public class CoresCreateUpdateDistributedIT extends AbstractAlfrescoDistributedI coreAdminHandler.handleCustomAction(request, response); TimeUnit.SECONDS.sleep(2); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateViaPropertyIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateViaPropertyIT.java index 01a1562d4..c6ba3df38 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateViaPropertyIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateViaPropertyIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/DynamicCopyFieldsIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/DynamicCopyFieldsIT.java index 7b75204a0..855600ce6 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/DynamicCopyFieldsIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/DynamicCopyFieldsIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/HandlerOfResourcesTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/HandlerOfResourcesTest.java index 669f6cd20..737c57f68 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/HandlerOfResourcesTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/HandlerOfResourcesTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -74,4 +74,4 @@ public class HandlerOfResourcesTest assertFalse(HandlerOfResources.allowedProperties(props, HandlerOfResources.DISALLOWED_SHARED_UPDATES)); props.remove("alfresco.cross.locale.datatype.2"); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrDataModelTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrDataModelTest.java index 590b6398c..4ab9d6069 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrDataModelTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrDataModelTest.java @@ -1,56 +1,56 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import org.alfresco.service.namespace.QName; -import org.junit.Test; - -/** - * @author Andy - * - */ -public class SolrDataModelTest -{ - private static final QName CONTENT_STREAM_LENGTH = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}contentStreamLength"); - private static final QName IS_PRIVATE_WOKING_COPY = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}isPrivateWorkingCopy"); - private static final QName IS_IMMUTABLE = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}isImmutable"); - private static final QName CREATION_DATE = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}creationDate"); - private static final QName NAME = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}name"); - private static QName OBJECT_ID = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}objectId"); - - @Test - public void testParseTransactionId() - { - Long expectedId = 94032903249l; - String id = AlfrescoSolrDataModel.getTransactionDocumentId(expectedId); - Long actualId = AlfrescoSolrDataModel.parseTransactionId(id); - assertEquals(expectedId, actualId); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.alfresco.service.namespace.QName; +import org.junit.Test; + +/** + * @author Andy + * + */ +public class SolrDataModelTest +{ + private static final QName CONTENT_STREAM_LENGTH = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}contentStreamLength"); + private static final QName IS_PRIVATE_WOKING_COPY = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}isPrivateWorkingCopy"); + private static final QName IS_IMMUTABLE = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}isImmutable"); + private static final QName CREATION_DATE = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}creationDate"); + private static final QName NAME = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}name"); + private static QName OBJECT_ID = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}objectId"); + + @Test + public void testParseTransactionId() + { + Long expectedId = 94032903249l; + String id = AlfrescoSolrDataModel.getTransactionDocumentId(expectedId); + Long actualId = AlfrescoSolrDataModel.parseTransactionId(id); + assertEquals(expectedId, actualId); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrITInitializer.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrITInitializer.java index 24d744f00..da6cb1430 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrITInitializer.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrITInitializer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -610,4 +610,4 @@ public abstract class SolrITInitializer extends SolrTestCaseJ4 } } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrInformationServerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrInformationServerTest.java index 2e4ba4f21..fe4253a51 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrInformationServerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrInformationServerTest.java @@ -1,421 +1,421 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import static java.util.Optional.ofNullable; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ANY; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ASSOC_REF; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.BOOLEAN; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CATEGORY; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CHILD_ASSOC_REF; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CONTENT; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.DOUBLE; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ENCRYPTED; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.FLOAT; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.INT; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LOCALE; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LONG; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.MLTEXT; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.NODE_REF; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PATH; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PERIOD; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.QNAME; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.TEXT; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_HOUR_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MONTH_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_SECOND_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_YEAR_FIELD_SUFFIX; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.solr.client.NodeMetaData; -import org.alfresco.solr.client.SOLRAPIClient; -import org.apache.solr.common.SolrDocument; -import org.apache.solr.common.SolrInputDocument; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.core.SolrCore; -import org.apache.solr.core.SolrResourceLoader; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.request.SolrRequestHandler; -import org.apache.solr.response.SolrQueryResponse; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -/** - * Unit tests for the {@link SolrInformationServer} class. - * - * @author Matt Ward - * @author Andrea Gazzarini - */ -@RunWith(MockitoJUnitRunner.class) -public class SolrInformationServerTest -{ - private SolrInformationServer infoServer; - - @Mock - private AlfrescoCoreAdminHandler adminHandler; - - @Mock - private SolrResourceLoader resourceLoader; - - @Mock - private SolrCore core; - - @Mock - private SOLRAPIClient client; - - @Mock - private SolrRequestHandler handler; - - @Mock - private SolrQueryResponse response; - - private SolrQueryRequest request; - - @Before - public void setUp() - { - when(core.getResourceLoader()).thenReturn(resourceLoader); - when(resourceLoader.getCoreProperties()).thenReturn(new Properties()); - infoServer = new SolrInformationServer(adminHandler, core, client) - { - // @Override - SolrQueryResponse newSolrQueryResponse() - { - return response; - } - }; - - request = infoServer.newSolrQueryRequest(); - } - - @Test - public void testGetStateOk() - { - String id = String.valueOf(System.currentTimeMillis()); - - SolrDocument state = new SolrDocument(); - - SimpleOrderedMap responseContent = new SimpleOrderedMap<>(); - responseContent.add(SolrInformationServer.RESPONSE_DEFAULT_ID, state); - - when(response.getValues()).thenReturn(responseContent); - when(core.getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET)).thenReturn(handler); - - SolrDocument document = infoServer.getState(core, request, id); - - assertEquals(id, request.getParams().get(CommonParams.ID)); - verify(core).getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET); - verify(response).getValues(); - - assertSame(state, document); - } - - @Test - public void setUnitOfTimeFieldsWithDatetimeField_shouldSetDateAndTimeFields() - { - DataTypeDefinition datatype = mock(DataTypeDefinition.class); - when(datatype.getName()).thenReturn(DataTypeDefinition.DATETIME); - - SolrInputDocument document = new SolrInputDocument(); - - String fieldPrefix = "datetime@sd@"; - String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created"; - String sourceFieldName = fieldPrefix + fieldSuffix; - - infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype); - - assertEquals(1972, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX)); - assertEquals(3, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX)); - assertEquals(9, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX)); - assertEquals(16, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX)); - assertEquals(17, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX)); - assertEquals(33, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX)); - assertEquals(18, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX)); - } - - @Test - public void setUnitOfTimeFieldsWithDatetimeField_shouldSetOnlyDateFields() - { - DataTypeDefinition datatype = mock(DataTypeDefinition.class); - when(datatype.getName()).thenReturn(DataTypeDefinition.DATE); - - SolrInputDocument document = new SolrInputDocument(); - - String fieldPrefix = "datetime@sd@"; - String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created"; - String sourceFieldName = fieldPrefix + fieldSuffix; - - infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype); - - assertEquals(1972, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX)); - assertEquals(3, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX)); - assertEquals(9, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX)); - assertEquals(16, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX)); - - assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX)); - assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX)); - assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX)); - } - - @Test - public void destructuringCannotBeAppliedToMultivaluedFields() - { - PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class); - when(multiValuedProperty.isMultiValued()).thenReturn(true); - - assertFalse(infoServer.canBeDestructured(multiValuedProperty, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created")); - } - - /** - * Destructuring cannot only be applied to those fields matching a dynamic field in the schema. - * The dynamic fields defined in the schema follow the naming: - * - * [datatype]@[options]@[namespace][local name] - * - * For example - * - * datetime@sd@{http://www.alfresco.org/model/content/1.0}created - * - * SearchServices calls those fields "PROPERTIES", while the static fields (even belonging to a Node) are called - * "FIELDS". - */ - @Test - public void destructuringCannotBeAppliedToProperties() - { - PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class); - when(multiValuedProperty.isMultiValued()).thenReturn(false); - - assertFalse( - "Destructuring can only be applied to Node properties (e.g. dynamic fields in Solr schema)", - infoServer.canBeDestructured(multiValuedProperty, "this_is_a_field_that_doesnt_follow_the_dynamic_naming")); - } - - @Test - public void destructuringCanBeAppliedToDateFields() - { - PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class); - when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false); - - DataTypeDefinition date = mock(DataTypeDefinition.class); - when(date.getName()).thenReturn(DataTypeDefinition.DATE); - when(propertyThatCanBeDestructured.getDataType()).thenReturn(date); - - assertTrue( - "Destructuring must be supported in Date fields!", - infoServer.canBeDestructured(propertyThatCanBeDestructured,"date@sd@{http://www.alfresco.org/model/content/1.0}created")); - } - - @Test - public void destructuringCanBeAppliedToDateTimeFields() - { - PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class); - when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false); - - DataTypeDefinition datetime = mock(DataTypeDefinition.class); - when(datetime.getName()).thenReturn(DataTypeDefinition.DATETIME); - when(propertyThatCanBeDestructured.getDataType()).thenReturn(datetime); - - assertTrue( - "Destructuring must be supported in Datetime fields!", - infoServer.canBeDestructured(propertyThatCanBeDestructured, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created")); - } - - @Test - public void destructuringCanBeAppliedOnlyToDateOrDatetimeFields() - { - Stream.of(ANY,ENCRYPTED,TEXT,MLTEXT,CONTENT,INT,LONG,FLOAT,DOUBLE, - BOOLEAN,QNAME,CATEGORY,NODE_REF,CHILD_ASSOC_REF,ASSOC_REF, - PATH,LOCALE,PERIOD) - .map(qname -> { - PropertyDefinition propertyThatCannotBeDestructured = mock(PropertyDefinition.class); - DataTypeDefinition def = Mockito.mock(DataTypeDefinition.class); - when(def.getName()).thenReturn(qname); - when(propertyThatCannotBeDestructured.getDataType()).thenReturn(def); - return propertyThatCannotBeDestructured;}) - .forEach(property -> assertFalse( - "Destructuring must be supported only on Date or Datetime fields!", - infoServer.canBeDestructured(property, "somedatatype@sd@{http://www.alfresco.org/model/content/1.0}somefield"))); - } - - private int unitOfTimeFieldValue(SolrInputDocument doc, String fieldName) { - return ofNullable(doc.getFieldValue(fieldName)) - .map(Number.class::cast) - .map(Number::intValue) - .orElseThrow(() -> new IllegalArgumentException(fieldName + " hasn't been set.")); - } - - /** - * GetState returns null in case the given id doesn't correspond to an existing state document. - */ - @Test - public void testGetStateWithStateNotFound_returnsNull() - { - String id = String.valueOf(System.currentTimeMillis()); - - SimpleOrderedMap responseContent = new SimpleOrderedMap<>(); - responseContent.add(SolrInformationServer.RESPONSE_DEFAULT_ID, null); - - when(response.getValues()).thenReturn(responseContent); - when(core.getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET)).thenReturn(handler); - - SolrDocument document = infoServer.getState(core, request, id); - - assertEquals(id, request.getParams().get(CommonParams.ID)); - verify(core).getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET); - verify(response).getValues(); - - assertNull(document); - } - - /** - * When storing ANAME and APATH fields, skipping to store duplicated entries should be granted. - * - * Test data has been simplified from a living input use case: - * - * { - * "apath": "/9ea65c3c/1f5eebed/d657ec29/7c7da7c4/3ca56633/85f3f802/5c3a9e15/da781274", - * "path": "/company_home/user_homes/user1/taskers/Tasker-1418058127641/responseSummary-1418332928505/responseSummary-1418332928552/response" - * }, - * { - * "apath": "/9ea65c3c/1f5eebed/d657ec29/572c38fc/4ff94a6e/85f3f802/5c3a9e15/da781274", - * "path": "/company_home/user_homes/user2/taskers/tasker/responseSummary-1418332928505/responseSummary-1418332928552/response" - * }, - * { - * "apath": "/9ea65c3c/1f5eebed/d657ec29/cebd969b/0decd203/85f3f802/5c3a9e15/da781274", - * "path": "/company_home/user_homes/user3/taskers/tasker/responseSummary-1418332928505/responseSummary-1418332928552/response" - * } - */ - @Test - public void testPathsFieldStorage() - { - - SolrInputDocument doc = new SolrInputDocument(); - NodeMetaData nodeMetaData = new NodeMetaData(); - nodeMetaData.setAncestorPaths(List.of("/1/2/4/7/10", "/1/2/5/8/10", "/1/2/6/9/10")); - nodeMetaData.setPaths(List.of()); - - List expectedANames = Stream.of("0/10", - "1/7/10", "1/8/10", "1/9/10", - "2/4/7/10", "2/5/8/10", "2/6/9/10", - "3/2/4/7/10", "3/2/5/8/10", "3/2/6/9/10", - "4/1/2/4/7/10", "4/1/2/5/8/10", "4/1/2/6/9/10", - "F/1/2/4/7/10", "F/1/2/5/8/10", "F/1/2/6/9/10") - .collect(Collectors.toCollection(ArrayList::new)); - - List expectedAPaths = Stream.of("0/1", - "1/1/2", - "2/1/2/4", "2/1/2/5", "2/1/2/6", - "3/1/2/4/7", "3/1/2/5/8", "3/1/2/6/9", - "4/1/2/4/7/10", "4/1/2/5/8/10", "4/1/2/6/9/10", - "F/1/2/4/7/10", "F/1/2/5/8/10", "F/1/2/6/9/10") - .collect(Collectors.toCollection(ArrayList::new)); - - infoServer.updatePathRelatedFields(nodeMetaData, doc); - - List anames = doc.getFieldValues(QueryConstants.FIELD_ANAME).stream() - .map(aname -> aname.toString()) - .collect(Collectors.toList()) - .stream() - .sorted() - .collect(Collectors.toList()); - - assertEquals(expectedANames, anames); - - List apaths = doc.getFieldValues(QueryConstants.FIELD_APATH).stream() - .map(aname -> aname.toString()) - .collect(Collectors.toList()) - .stream() - .sorted() - .collect(Collectors.toList()); - - assertEquals(expectedAPaths, apaths); - - - } - - /** - * Repeat the indexing operation 2 times to verify that updating and existing document - * removes previous information in ANAME and APATH fields - */ - @Test - public void testPathsFieldStorageIterations() - { - - SolrInputDocument doc = new SolrInputDocument(); - NodeMetaData nodeMetaData = new NodeMetaData(); - nodeMetaData.setAncestorPaths(List.of("/1/2/4/7/10", "/1/2/5/8/10", "/1/2/6/9/10")); - nodeMetaData.setPaths(List.of()); - - IntStream.range(0, 2).forEach(i -> - { - - infoServer.updatePathRelatedFields(nodeMetaData, doc); - - assertEquals(doc.getFieldValues(QueryConstants.FIELD_ANAME).size(), 16); - assertEquals(doc.getFieldValues(QueryConstants.FIELD_APATH).size(), 14); - - }); - - } - -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import static java.util.Optional.ofNullable; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ANY; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ASSOC_REF; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.BOOLEAN; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CATEGORY; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CHILD_ASSOC_REF; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CONTENT; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.DOUBLE; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ENCRYPTED; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.FLOAT; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.INT; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LOCALE; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LONG; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.MLTEXT; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.NODE_REF; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PATH; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PERIOD; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.QNAME; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.TEXT; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_HOUR_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MONTH_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_SECOND_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_YEAR_FIELD_SUFFIX; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.solr.client.NodeMetaData; +import org.alfresco.solr.client.SOLRAPIClient; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.SolrCore; +import org.apache.solr.core.SolrResourceLoader; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestHandler; +import org.apache.solr.response.SolrQueryResponse; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Unit tests for the {@link SolrInformationServer} class. + * + * @author Matt Ward + * @author Andrea Gazzarini + */ +@RunWith(MockitoJUnitRunner.class) +public class SolrInformationServerTest +{ + private SolrInformationServer infoServer; + + @Mock + private AlfrescoCoreAdminHandler adminHandler; + + @Mock + private SolrResourceLoader resourceLoader; + + @Mock + private SolrCore core; + + @Mock + private SOLRAPIClient client; + + @Mock + private SolrRequestHandler handler; + + @Mock + private SolrQueryResponse response; + + private SolrQueryRequest request; + + @Before + public void setUp() + { + when(core.getResourceLoader()).thenReturn(resourceLoader); + when(resourceLoader.getCoreProperties()).thenReturn(new Properties()); + infoServer = new SolrInformationServer(adminHandler, core, client) + { + // @Override + SolrQueryResponse newSolrQueryResponse() + { + return response; + } + }; + + request = infoServer.newSolrQueryRequest(); + } + + @Test + public void testGetStateOk() + { + String id = String.valueOf(System.currentTimeMillis()); + + SolrDocument state = new SolrDocument(); + + SimpleOrderedMap responseContent = new SimpleOrderedMap<>(); + responseContent.add(SolrInformationServer.RESPONSE_DEFAULT_ID, state); + + when(response.getValues()).thenReturn(responseContent); + when(core.getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET)).thenReturn(handler); + + SolrDocument document = infoServer.getState(core, request, id); + + assertEquals(id, request.getParams().get(CommonParams.ID)); + verify(core).getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET); + verify(response).getValues(); + + assertSame(state, document); + } + + @Test + public void setUnitOfTimeFieldsWithDatetimeField_shouldSetDateAndTimeFields() + { + DataTypeDefinition datatype = mock(DataTypeDefinition.class); + when(datatype.getName()).thenReturn(DataTypeDefinition.DATETIME); + + SolrInputDocument document = new SolrInputDocument(); + + String fieldPrefix = "datetime@sd@"; + String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created"; + String sourceFieldName = fieldPrefix + fieldSuffix; + + infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype); + + assertEquals(1972, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX)); + assertEquals(3, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX)); + assertEquals(9, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX)); + assertEquals(16, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX)); + assertEquals(17, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX)); + assertEquals(33, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX)); + assertEquals(18, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX)); + } + + @Test + public void setUnitOfTimeFieldsWithDatetimeField_shouldSetOnlyDateFields() + { + DataTypeDefinition datatype = mock(DataTypeDefinition.class); + when(datatype.getName()).thenReturn(DataTypeDefinition.DATE); + + SolrInputDocument document = new SolrInputDocument(); + + String fieldPrefix = "datetime@sd@"; + String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created"; + String sourceFieldName = fieldPrefix + fieldSuffix; + + infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype); + + assertEquals(1972, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX)); + assertEquals(3, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX)); + assertEquals(9, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX)); + assertEquals(16, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX)); + + assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX)); + assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX)); + assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX)); + } + + @Test + public void destructuringCannotBeAppliedToMultivaluedFields() + { + PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class); + when(multiValuedProperty.isMultiValued()).thenReturn(true); + + assertFalse(infoServer.canBeDestructured(multiValuedProperty, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created")); + } + + /** + * Destructuring cannot only be applied to those fields matching a dynamic field in the schema. + * The dynamic fields defined in the schema follow the naming: + * + * [datatype]@[options]@[namespace][local name] + * + * For example + * + * datetime@sd@{http://www.alfresco.org/model/content/1.0}created + * + * SearchServices calls those fields "PROPERTIES", while the static fields (even belonging to a Node) are called + * "FIELDS". + */ + @Test + public void destructuringCannotBeAppliedToProperties() + { + PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class); + when(multiValuedProperty.isMultiValued()).thenReturn(false); + + assertFalse( + "Destructuring can only be applied to Node properties (e.g. dynamic fields in Solr schema)", + infoServer.canBeDestructured(multiValuedProperty, "this_is_a_field_that_doesnt_follow_the_dynamic_naming")); + } + + @Test + public void destructuringCanBeAppliedToDateFields() + { + PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class); + when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false); + + DataTypeDefinition date = mock(DataTypeDefinition.class); + when(date.getName()).thenReturn(DataTypeDefinition.DATE); + when(propertyThatCanBeDestructured.getDataType()).thenReturn(date); + + assertTrue( + "Destructuring must be supported in Date fields!", + infoServer.canBeDestructured(propertyThatCanBeDestructured,"date@sd@{http://www.alfresco.org/model/content/1.0}created")); + } + + @Test + public void destructuringCanBeAppliedToDateTimeFields() + { + PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class); + when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false); + + DataTypeDefinition datetime = mock(DataTypeDefinition.class); + when(datetime.getName()).thenReturn(DataTypeDefinition.DATETIME); + when(propertyThatCanBeDestructured.getDataType()).thenReturn(datetime); + + assertTrue( + "Destructuring must be supported in Datetime fields!", + infoServer.canBeDestructured(propertyThatCanBeDestructured, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created")); + } + + @Test + public void destructuringCanBeAppliedOnlyToDateOrDatetimeFields() + { + Stream.of(ANY,ENCRYPTED,TEXT,MLTEXT,CONTENT,INT,LONG,FLOAT,DOUBLE, + BOOLEAN,QNAME,CATEGORY,NODE_REF,CHILD_ASSOC_REF,ASSOC_REF, + PATH,LOCALE,PERIOD) + .map(qname -> { + PropertyDefinition propertyThatCannotBeDestructured = mock(PropertyDefinition.class); + DataTypeDefinition def = Mockito.mock(DataTypeDefinition.class); + when(def.getName()).thenReturn(qname); + when(propertyThatCannotBeDestructured.getDataType()).thenReturn(def); + return propertyThatCannotBeDestructured;}) + .forEach(property -> assertFalse( + "Destructuring must be supported only on Date or Datetime fields!", + infoServer.canBeDestructured(property, "somedatatype@sd@{http://www.alfresco.org/model/content/1.0}somefield"))); + } + + private int unitOfTimeFieldValue(SolrInputDocument doc, String fieldName) { + return ofNullable(doc.getFieldValue(fieldName)) + .map(Number.class::cast) + .map(Number::intValue) + .orElseThrow(() -> new IllegalArgumentException(fieldName + " hasn't been set.")); + } + + /** + * GetState returns null in case the given id doesn't correspond to an existing state document. + */ + @Test + public void testGetStateWithStateNotFound_returnsNull() + { + String id = String.valueOf(System.currentTimeMillis()); + + SimpleOrderedMap responseContent = new SimpleOrderedMap<>(); + responseContent.add(SolrInformationServer.RESPONSE_DEFAULT_ID, null); + + when(response.getValues()).thenReturn(responseContent); + when(core.getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET)).thenReturn(handler); + + SolrDocument document = infoServer.getState(core, request, id); + + assertEquals(id, request.getParams().get(CommonParams.ID)); + verify(core).getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET); + verify(response).getValues(); + + assertNull(document); + } + + /** + * When storing ANAME and APATH fields, skipping to store duplicated entries should be granted. + * + * Test data has been simplified from a living input use case: + * + * { + * "apath": "/9ea65c3c/1f5eebed/d657ec29/7c7da7c4/3ca56633/85f3f802/5c3a9e15/da781274", + * "path": "/company_home/user_homes/user1/taskers/Tasker-1418058127641/responseSummary-1418332928505/responseSummary-1418332928552/response" + * }, + * { + * "apath": "/9ea65c3c/1f5eebed/d657ec29/572c38fc/4ff94a6e/85f3f802/5c3a9e15/da781274", + * "path": "/company_home/user_homes/user2/taskers/tasker/responseSummary-1418332928505/responseSummary-1418332928552/response" + * }, + * { + * "apath": "/9ea65c3c/1f5eebed/d657ec29/cebd969b/0decd203/85f3f802/5c3a9e15/da781274", + * "path": "/company_home/user_homes/user3/taskers/tasker/responseSummary-1418332928505/responseSummary-1418332928552/response" + * } + */ + @Test + public void testPathsFieldStorage() + { + + SolrInputDocument doc = new SolrInputDocument(); + NodeMetaData nodeMetaData = new NodeMetaData(); + nodeMetaData.setAncestorPaths(List.of("/1/2/4/7/10", "/1/2/5/8/10", "/1/2/6/9/10")); + nodeMetaData.setPaths(List.of()); + + List expectedANames = Stream.of("0/10", + "1/7/10", "1/8/10", "1/9/10", + "2/4/7/10", "2/5/8/10", "2/6/9/10", + "3/2/4/7/10", "3/2/5/8/10", "3/2/6/9/10", + "4/1/2/4/7/10", "4/1/2/5/8/10", "4/1/2/6/9/10", + "F/1/2/4/7/10", "F/1/2/5/8/10", "F/1/2/6/9/10") + .collect(Collectors.toCollection(ArrayList::new)); + + List expectedAPaths = Stream.of("0/1", + "1/1/2", + "2/1/2/4", "2/1/2/5", "2/1/2/6", + "3/1/2/4/7", "3/1/2/5/8", "3/1/2/6/9", + "4/1/2/4/7/10", "4/1/2/5/8/10", "4/1/2/6/9/10", + "F/1/2/4/7/10", "F/1/2/5/8/10", "F/1/2/6/9/10") + .collect(Collectors.toCollection(ArrayList::new)); + + infoServer.updatePathRelatedFields(nodeMetaData, doc); + + List anames = doc.getFieldValues(QueryConstants.FIELD_ANAME).stream() + .map(aname -> aname.toString()) + .collect(Collectors.toList()) + .stream() + .sorted() + .collect(Collectors.toList()); + + assertEquals(expectedANames, anames); + + List apaths = doc.getFieldValues(QueryConstants.FIELD_APATH).stream() + .map(aname -> aname.toString()) + .collect(Collectors.toList()) + .stream() + .sorted() + .collect(Collectors.toList()); + + assertEquals(expectedAPaths, apaths); + + + } + + /** + * Repeat the indexing operation 2 times to verify that updating and existing document + * removes previous information in ANAME and APATH fields + */ + @Test + public void testPathsFieldStorageIterations() + { + + SolrInputDocument doc = new SolrInputDocument(); + NodeMetaData nodeMetaData = new NodeMetaData(); + nodeMetaData.setAncestorPaths(List.of("/1/2/4/7/10", "/1/2/5/8/10", "/1/2/6/9/10")); + nodeMetaData.setPaths(List.of()); + + IntStream.range(0, 2).forEach(i -> + { + + infoServer.updatePathRelatedFields(nodeMetaData, doc); + + assertEquals(doc.getFieldValues(QueryConstants.FIELD_ANAME).size(), 16); + assertEquals(doc.getFieldValues(QueryConstants.FIELD_APATH).size(), 14); + + }); + + } + +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrTestFiles.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrTestFiles.java index dba8f428b..0af2a1165 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrTestFiles.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrTestFiles.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/TemplatesDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/TemplatesDistributedIT.java index fd270dc89..48f74493f 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/TemplatesDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/TemplatesDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/RandomSupplier.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/RandomSupplier.java index c27333119..ab774ffff 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/RandomSupplier.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/RandomSupplier.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/SolrResponsesComparator.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/SolrResponsesComparator.java index b9d98006c..eb90c4b43 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/SolrResponsesComparator.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/SolrResponsesComparator.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/RewriteFacetParametersComponentTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/RewriteFacetParametersComponentTest.java index 0465a0126..798f01ed9 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/RewriteFacetParametersComponentTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/RewriteFacetParametersComponentTest.java @@ -1,330 +1,330 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component; - -import static java.util.Arrays.asList; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import java.util.HashMap; -import java.util.Map; - -import com.google.common.collect.ImmutableMap; -import com.sun.xml.xsom.impl.scd.Iterators; - -import org.apache.lucene.util.LuceneTestCase; -import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.handler.component.ShardRequest; -import org.apache.solr.request.SolrQueryRequest; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) -@SolrTestCaseJ4.SuppressSSL -public class RewriteFacetParametersComponentTest -{ - @InjectMocks - RewriteFacetParametersComponent rewriteFacetParametersComponent; - @Mock - SolrParams mockParams; - @Mock - SolrQueryRequest mockRequest; - - @Before - public void setUp() - { - initMocks(this); - when(mockRequest.getParams()).thenReturn(mockParams); - } - - /** - * Search-409, mange parsing of facet fields. - * RewriteFacetParametersComponent incorrectly splits fields when a comma - * is present within {}. Below example uses {max=100, percentiles=90,99} - */ - @Test - public void parseFacetField() - { - String a = "created,modified"; - String b = "cm:created,modified"; - String c = "modified"; - String d = "{crazy}created,modified,updated"; - String e = "{bob:\"fred\"}created,modified,updated"; - String f = "{perc=\"3,4,5\"}created,modified"; - String g = "{perc='3,4,5'}created,modified"; - - assertEquals(2, RewriteFacetParametersComponent.parseFacetField(a).length); - assertEquals(2, RewriteFacetParametersComponent.parseFacetField(b).length); - assertEquals(1, RewriteFacetParametersComponent.parseFacetField(c).length); - assertEquals(3, RewriteFacetParametersComponent.parseFacetField(d).length); - assertEquals(3, RewriteFacetParametersComponent.parseFacetField(e).length); - assertEquals(2, RewriteFacetParametersComponent.parseFacetField(f).length); - assertEquals(2, RewriteFacetParametersComponent.parseFacetField(g).length); - } - @Test(expected=RuntimeException.class) - public void parseEmpty() - { - RewriteFacetParametersComponent.parseFacetField(""); - } - @Test(expected=RuntimeException.class) - public void parseNull() - { - RewriteFacetParametersComponent.parseFacetField(null); - } - - /** Check that if no mincount is supplied for a field facet then it gets defaulted to 1. */ - @Test - public void rewriteMincountFacetFieldOption_mincountMissing_shouldSetGenericMinCountToOne() - { - // There are no existing facet parameters. - ModifiableSolrParams fixed = new ModifiableSolrParams(); - when(mockParams.getParameterNamesIterator()).thenReturn(Iterators.empty()); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1. - String actual = fixed.get("facet.mincount"); - assertEquals("Expected the existing mincount to be preserved.", "1", actual); - } - - /** Check that if the mincount is set as 0 then it is updated to be 1. */ - @Test - public void rewriteMincountFacetFieldOption_mincountSetZero_shouldSetMincountToOne() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); - when(mockParams.get("facet.mincount")).thenReturn("0"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualCount); - } - - @Test - public void rewriteShardedRequestParameters_mincountSetZero_shouldKeepMincountToZero() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); - when(mockParams.get("facet.mincount")).thenReturn("0"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(String.valueOf(ShardRequest.PURPOSE_GET_FACETS)); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.mincount"); - assertEquals("Expected no fixed value", null, actualCount); - } - - @Test - public void rewriteMincountFacetFieldOption_mincountSetTwo_shouldKeepIt() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); - when(mockParams.get("facet.mincount")).thenReturn("2"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.mincount"); - assertEquals("Expected the mincount to be 2.", "2", actualCount); - } - - @Test - public void rewriteMincountFacetFieldOption_perFieldMincountSetZero_shouldSetPerFieldMincountAndMincountToOne() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); - when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = ImmutableMap.of( - "NAME", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualNameCount); - String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualContentCount); - String actualCount = fixed.get("facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualCount); - } - - /** Check that if the mincount is set as 0 then it is updated to be 1. */ - @Test - public void rewriteMincountFacetFieldOption_perFieldMincountSetZero_shouldSetPerFieldMincountToOne() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); - when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = ImmutableMap.of( - "NAME", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualNameCount); - String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualContentCount); - } - - /** Check that if the user supplies a mincount of 2 then this is not changed. */ - @Test - public void rewriteMincountFacetFieldOption_perFieldMincountSetTwo_shouldKeepIt() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); - when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"2"}); - when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = ImmutableMap.of( - "NAME", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is kept as 2 and the field name is converted to the format stored by Solr. - String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); - assertEquals("Expected the mincount to be 2.", "2", actualNameCount); - String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualContentCount); - } - - - @Test - public void rewriteMincountFacetPivotOption_mincountMissing_shouldSetPivotMinCountToOne() - { - // There are no existing facet parameters. - ModifiableSolrParams fixed = new ModifiableSolrParams(); - when(mockParams.getParameterNamesIterator()).thenReturn(Iterators.empty()); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1. - String actual = fixed.get("facet.pivot.mincount"); - assertEquals("Expected the existing mincount to be preserved.", "1", actual); - } - - /** Check that if the mincount is set as 0 then it is updated to be 1. */ - @Test - public void rewriteMincountFacetPivotOption_mincountSetZero_shouldSetMincountToOne() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.pivot.mincount").iterator()); - when(mockParams.get("facet.pivot.mincount")).thenReturn("0"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.pivot.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualCount); - } - - @Test - public void rewriteMincountFacetPivotOption_mincountSetTwo_shouldKeepIt() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.pivot.mincount").iterator()); - when(mockParams.get("facet.pivot.mincount")).thenReturn("2"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.pivot.mincount"); - assertEquals("Expected the mincount to be 2.", "2", actualCount); - } - -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component; + +import static java.util.Arrays.asList; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.xml.xsom.impl.scd.Iterators; + +import org.apache.lucene.util.LuceneTestCase; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.handler.component.ShardRequest; +import org.apache.solr.request.SolrQueryRequest; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) +@SolrTestCaseJ4.SuppressSSL +public class RewriteFacetParametersComponentTest +{ + @InjectMocks + RewriteFacetParametersComponent rewriteFacetParametersComponent; + @Mock + SolrParams mockParams; + @Mock + SolrQueryRequest mockRequest; + + @Before + public void setUp() + { + initMocks(this); + when(mockRequest.getParams()).thenReturn(mockParams); + } + + /** + * Search-409, mange parsing of facet fields. + * RewriteFacetParametersComponent incorrectly splits fields when a comma + * is present within {}. Below example uses {max=100, percentiles=90,99} + */ + @Test + public void parseFacetField() + { + String a = "created,modified"; + String b = "cm:created,modified"; + String c = "modified"; + String d = "{crazy}created,modified,updated"; + String e = "{bob:\"fred\"}created,modified,updated"; + String f = "{perc=\"3,4,5\"}created,modified"; + String g = "{perc='3,4,5'}created,modified"; + + assertEquals(2, RewriteFacetParametersComponent.parseFacetField(a).length); + assertEquals(2, RewriteFacetParametersComponent.parseFacetField(b).length); + assertEquals(1, RewriteFacetParametersComponent.parseFacetField(c).length); + assertEquals(3, RewriteFacetParametersComponent.parseFacetField(d).length); + assertEquals(3, RewriteFacetParametersComponent.parseFacetField(e).length); + assertEquals(2, RewriteFacetParametersComponent.parseFacetField(f).length); + assertEquals(2, RewriteFacetParametersComponent.parseFacetField(g).length); + } + @Test(expected=RuntimeException.class) + public void parseEmpty() + { + RewriteFacetParametersComponent.parseFacetField(""); + } + @Test(expected=RuntimeException.class) + public void parseNull() + { + RewriteFacetParametersComponent.parseFacetField(null); + } + + /** Check that if no mincount is supplied for a field facet then it gets defaulted to 1. */ + @Test + public void rewriteMincountFacetFieldOption_mincountMissing_shouldSetGenericMinCountToOne() + { + // There are no existing facet parameters. + ModifiableSolrParams fixed = new ModifiableSolrParams(); + when(mockParams.getParameterNamesIterator()).thenReturn(Iterators.empty()); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1. + String actual = fixed.get("facet.mincount"); + assertEquals("Expected the existing mincount to be preserved.", "1", actual); + } + + /** Check that if the mincount is set as 0 then it is updated to be 1. */ + @Test + public void rewriteMincountFacetFieldOption_mincountSetZero_shouldSetMincountToOne() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); + when(mockParams.get("facet.mincount")).thenReturn("0"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualCount); + } + + @Test + public void rewriteShardedRequestParameters_mincountSetZero_shouldKeepMincountToZero() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); + when(mockParams.get("facet.mincount")).thenReturn("0"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(String.valueOf(ShardRequest.PURPOSE_GET_FACETS)); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.mincount"); + assertEquals("Expected no fixed value", null, actualCount); + } + + @Test + public void rewriteMincountFacetFieldOption_mincountSetTwo_shouldKeepIt() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); + when(mockParams.get("facet.mincount")).thenReturn("2"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.mincount"); + assertEquals("Expected the mincount to be 2.", "2", actualCount); + } + + @Test + public void rewriteMincountFacetFieldOption_perFieldMincountSetZero_shouldSetPerFieldMincountAndMincountToOne() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); + when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = ImmutableMap.of( + "NAME", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualNameCount); + String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualContentCount); + String actualCount = fixed.get("facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualCount); + } + + /** Check that if the mincount is set as 0 then it is updated to be 1. */ + @Test + public void rewriteMincountFacetFieldOption_perFieldMincountSetZero_shouldSetPerFieldMincountToOne() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); + when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = ImmutableMap.of( + "NAME", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualNameCount); + String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualContentCount); + } + + /** Check that if the user supplies a mincount of 2 then this is not changed. */ + @Test + public void rewriteMincountFacetFieldOption_perFieldMincountSetTwo_shouldKeepIt() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); + when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"2"}); + when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = ImmutableMap.of( + "NAME", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is kept as 2 and the field name is converted to the format stored by Solr. + String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); + assertEquals("Expected the mincount to be 2.", "2", actualNameCount); + String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualContentCount); + } + + + @Test + public void rewriteMincountFacetPivotOption_mincountMissing_shouldSetPivotMinCountToOne() + { + // There are no existing facet parameters. + ModifiableSolrParams fixed = new ModifiableSolrParams(); + when(mockParams.getParameterNamesIterator()).thenReturn(Iterators.empty()); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1. + String actual = fixed.get("facet.pivot.mincount"); + assertEquals("Expected the existing mincount to be preserved.", "1", actual); + } + + /** Check that if the mincount is set as 0 then it is updated to be 1. */ + @Test + public void rewriteMincountFacetPivotOption_mincountSetZero_shouldSetMincountToOne() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.pivot.mincount").iterator()); + when(mockParams.get("facet.pivot.mincount")).thenReturn("0"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.pivot.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualCount); + } + + @Test + public void rewriteMincountFacetPivotOption_mincountSetTwo_shouldKeepIt() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.pivot.mincount").iterator()); + when(mockParams.get("facet.pivot.mincount")).thenReturn("2"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.pivot.mincount"); + assertEquals("Expected the mincount to be 2.", "2", actualCount); + } + +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/TempFileWarningLoggerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/TempFileWarningLoggerTest.java index 6275f308f..1c0e89fef 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/TempFileWarningLoggerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/TempFileWarningLoggerTest.java @@ -1,179 +1,179 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.component; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; - -public class TempFileWarningLoggerTest -{ - private Logger log; - private Path path; - - @Before - public void setUp() - { - path = Paths.get(System.getProperty("java.io.tmpdir")); - - // Simulate warn-level logging (Although currently we only log debug messages). - log = mock(Logger.class, withSettings().lenient()); - when(log.isWarnEnabled()).thenReturn(true); - when(log.isErrorEnabled()).thenReturn(true); - } - - @Test - public void checkGlobBuiltCorrectly() - { - TempFileWarningLogger warner = - new TempFileWarningLogger( - log, - "MyPrefix*", - new String[] { "temp", "remove-me", "~notrequired" }, - path); - - assertEquals("MyPrefix*.{temp,remove-me,~notrequired}", warner.getGlob()); - } - - @Test - public void checkFindFiles() throws IOException - { - File f = File.createTempFile("MyPrefix", ".remove-me", path.toFile()); - f.deleteOnExit(); - - try - { - TempFileWarningLogger warner = - new TempFileWarningLogger( - log, - "MyPrefix*", - new String[] { "temp", "remove-me", "~notrequired" }, - path); - - boolean found = warner.checkFiles(); - - assertTrue("Should have found matching files", found); - // Should be a warn-level log message. - verify(log, never()).warn(anyString()); - } - finally - { - f.delete(); - } - } - - - @Test - public void checkWhenNoFilesToFind() throws IOException - { - File f = new File(path.toFile(), "TestFile.random"); - - // It would be very odd if this file exists! - assertFalse("Unable to perform test as file exists: " + f, f.exists()); - - TempFileWarningLogger warner = - new TempFileWarningLogger( - log, - "TestFile", - new String[] { "random" }, - path); - - boolean found = warner.checkFiles(); - - assertFalse("Should NOT have found matching file", found); - // Should be no warn-level log message. - verify(log, never()).warn(anyString()); - } - - @Test - public void removeManyFiles() throws IOException - { - File f = File.createTempFile("WFSTInputIterator", ".input", path.toFile()); - File f2 = File.createTempFile("WFSTInputIterator", ".sorted", path.toFile()); - f.deleteOnExit(); - f2.deleteOnExit(); - - TempFileWarningLogger warner = new TempFileWarningLogger(log, - "WFSTInputIterator*", - new String[] { "input", "sorted" }, - path); - boolean found = warner.checkFiles(); - assertTrue("Should have found matching file", found); - assertTrue(f.exists()); - assertTrue(f2.exists()); - if(found) - { - warner.removeFiles(); - } - assertFalse(f.exists()); - assertFalse(f2.exists()); - - boolean found2 = warner.checkFiles(); - assertFalse("Should NOT have found a matching file", found2); - - } - @Test - public void notToRemoveFilesThatDontMatch() throws IOException - { - File f = File.createTempFile("someotherfile", ".input", path.toFile()); - File f2 = File.createTempFile("someotherfile", ".sorted", path.toFile()); - f.deleteOnExit(); - f2.deleteOnExit(); - - TempFileWarningLogger warner = new TempFileWarningLogger(log, - "WFSTInputIterator*", - new String[] { "input", "sorted" }, - path); - - - assertTrue(f.exists()); - assertTrue(f2.exists()); - warner.removeFiles(); - assertTrue(f.exists()); - assertTrue(f2.exists()); - - boolean found2 = warner.checkFiles(); - assertFalse("Should NOT have found a matching file", found2); - - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.component; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; + +public class TempFileWarningLoggerTest +{ + private Logger log; + private Path path; + + @Before + public void setUp() + { + path = Paths.get(System.getProperty("java.io.tmpdir")); + + // Simulate warn-level logging (Although currently we only log debug messages). + log = mock(Logger.class, withSettings().lenient()); + when(log.isWarnEnabled()).thenReturn(true); + when(log.isErrorEnabled()).thenReturn(true); + } + + @Test + public void checkGlobBuiltCorrectly() + { + TempFileWarningLogger warner = + new TempFileWarningLogger( + log, + "MyPrefix*", + new String[] { "temp", "remove-me", "~notrequired" }, + path); + + assertEquals("MyPrefix*.{temp,remove-me,~notrequired}", warner.getGlob()); + } + + @Test + public void checkFindFiles() throws IOException + { + File f = File.createTempFile("MyPrefix", ".remove-me", path.toFile()); + f.deleteOnExit(); + + try + { + TempFileWarningLogger warner = + new TempFileWarningLogger( + log, + "MyPrefix*", + new String[] { "temp", "remove-me", "~notrequired" }, + path); + + boolean found = warner.checkFiles(); + + assertTrue("Should have found matching files", found); + // Should be a warn-level log message. + verify(log, never()).warn(anyString()); + } + finally + { + f.delete(); + } + } + + + @Test + public void checkWhenNoFilesToFind() throws IOException + { + File f = new File(path.toFile(), "TestFile.random"); + + // It would be very odd if this file exists! + assertFalse("Unable to perform test as file exists: " + f, f.exists()); + + TempFileWarningLogger warner = + new TempFileWarningLogger( + log, + "TestFile", + new String[] { "random" }, + path); + + boolean found = warner.checkFiles(); + + assertFalse("Should NOT have found matching file", found); + // Should be no warn-level log message. + verify(log, never()).warn(anyString()); + } + + @Test + public void removeManyFiles() throws IOException + { + File f = File.createTempFile("WFSTInputIterator", ".input", path.toFile()); + File f2 = File.createTempFile("WFSTInputIterator", ".sorted", path.toFile()); + f.deleteOnExit(); + f2.deleteOnExit(); + + TempFileWarningLogger warner = new TempFileWarningLogger(log, + "WFSTInputIterator*", + new String[] { "input", "sorted" }, + path); + boolean found = warner.checkFiles(); + assertTrue("Should have found matching file", found); + assertTrue(f.exists()); + assertTrue(f2.exists()); + if(found) + { + warner.removeFiles(); + } + assertFalse(f.exists()); + assertFalse(f2.exists()); + + boolean found2 = warner.checkFiles(); + assertFalse("Should NOT have found a matching file", found2); + + } + @Test + public void notToRemoveFilesThatDontMatch() throws IOException + { + File f = File.createTempFile("someotherfile", ".input", path.toFile()); + File f2 = File.createTempFile("someotherfile", ".sorted", path.toFile()); + f.deleteOnExit(); + f2.deleteOnExit(); + + TempFileWarningLogger warner = new TempFileWarningLogger(log, + "WFSTInputIterator*", + new String[] { "input", "sorted" }, + path); + + + assertTrue(f.exists()); + assertTrue(f2.exists()); + warner.removeFiles(); + assertTrue(f.exists()); + assertTrue(f2.exists()); + + boolean found2 = warner.checkFiles(); + assertFalse("Should NOT have found a matching file", found2); + + } + +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/config/ConfigUtilTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/config/ConfigUtilTest.java index 3d55b634d..a47e3e24a 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/config/ConfigUtilTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/config/ConfigUtilTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -75,4 +75,4 @@ public class ConfigUtilTest { assertEquals("SOLR_SOLR_BASEURL",ConfigUtil.convertPropertyNameToEnvironmentParam(SolrInformationServer.SOLR_BASEURL)); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/dataload/TestDataProvider.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/dataload/TestDataProvider.java index a99533957..26a3d6de7 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/dataload/TestDataProvider.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/dataload/TestDataProvider.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -906,4 +906,4 @@ public class TestDataProvider implements AlfrescoSolrConstants { return new StringPropertyValue(value); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighligherDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighligherDistributedIT.java index 28d4b0c1f..aa43e1711 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighligherDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighligherDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighlighterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighlighterIT.java index a7773508c..3a7801408 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighlighterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighlighterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -425,4 +425,4 @@ public class AlfrescoHighlighterIT extends AbstractAlfrescoSolrIT "*[count(//lst[@name='highlighting']/lst/arr[@name='name'])=1]", "//lst[@name='highlighting']/lst[1]/arr[@name='name']/str[.='some {very} long {name}']"); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java index bc46468dc..05882adf3 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -211,4 +211,4 @@ public class SolrCoreLoadListenerTest SolrConfig solrConfig = new SolrConfig(configName, new InputSource(getClass().getResourceAsStream("/test-files/" + configName))); when(core.getSolrConfig()).thenReturn(solrConfig); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoLuceneQParserTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoLuceneQParserTest.java index 5ecc9e98d..abc682462 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoLuceneQParserTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoLuceneQParserTest.java @@ -1,62 +1,62 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import static org.junit.Assert.assertEquals; - -import org.alfresco.solr.query.AlfrescoLuceneQParserPlugin.AlfrescoLuceneQParser; -import org.junit.Before; -import org.junit.Test; - - -public class AlfrescoLuceneQParserTest -{ - private AlfrescoLuceneQParser parser; - - @Before - public void setUp() throws Exception - { - parser = new AlfrescoLuceneQParser(null, null, null, null, null); - } - - @Test - public void forwardSlashesEscapedCorrectly() - { - assertEquals("Nothing to escape", "abcdef", parser.escape("abcdef")); - assertEquals("Escape single slash", "abc\\/def", parser.escape("abc/def")); - assertEquals("Do not escape quoted slash", "abc\"/\"def", parser.escape("abc\"/\"def")); - assertEquals("Do not escape escaped slash", "abc\\/def", parser.escape("abc\\/def")); - assertEquals("Do not escape quoted escaped slash", "abc\"\\/\"def", parser.escape("abc\"\\/\"def")); - assertEquals("Escape multiple consecutive slashes", "abc\\/\\/def", parser.escape("abc//def")); - assertEquals("abc\\/\\/\\/\\/\\/def\\/\\/\\/:\"bl/ah\"", parser.escape("abc/////def/\\//:\"bl/ah\"")); - - // Test case from ACE-3071 - final String input = "(@\\{http\\://www.alfresco.org/model/imap/1.0\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\" OR @\\{http\\://www.westernacher.com/alfresco/models/wpsmail\\-v2\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\")"; - final String expected = "(@\\{http\\:\\/\\/www.alfresco.org\\/model\\/imap\\/1.0\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\" OR @\\{http\\:\\/\\/www.westernacher.com\\/alfresco\\/models\\/wpsmail\\-v2\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\")"; - assertEquals(expected, parser.escape(input)); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import static org.junit.Assert.assertEquals; + +import org.alfresco.solr.query.AlfrescoLuceneQParserPlugin.AlfrescoLuceneQParser; +import org.junit.Before; +import org.junit.Test; + + +public class AlfrescoLuceneQParserTest +{ + private AlfrescoLuceneQParser parser; + + @Before + public void setUp() throws Exception + { + parser = new AlfrescoLuceneQParser(null, null, null, null, null); + } + + @Test + public void forwardSlashesEscapedCorrectly() + { + assertEquals("Nothing to escape", "abcdef", parser.escape("abcdef")); + assertEquals("Escape single slash", "abc\\/def", parser.escape("abc/def")); + assertEquals("Do not escape quoted slash", "abc\"/\"def", parser.escape("abc\"/\"def")); + assertEquals("Do not escape escaped slash", "abc\\/def", parser.escape("abc\\/def")); + assertEquals("Do not escape quoted escaped slash", "abc\"\\/\"def", parser.escape("abc\"\\/\"def")); + assertEquals("Escape multiple consecutive slashes", "abc\\/\\/def", parser.escape("abc//def")); + assertEquals("abc\\/\\/\\/\\/\\/def\\/\\/\\/:\"bl/ah\"", parser.escape("abc/////def/\\//:\"bl/ah\"")); + + // Test case from ACE-3071 + final String input = "(@\\{http\\://www.alfresco.org/model/imap/1.0\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\" OR @\\{http\\://www.westernacher.com/alfresco/models/wpsmail\\-v2\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\")"; + final String expected = "(@\\{http\\:\\/\\/www.alfresco.org\\/model\\/imap\\/1.0\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\" OR @\\{http\\:\\/\\/www.westernacher.com\\/alfresco\\/models\\/wpsmail\\-v2\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\")"; + assertEquals(expected, parser.escape(input)); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoReRankQParserPluginIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoReRankQParserPluginIT.java index 5ffc5fc01..d80341527 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoReRankQParserPluginIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoReRankQParserPluginIT.java @@ -1,705 +1,705 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.query; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import org.alfresco.solr.AbstractAlfrescoSolrIT; -import org.apache.lucene.util.LuceneTestCase; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.ResultContext; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocList; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - - -@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) -public class AlfrescoReRankQParserPluginIT extends AbstractAlfrescoSolrIT -{ - @BeforeClass - public static void beforeClass() throws Exception - { - initAlfrescoCore("schema-rerank.xml"); - } - @Before - public void setUp() throws Exception - { - - // if you override setUp or tearDown, you better call - // the super classes version - clearIndex(); - assertU(commit()); - } - - @Test - public void testReRankQueries() throws Exception { - - assertU(delQ("*:*")); - assertU(commit()); - - String[] doc = {"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc)); - assertU(commit()); - String[] doc1 = {"id","2", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc1)); - - String[] doc2 = {"id","3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc2)); - assertU(commit()); - String[] doc3 = {"id","4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; - assertU(adoc(doc3)); - - String[] doc4 = {"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc4)); - assertU(commit()); - String[] doc5 = {"id","6", "term_s","YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc5)); - assertU(commit()); - - - Random random = new Random(); - boolean scale = random.nextBoolean(); - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=200 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "term_s:YYYY"); - params.add("rqq", "{!edismax bf=$bff}*:*"); - params.add("bff", "field(test_ti)"); - params.add("start", "0"); - params.add("rows", "6"); - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='3.0']", - "//result/doc[2]/float[@name='id'][.='4.0']", - "//result/doc[3]/float[@name='id'][.='2.0']", - "//result/doc[4]/float[@name='id'][.='6.0']", - "//result/doc[5]/float[@name='id'][.='1.0']", - "//result/doc[6]/float[@name='id'][.='5.0']" - ); - - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "{!edismax bq=$bqq2}*:*"); - params.add("bqq2", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='5.0']", - "//result/doc[4]/float[@name='id'][.='4.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - //Test with sort by score. - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "{!edismax bq=$bqq2}*:*"); - params.add("bqq2", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("sort", "score desc"); - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='5.0']", - "//result/doc[4]/float[@name='id'][.='4.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - - //Test with compound sort. - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "{!edismax bq=$bqq2}*:*"); - params.add("bqq2", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("sort", "score desc,test_ti asc"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='5.0']", - "//result/doc[4]/float[@name='id'][.='4.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - - //Test with elevation - /* - - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=50 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "{!edismax bq=$bqq2}*:*"); - params.add("bqq2", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("qt", "/elevate"); - params.add("elevateIds", "1"); - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='1.0']", - "//result/doc[2]/float[@name='id'][.='2.0']", - "//result/doc[3]/float[@name='id'][.='6.0']", - "//result/doc[4]/float[@name='id'][.='5.0']", - "//result/doc[5]/float[@name='id'][.='4.0']", - "//result/doc[6]/float[@name='id'][.='3.0']" - - ); - */ - - - //Test TermQuery rqq - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='5.0']", - "//result/doc[4]/float[@name='id'][.='4.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - - //Test Elevation - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "1,4"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='1.0']", //Elevated - "//result/doc[2]/float[@name='id'][.='4.0']", //Elevated - "//result/doc[3]/float[@name='id'][.='2.0']", //Boosted during rerank. - "//result/doc[4]/float[@name='id'][.='6.0']", - "//result/doc[5]/float[@name='id'][.='5.0']", - "//result/doc[6]/float[@name='id'][.='3.0']" - ); - */ - - //Test Elevation swapped - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "4,1"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='4.0']", //Elevated - "//result/doc[2]/float[@name='id'][.='1.0']", //Elevated - "//result/doc[3]/float[@name='id'][.='2.0']", //Boosted during rerank. - "//result/doc[4]/float[@name='id'][.='6.0']", - "//result/doc[5]/float[@name='id'][.='5.0']", - "//result/doc[6]/float[@name='id'][.='3.0']" - ); - */ - - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=4 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "4,1"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='4.0']", //Elevated - "//result/doc[2]/float[@name='id'][.='1.0']", //Elevated - "//result/doc[3]/float[@name='id'][.='6.0']", - "//result/doc[4]/float[@name='id'][.='5.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='2.0']" //Not in reRankeDocs - ); - - */ - - - //Test Elevation with start beyond the rerank docs - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "4"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "4,1"); - - assertQ(req(params), "*[count(//doc)=2]", - "//result/doc[1]/float[@name='id'][.='3.0']", - "//result/doc[2]/float[@name='id'][.='2.0']" //Was not in reRankDocs - ); - */ - //Test Elevation with zero results - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}nada"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "4"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "4,1"); - - assertQ(req(params), "*[count(//doc)=0]"); - */ - - - //Pass in reRankDocs lower then the length being collected. - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=1 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=0 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=2 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:4^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='5.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - //Test reRankWeight of 0, reranking will have no effect. - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=0 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "5"); - - assertQ(req(params), "*[count(//doc)=5]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']" - ); - - - - //Test range query - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "test_ti:[0 TO 2000]"); - params.add("rqq", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "6"); - - assertQ(req(params), "*[count(//doc)=5]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='2.0']", - "//result/doc[5]/float[@name='id'][.='1.0']" - ); - - //Test range query embedded in larger query - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "*:* OR test_ti:[0 TO 2000]"); - params.add("rqq", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "6"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - - //Test with start beyond reRankDocs - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "id:1^1000"); - params.add("fl", "id,score"); - params.add("start", "4"); - params.add("rows", "5"); - - assertQ(req(params), "*[count(//doc)=2]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='1.0']" - ); - - //Test ReRankDocs > docs returned - - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50"); - params.add("rqq", "id:1^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "1"); - - assertQ(req(params), "*[count(//doc)=1]", - "//result/doc[1]/float[@name='id'][.='1.0']" - ); - - - - //Test with zero results - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "term_s:NNNN"); - params.add("rqq", "id:1^1000"); - params.add("fl", "id,score"); - params.add("start", "4"); - params.add("rows", "5"); - - assertQ(req(params), "*[count(//doc)=0]"); - - } - - @Test - public void testOverRank() throws Exception { - - assertU(delQ("*:*")); - assertU(commit()); - - //Test the scenario that where we rank more documents then we return. - - boolean scale = new Random().nextBoolean(); - - String[] doc = {"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc)); - - String[] doc1 = {"id","2", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc1)); - - String[] doc2 = {"id","3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc2)); - String[] doc3 = {"id","4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; - assertU(adoc(doc3)); - - - String[] doc4 = {"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc4)); - - String[] doc5 = {"id","6", "term_s","YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc5)); - - String[] doc6 = {"id","7", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc6)); - - - String[] doc7 = {"id","8", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc7)); - - String[] doc8 = {"id","9", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc8)); - String[] doc9 = {"id","10", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; - assertU(adoc(doc9)); - - String[] doc10 = {"id","11", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc10)); - assertU(commit()); - - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=11 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60 id:7^70 id:8^80 id:9^90 id:10^100 id:11^110"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "2"); - - assertQ(req(params), "*[count(//doc)=2]", - "//result/doc[1]/float[@name='id'][.='8.0']", - "//result/doc[2]/float[@name='id'][.='2.0']" - ); - - //Test Elevation - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=11 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60 id:7^70 id:8^80 id:9^90 id:10^100 id:11^110"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "3"); - params.add("qt","/elevate"); - params.add("elevateIds", "1,4"); - - assertQ(req(params), "*[count(//doc)=3]", - "//result/doc[1]/float[@name='id'][.='1.0']", //Elevated - "//result/doc[2]/float[@name='id'][.='4.0']", //Elevated - "//result/doc[3]/float[@name='id'][.='8.0']"); //Boosted during rerank. - */ - - } - - @Test - public void testScale() throws Exception { - - assertU(delQ("*:*")); - assertU(commit()); - - String[] doc = {"id", "1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc)); - assertU(commit()); - String[] doc1 = {"id", "2", "term_s", "YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc1)); - - String[] doc2 = {"id", "3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc2)); - assertU(commit()); - String[] doc3 = {"id", "4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; - assertU(adoc(doc3)); - - String[] doc4 = {"id", "5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc4)); - assertU(commit()); - String[] doc5 = {"id", "6", "term_s", "YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc5)); - assertU(commit()); - - - //Calculate the scales manually - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=200 scale=false}"); - params.add("df", "TEXT"); - params.add("q", "term_s:YYYY"); - params.add("rqq", "{!edismax bf=$bff}id:(1 2 4 5 6)"); - params.add("bff", "field(test_ti)"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "6"); - - SolrQueryRequest req = req(params); - SolrQueryResponse res = null; - - try { - res = queryAndResponse(null, req); - } finally { - req.close(); - } - - @SuppressWarnings("rawtypes") - NamedList vals = res.getValues(); - ResultContext resultContext = (ResultContext)vals.get("response"); - - DocList docs = resultContext.getDocList(); - DocIterator it = docs.iterator(); - float max = -Float.MAX_VALUE; - List scores = new ArrayList(); - - while(it.hasNext()) { - it.next(); - float score = it.score(); - max = Math.max(score, max); - scores.add(score); - } - - - float[] scaledScores = new float[scores.size()]; - - for(int i=0; i. + * #L% + */ + +package org.alfresco.solr.query; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.alfresco.solr.AbstractAlfrescoSolrIT; +import org.apache.lucene.util.LuceneTestCase; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.ResultContext; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + + +@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) +public class AlfrescoReRankQParserPluginIT extends AbstractAlfrescoSolrIT +{ + @BeforeClass + public static void beforeClass() throws Exception + { + initAlfrescoCore("schema-rerank.xml"); + } + @Before + public void setUp() throws Exception + { + + // if you override setUp or tearDown, you better call + // the super classes version + clearIndex(); + assertU(commit()); + } + + @Test + public void testReRankQueries() throws Exception { + + assertU(delQ("*:*")); + assertU(commit()); + + String[] doc = {"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc)); + assertU(commit()); + String[] doc1 = {"id","2", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc1)); + + String[] doc2 = {"id","3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc2)); + assertU(commit()); + String[] doc3 = {"id","4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; + assertU(adoc(doc3)); + + String[] doc4 = {"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc4)); + assertU(commit()); + String[] doc5 = {"id","6", "term_s","YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc5)); + assertU(commit()); + + + Random random = new Random(); + boolean scale = random.nextBoolean(); + + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=200 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "term_s:YYYY"); + params.add("rqq", "{!edismax bf=$bff}*:*"); + params.add("bff", "field(test_ti)"); + params.add("start", "0"); + params.add("rows", "6"); + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='3.0']", + "//result/doc[2]/float[@name='id'][.='4.0']", + "//result/doc[3]/float[@name='id'][.='2.0']", + "//result/doc[4]/float[@name='id'][.='6.0']", + "//result/doc[5]/float[@name='id'][.='1.0']", + "//result/doc[6]/float[@name='id'][.='5.0']" + ); + + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "{!edismax bq=$bqq2}*:*"); + params.add("bqq2", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='5.0']", + "//result/doc[4]/float[@name='id'][.='4.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + //Test with sort by score. + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "{!edismax bq=$bqq2}*:*"); + params.add("bqq2", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("sort", "score desc"); + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='5.0']", + "//result/doc[4]/float[@name='id'][.='4.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + + //Test with compound sort. + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "{!edismax bq=$bqq2}*:*"); + params.add("bqq2", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("sort", "score desc,test_ti asc"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='5.0']", + "//result/doc[4]/float[@name='id'][.='4.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + + //Test with elevation + /* + + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=50 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "{!edismax bq=$bqq2}*:*"); + params.add("bqq2", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("qt", "/elevate"); + params.add("elevateIds", "1"); + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='1.0']", + "//result/doc[2]/float[@name='id'][.='2.0']", + "//result/doc[3]/float[@name='id'][.='6.0']", + "//result/doc[4]/float[@name='id'][.='5.0']", + "//result/doc[5]/float[@name='id'][.='4.0']", + "//result/doc[6]/float[@name='id'][.='3.0']" + + ); + */ + + + //Test TermQuery rqq + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='5.0']", + "//result/doc[4]/float[@name='id'][.='4.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + + //Test Elevation + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "1,4"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='1.0']", //Elevated + "//result/doc[2]/float[@name='id'][.='4.0']", //Elevated + "//result/doc[3]/float[@name='id'][.='2.0']", //Boosted during rerank. + "//result/doc[4]/float[@name='id'][.='6.0']", + "//result/doc[5]/float[@name='id'][.='5.0']", + "//result/doc[6]/float[@name='id'][.='3.0']" + ); + */ + + //Test Elevation swapped + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "4,1"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='4.0']", //Elevated + "//result/doc[2]/float[@name='id'][.='1.0']", //Elevated + "//result/doc[3]/float[@name='id'][.='2.0']", //Boosted during rerank. + "//result/doc[4]/float[@name='id'][.='6.0']", + "//result/doc[5]/float[@name='id'][.='5.0']", + "//result/doc[6]/float[@name='id'][.='3.0']" + ); + */ + + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=4 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "4,1"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='4.0']", //Elevated + "//result/doc[2]/float[@name='id'][.='1.0']", //Elevated + "//result/doc[3]/float[@name='id'][.='6.0']", + "//result/doc[4]/float[@name='id'][.='5.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='2.0']" //Not in reRankeDocs + ); + + */ + + + //Test Elevation with start beyond the rerank docs + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "4"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "4,1"); + + assertQ(req(params), "*[count(//doc)=2]", + "//result/doc[1]/float[@name='id'][.='3.0']", + "//result/doc[2]/float[@name='id'][.='2.0']" //Was not in reRankDocs + ); + */ + //Test Elevation with zero results + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}nada"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "4"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "4,1"); + + assertQ(req(params), "*[count(//doc)=0]"); + */ + + + //Pass in reRankDocs lower then the length being collected. + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=1 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=0 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=2 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:4^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='5.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + //Test reRankWeight of 0, reranking will have no effect. + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=0 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "5"); + + assertQ(req(params), "*[count(//doc)=5]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']" + ); + + + + //Test range query + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "test_ti:[0 TO 2000]"); + params.add("rqq", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "6"); + + assertQ(req(params), "*[count(//doc)=5]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='2.0']", + "//result/doc[5]/float[@name='id'][.='1.0']" + ); + + //Test range query embedded in larger query + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "*:* OR test_ti:[0 TO 2000]"); + params.add("rqq", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "6"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + + //Test with start beyond reRankDocs + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "id:1^1000"); + params.add("fl", "id,score"); + params.add("start", "4"); + params.add("rows", "5"); + + assertQ(req(params), "*[count(//doc)=2]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='1.0']" + ); + + //Test ReRankDocs > docs returned + + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50"); + params.add("rqq", "id:1^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "1"); + + assertQ(req(params), "*[count(//doc)=1]", + "//result/doc[1]/float[@name='id'][.='1.0']" + ); + + + + //Test with zero results + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "term_s:NNNN"); + params.add("rqq", "id:1^1000"); + params.add("fl", "id,score"); + params.add("start", "4"); + params.add("rows", "5"); + + assertQ(req(params), "*[count(//doc)=0]"); + + } + + @Test + public void testOverRank() throws Exception { + + assertU(delQ("*:*")); + assertU(commit()); + + //Test the scenario that where we rank more documents then we return. + + boolean scale = new Random().nextBoolean(); + + String[] doc = {"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc)); + + String[] doc1 = {"id","2", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc1)); + + String[] doc2 = {"id","3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc2)); + String[] doc3 = {"id","4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; + assertU(adoc(doc3)); + + + String[] doc4 = {"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc4)); + + String[] doc5 = {"id","6", "term_s","YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc5)); + + String[] doc6 = {"id","7", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc6)); + + + String[] doc7 = {"id","8", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc7)); + + String[] doc8 = {"id","9", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc8)); + String[] doc9 = {"id","10", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; + assertU(adoc(doc9)); + + String[] doc10 = {"id","11", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc10)); + assertU(commit()); + + + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=11 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60 id:7^70 id:8^80 id:9^90 id:10^100 id:11^110"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "2"); + + assertQ(req(params), "*[count(//doc)=2]", + "//result/doc[1]/float[@name='id'][.='8.0']", + "//result/doc[2]/float[@name='id'][.='2.0']" + ); + + //Test Elevation + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=11 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60 id:7^70 id:8^80 id:9^90 id:10^100 id:11^110"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "3"); + params.add("qt","/elevate"); + params.add("elevateIds", "1,4"); + + assertQ(req(params), "*[count(//doc)=3]", + "//result/doc[1]/float[@name='id'][.='1.0']", //Elevated + "//result/doc[2]/float[@name='id'][.='4.0']", //Elevated + "//result/doc[3]/float[@name='id'][.='8.0']"); //Boosted during rerank. + */ + + } + + @Test + public void testScale() throws Exception { + + assertU(delQ("*:*")); + assertU(commit()); + + String[] doc = {"id", "1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc)); + assertU(commit()); + String[] doc1 = {"id", "2", "term_s", "YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc1)); + + String[] doc2 = {"id", "3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc2)); + assertU(commit()); + String[] doc3 = {"id", "4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; + assertU(adoc(doc3)); + + String[] doc4 = {"id", "5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc4)); + assertU(commit()); + String[] doc5 = {"id", "6", "term_s", "YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc5)); + assertU(commit()); + + + //Calculate the scales manually + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=200 scale=false}"); + params.add("df", "TEXT"); + params.add("q", "term_s:YYYY"); + params.add("rqq", "{!edismax bf=$bff}id:(1 2 4 5 6)"); + params.add("bff", "field(test_ti)"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "6"); + + SolrQueryRequest req = req(params); + SolrQueryResponse res = null; + + try { + res = queryAndResponse(null, req); + } finally { + req.close(); + } + + @SuppressWarnings("rawtypes") + NamedList vals = res.getValues(); + ResultContext resultContext = (ResultContext)vals.get("response"); + + DocList docs = resultContext.getDocList(); + DocIterator it = docs.iterator(); + float max = -Float.MAX_VALUE; + List scores = new ArrayList(); + + while(it.hasNext()) { + it.next(); + float score = it.score(); + max = Math.max(score, max); + scores.add(score); + } + + + float[] scaledScores = new float[scores.size()]; + + for(int i=0; i. - * #L% - */ - -package org.alfresco.solr.query; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.AbstractAlfrescoSolrIT; -import org.apache.lucene.util.LuceneTestCase; -import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) -@SolrTestCaseJ4.SuppressSSL - -public class SolrAuthIT extends AbstractAlfrescoSolrIT -{ - - @BeforeClass - public static void beforeClass() throws Exception - { - initAlfrescoCore("schema.xml"); - } - - @Before - public void setUp() throws Exception - { - // if you override setUp or tearDown, you better call - // the super classes version - //clearIndex(); - //assertU(commit()); - } - - @Before - public void loadAuth() throws Exception - { - assertU(delQ("*:*")); - assertU(commit()); - - String[] acldoc = {"id", "100", QueryConstants.FIELD_VERSION,"0","READER", "GROUP_R1", "READER", "GROUP_R2", "ACLID", "5000"}; - assertU(adoc(acldoc)); - assertU(commit()); - - String[] acldoc1 = {"id", "101", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R4", "ACLID", "6000", "DENIED", "GROUP_D1"}; - assertU(adoc(acldoc1)); - assertU(commit()); - - String[] acldoc2 = {"id", "102", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R2", "ACLID", "7000"}; - assertU(adoc(acldoc2)); - assertU(commit()); - - String[] acldoc3 = {"id", "103", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R5", "ACLID", "8000"}; - assertU(adoc(acldoc3)); - assertU(commit()); - - String[] acldoc4 = {"id", "104", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R5", "READER", "GROUP_R1", "ACLID", "9000"}; - assertU(adoc(acldoc4)); - assertU(commit()); - - - //Index Main Documents - String[] doc = {"id", "1", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "5000", "OWNER", "jim"}; - assertU(adoc(doc)); - assertU(commit()); - String[] doc1 = {"id", "2", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "6000", "OWNER", "dave"}; - assertU(adoc(doc1)); - - String[] doc2 = {"id", "3", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "7000", "OWNER", "mary"}; - assertU(adoc(doc2)); - assertU(commit()); - String[] doc3 = {"id", "4", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "8000", "OWNER", "bill"}; - assertU(adoc(doc3)); - - String[] doc4 = {"id", "5", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "9000", "OWNER", "steve"}; - assertU(adoc(doc4)); - assertU(commit()); - String[] doc5 = {"id", "6", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "10000", "OWNER", "sara"}; - assertU(adoc(doc5)); - assertU(commit()); - } - @Test - public void testAuthInFilter() - { - testAuth("false"); - } - @Test - public void testAuthPostFilter() - { - testAuth("true"); - } - - public void testAuth(String postFilter){ - System.setProperty("alfresco.postfilter", postFilter); - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq", "{!afts}AUTHORITY_FILTER_FROM_JSON"); - SolrServletRequest req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\" ], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=3]", - "//result/doc[1]/str[@name='id'][.='1']", - "//result/doc[2]/str[@name='id'][.='2']", - "//result/doc[3]/str[@name='id'][.='3']"); - - - //Test with owner - params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); - req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\", \"steve\" ], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=4]", - "//result/doc[1]/str[@name='id'][.='1']", - "//result/doc[2]/str[@name='id'][.='2']", - "//result/doc[3]/str[@name='id'][.='3']", - "//result/doc[4]/str[@name='id'][.='5']"); - // Test Deny - - params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); - req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\", \"GROUP_D1\"], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=2]", - "//result/doc[1]/str[@name='id'][.='1']", - "//result/doc[2]/str[@name='id'][.='3']"); - - //Test global read authority - params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); - req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"ROLE_ADMINISTRATOR\"], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=6]"); - - //Test zero hits - params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); - req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"blah\"], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=0]"); - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.query; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.AbstractAlfrescoSolrIT; +import org.apache.lucene.util.LuceneTestCase; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) +@SolrTestCaseJ4.SuppressSSL + +public class SolrAuthIT extends AbstractAlfrescoSolrIT +{ + + @BeforeClass + public static void beforeClass() throws Exception + { + initAlfrescoCore("schema.xml"); + } + + @Before + public void setUp() throws Exception + { + // if you override setUp or tearDown, you better call + // the super classes version + //clearIndex(); + //assertU(commit()); + } + + @Before + public void loadAuth() throws Exception + { + assertU(delQ("*:*")); + assertU(commit()); + + String[] acldoc = {"id", "100", QueryConstants.FIELD_VERSION,"0","READER", "GROUP_R1", "READER", "GROUP_R2", "ACLID", "5000"}; + assertU(adoc(acldoc)); + assertU(commit()); + + String[] acldoc1 = {"id", "101", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R4", "ACLID", "6000", "DENIED", "GROUP_D1"}; + assertU(adoc(acldoc1)); + assertU(commit()); + + String[] acldoc2 = {"id", "102", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R2", "ACLID", "7000"}; + assertU(adoc(acldoc2)); + assertU(commit()); + + String[] acldoc3 = {"id", "103", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R5", "ACLID", "8000"}; + assertU(adoc(acldoc3)); + assertU(commit()); + + String[] acldoc4 = {"id", "104", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R5", "READER", "GROUP_R1", "ACLID", "9000"}; + assertU(adoc(acldoc4)); + assertU(commit()); + + + //Index Main Documents + String[] doc = {"id", "1", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "5000", "OWNER", "jim"}; + assertU(adoc(doc)); + assertU(commit()); + String[] doc1 = {"id", "2", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "6000", "OWNER", "dave"}; + assertU(adoc(doc1)); + + String[] doc2 = {"id", "3", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "7000", "OWNER", "mary"}; + assertU(adoc(doc2)); + assertU(commit()); + String[] doc3 = {"id", "4", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "8000", "OWNER", "bill"}; + assertU(adoc(doc3)); + + String[] doc4 = {"id", "5", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "9000", "OWNER", "steve"}; + assertU(adoc(doc4)); + assertU(commit()); + String[] doc5 = {"id", "6", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "10000", "OWNER", "sara"}; + assertU(adoc(doc5)); + assertU(commit()); + } + @Test + public void testAuthInFilter() + { + testAuth("false"); + } + @Test + public void testAuthPostFilter() + { + testAuth("true"); + } + + public void testAuth(String postFilter){ + System.setProperty("alfresco.postfilter", postFilter); + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq", "{!afts}AUTHORITY_FILTER_FROM_JSON"); + SolrServletRequest req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\" ], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=3]", + "//result/doc[1]/str[@name='id'][.='1']", + "//result/doc[2]/str[@name='id'][.='2']", + "//result/doc[3]/str[@name='id'][.='3']"); + + + //Test with owner + params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); + req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\", \"steve\" ], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=4]", + "//result/doc[1]/str[@name='id'][.='1']", + "//result/doc[2]/str[@name='id'][.='2']", + "//result/doc[3]/str[@name='id'][.='3']", + "//result/doc[4]/str[@name='id'][.='5']"); + // Test Deny + + params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); + req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\", \"GROUP_D1\"], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=2]", + "//result/doc[1]/str[@name='id'][.='1']", + "//result/doc[2]/str[@name='id'][.='3']"); + + //Test global read authority + params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); + req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"ROLE_ADMINISTRATOR\"], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=6]"); + + //Test zero hits + params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); + req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"blah\"], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=0]"); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/UntokenisedFieldIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/UntokenisedFieldIT.java index 34630d704..f6bd5b518 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/UntokenisedFieldIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/UntokenisedFieldIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/AbstractQParserPluginIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/AbstractQParserPluginIT.java index 7865b4060..8582a92d1 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/AbstractQParserPluginIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/AbstractQParserPluginIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/FieldNameEscapingIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/FieldNameEscapingIT.java index fe2bfb9d8..29c2525a8 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/FieldNameEscapingIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/FieldNameEscapingIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -54,4 +54,4 @@ public class FieldNameEscapingIT extends AbstractQParserPluginIT implements Quer { assertAQuery("PATH:\"/cm:" + ISO9075.encode(DATASETS_PROVIDER.getNumericLocalName()) + "\"", 1); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/QParserPluginIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/QParserPluginIT.java index ff7172625..c36d3a715 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/QParserPluginIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/QParserPluginIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -711,4 +711,4 @@ public class QParserPluginIT extends AbstractQParserPluginIT implements QueryCon assertAQuery("@" + escape(QName.createQName(TEST_NAMESPACE, "period-ista")) + ":\"period|12\"", 1); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDefaultTextQueryIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDefaultTextQueryIT.java index 8bdd3ca17..7a55fd177 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDefaultTextQueryIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDefaultTextQueryIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDisjunctionIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDisjunctionIT.java index aa35829e4..df71a6809 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDisjunctionIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDisjunctionIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -85,4 +85,4 @@ public class AFTSDisjunctionIT extends AbstractRequestHandlerIT assertResponseCardinality("cm:name:test* AND NOT (id:1)", 3); assertResponseCardinality("cm:name:test* AND (NOT id:1)", 3); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSIdentifierFieldsIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSIdentifierFieldsIT.java index b2c776e9b..2a243da96 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSIdentifierFieldsIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSIdentifierFieldsIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -95,4 +95,4 @@ public class AFTSIdentifierFieldsIT extends AbstractRequestHandlerIT assertResponseCardinality("creator:'daniel*'", 1); assertResponseCardinality("creator:'daniel.armstrong'", 1); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRangeQueryIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRangeQueryIT.java index d17a66e20..80f147134 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRangeQueryIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRangeQueryIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -106,4 +106,4 @@ public class AFTSRangeQueryIT extends AbstractRequestHandlerIT assertResponseCardinality("cm:creator:[Luigi2 TO Mario]", 1); assertResponseCardinality("cm:creator:[* TO Mario]", 2); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRequestHandlerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRequestHandlerIT.java index ce6557fe8..28ab73425 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRequestHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRequestHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -917,4 +917,4 @@ public class AFTSRequestHandlerIT extends AbstractRequestHandlerIT implements Qu assertResponseCardinality("ANAME:0/" + DATASETS_PROVIDER.getNode03().getId(), 0); assertResponseCardinality("ANAME:1/" + TEST_ROOT_NODEREF.getId()+"/" + DATASETS_PROVIDER.getNode01().getId(), 2); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AbstractRequestHandlerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AbstractRequestHandlerIT.java index ac44c1b71..f47195f06 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AbstractRequestHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AbstractRequestHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/MNTIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/MNTIT.java index 3dbb2a977..3730e2060 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/MNTIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/MNTIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -92,4 +92,4 @@ public class MNTIT extends AbstractRequestHandlerIT assertResponseCardinality("\"AnalystName\" AND !\"AnalystName Craig\"", 1); assertResponseCardinality("cm:name:\"BASF*.txt\"", 4); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/AlfrescoCMISQParserPluginIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/AlfrescoCMISQParserPluginIT.java index 1d8a4e6e4..f1527619e 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/AlfrescoCMISQParserPluginIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/AlfrescoCMISQParserPluginIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -795,4 +795,4 @@ public class AlfrescoCMISQParserPluginIT extends LoadCMISData implements QueryCo assertQ(qurySolr("SELECT * FROM cmis:document where contains('\\'two\\' \\'zebra\\'')"), expectedDocCount(1)); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/CmisIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/CmisIT.java index 0a6c2dbb9..f8ec1c897 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/CmisIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/CmisIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/LoadCMISData.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/LoadCMISData.java index 78da12105..af4bf1242 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/LoadCMISData.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/LoadCMISData.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/SortCMISTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/SortCMISTest.java index e644b1658..f706d379e 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/SortCMISTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/SortCMISTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/TrieFieldsCMISTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/TrieFieldsCMISTest.java index 37b040cd7..564f75afb 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/TrieFieldsCMISTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/TrieFieldsCMISTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/CompositeReaderTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/CompositeReaderTest.java index eaffa0a52..bf5c46698 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/CompositeReaderTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/CompositeReaderTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStreamTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStreamTest.java index 87968bb54..4c5e7a381 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStreamTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStreamTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/servlet/Solr4X509ServletFilterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/servlet/Solr4X509ServletFilterTest.java index dd2ac4d5c..764322826 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/servlet/Solr4X509ServletFilterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/servlet/Solr4X509ServletFilterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ACLIDMurmurRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ACLIDMurmurRouterTest.java index c3ef0d2fb..928802913 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ACLIDMurmurRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ACLIDMurmurRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -136,4 +136,4 @@ public class ACLIDMurmurRouterTest assertTrue(aclDistributionMap.values().toString() + ", SD = " + aclsDeviation, aclsDeviation/(howManyDocumentsPerShard) * 100 < 30); assertTrue(nodeDistributionMap.values().toString() + ", SD = " + nodesDeviation,nodesDeviation/(howManyDocumentsPerShard) * 100 < 30); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclModCountRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclModCountRouterIT.java index 07b0612f1..4e793bfe6 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclModCountRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclModCountRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -130,4 +130,4 @@ public class AclModCountRouterIT assertEquals(shardIdentifiers.length, nodeDistributionMap.size()); nodeDistributionMap.forEach((k, v) -> assertEquals(howManyDocumentsPerShard, v.intValue())); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ActivatableTrackerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ActivatableTrackerTest.java index 33bddf27e..38f6e30f0 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ActivatableTrackerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ActivatableTrackerTest.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 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% diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerExceptionIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerExceptionIT.java index 6e5ed57d8..4794295e9 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerExceptionIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerExceptionIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerIT.java index 3e0d935be..b83e6596d 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerRollbackIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerRollbackIT.java index af1595563..70e29fa84 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerRollbackIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerRollbackIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerStateIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerStateIT.java index d45fe0f9e..a2fa9127d 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerStateIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerStateIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -203,4 +203,4 @@ public class AlfrescoSolrTrackerStateIT extends AbstractAlfrescoSolrIT makeSureTransactionHasBeenIndexed(txn.getId()); makeSureNodesHaveBeenIndexed(data.getKey().size(), sampleTextContent); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/CascadingIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/CascadingIT.java index d44a0cad6..7e10fb4d3 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/CascadingIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/CascadingIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -211,4 +211,4 @@ public class CascadingIT extends AbstractAlfrescoSolrIT Query waitForQuery = builder.build(); waitForDocCount(waitForQuery, 1, MAX_WAIT_TIME); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentPropertyValueTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentPropertyValueTrackerIT.java index 87cc9c270..7dba73fa0 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentPropertyValueTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentPropertyValueTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -127,4 +127,4 @@ public class ContentPropertyValueTrackerIT extends AbstractAlfrescoDistributedIT // Check the document is still indexed after the update. waitForDocCount(new TermQuery(new Term(authorField, authorAfterUpdate)), 1, MAX_WAIT_TIME); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentTrackerIT.java index c574311d1..c435721bc 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRangeRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRangeRouterTest.java index b2596cdba..659e7bda1 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRangeRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRangeRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -93,4 +93,4 @@ public class DBIDRangeRouterTest when(node.getId()).thenReturn(543L); assertTrue(router.routeNode(randomizer.nextInt(), randomizer.nextInt(), node)); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRouterTest.java index 1de135a26..0d1e1fdda 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -136,4 +136,4 @@ public class DBIDRouterTest nodeDistributionMap.values().toString() + ", SD = " + deviation + ", SD_NORM = " + norm + "%", norm < 30); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DateMonthRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DateMonthRouterIT.java index 247d8da95..5b1afcec9 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DateMonthRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DateMonthRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -148,4 +148,4 @@ public class DateMonthRouterIT deviation/(howManyDocuments/shardCount) * 100 < 30); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAclIdAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAclIdAlfrescoSolrTrackerIT.java index d8e94c226..e6a620e7f 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAclIdAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAclIdAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrJsonIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrJsonIT.java index 381e8f517..9f51540c1 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrJsonIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrJsonIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerIT.java index 992e02738..1b36f32fa 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -173,4 +173,4 @@ public class DistributedAlfrescoSolrTrackerIT extends AbstractAlfrescoDistribute waitForDocCountAllCores(new TermQuery(new Term(QueryConstants.FIELD_READER, "joel")), numAcls+1, 80000); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerLargeTxnIdIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerLargeTxnIdIT.java index c0b96edd4..9c23adb60 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerLargeTxnIdIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerLargeTxnIdIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -126,4 +126,4 @@ public class DistributedAlfrescoSolrTrackerLargeTxnIdIT extends AbstractAlfresco waitForDocCountAllCores(waitForQuery, 1, 80000); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerRaceIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerRaceIT.java index 6015171ee..95264a10d 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerRaceIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerRaceIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -151,4 +151,4 @@ public class DistributedAlfrescoSolrTrackerRaceIT extends AbstractAlfrescoDistri "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}]}", params("q", "t1:world", "qt", "/afts", "shards.qt", "/afts", "start", "0", "rows", "6", "sort", "id asc")); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerStateIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerStateIT.java index 610022573..ee3e0d973 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerStateIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerStateIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -193,4 +193,4 @@ public class DistributedAlfrescoSolrTrackerStateIT extends AbstractAlfrescoDistr throw new RuntimeException(exception); } } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoTrackerWithDelayedTransactionsIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoTrackerWithDelayedTransactionsIT.java index 66a2e2d92..bead5eca6 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoTrackerWithDelayedTransactionsIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoTrackerWithDelayedTransactionsIT.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 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% diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedCascadeTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedCascadeTrackerIT.java index c572dd368..e207405a3 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedCascadeTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedCascadeTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -205,4 +205,4 @@ public class DistributedCascadeTrackerIT extends AbstractAlfrescoDistributedIT prop.put("shard.method", "DB_ID_RANGE"); return prop; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateAbstractSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateAbstractSolrTrackerIT.java index 266699084..4cfd67260 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateAbstractSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateAbstractSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateMonthAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateMonthAlfrescoSolrTrackerIT.java index 43a797b24..bdae45105 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateMonthAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateMonthAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateQuarterAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateQuarterAlfrescoSolrTrackerIT.java index f2533c8e2..3cd130313 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateQuarterAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateQuarterAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateSplitYearAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateSplitYearAlfrescoSolrTrackerIT.java index d1aac24b2..a1bf5f098 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateSplitYearAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateSplitYearAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDbidRangeAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDbidRangeAlfrescoSolrTrackerIT.java index fc5d52c90..e7162dc28 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDbidRangeAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDbidRangeAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExpandDbidRangeAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExpandDbidRangeAlfrescoSolrTrackerIT.java index a1a234203..f5ac6b8e4 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExpandDbidRangeAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExpandDbidRangeAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardIdWithStaticPropertyRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardIdWithStaticPropertyRouterIT.java index 302c19bb2..ce321be17 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardIdWithStaticPropertyRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardIdWithStaticPropertyRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -170,4 +170,4 @@ public class DistributedExplicitShardIdWithStaticPropertyRouterIT extends Abstra prop.put("shard.method", ShardMethodEnum.LAST_REGISTERED_INDEXING_SHARD.toString()); return prop; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardRoutingTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardRoutingTrackerIT.java index e9f269a95..039c5d07e 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardRoutingTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardRoutingTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedPropertyBasedAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedPropertyBasedAlfrescoSolrTrackerIT.java index 979c90774..1d066c244 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedPropertyBasedAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedPropertyBasedAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedRemovePropertiesAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedRemovePropertiesAlfrescoSolrTrackerIT.java index 2aebe0b26..db37d4a4d 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedRemovePropertiesAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedRemovePropertiesAlfrescoSolrTrackerIT.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 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% @@ -111,4 +111,4 @@ public class DistributedRemovePropertiesAlfrescoSolrTrackerIT extends AbstractAl // Check that author is removed from index. waitForDocCount(new TermQuery(new Term(authorField, author)), 0, MAX_WAIT_TIME); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DocRouterFactoryTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DocRouterFactoryTest.java index a1a13e8d0..ea5fc350a 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DocRouterFactoryTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DocRouterFactoryTest.java @@ -1,68 +1,68 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker; - -import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_RANGE_KEY; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.Properties; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.index.shard.ShardMethodEnum; -import org.junit.Test; - -/** Unit tests for the {@link DocRouterFactory}. */ -public class DocRouterFactoryTest -{ - /** Check that a DB_ID_RANGE router can be created. */ - @Test - public void testDBIDRANGEWithShardRangeKey() - { - Properties mockProperties = mock(Properties.class); - when(mockProperties.containsKey(SHARD_RANGE_KEY)).thenReturn(true); - when(mockProperties.getProperty(SHARD_RANGE_KEY)).thenReturn("100000000-150000000"); - - // Call the method under test. - DocRouter docRouter = DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE); - - assertTrue("Expected to get a DBIDRangeRouter.", docRouter instanceof DBIDRangeRouter); - DBIDRangeRouter dbidRangeRouter = (DBIDRangeRouter) docRouter; - assertEquals("Unexpected start of range.", dbidRangeRouter.getStartRange(), 100000000L); - assertEquals("Unexpected end of range.", dbidRangeRouter.getEndRange(), 150000000L); - } - - /** Check that an exception is raised if the range information is missing. */ - @Test(expected = AlfrescoRuntimeException.class) - public void testDBIDRANGEWithoutShardRangeKey() - { - Properties mockProperties = mock(Properties.class); - DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker; + +import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_RANGE_KEY; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Properties; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.index.shard.ShardMethodEnum; +import org.junit.Test; + +/** Unit tests for the {@link DocRouterFactory}. */ +public class DocRouterFactoryTest +{ + /** Check that a DB_ID_RANGE router can be created. */ + @Test + public void testDBIDRANGEWithShardRangeKey() + { + Properties mockProperties = mock(Properties.class); + when(mockProperties.containsKey(SHARD_RANGE_KEY)).thenReturn(true); + when(mockProperties.getProperty(SHARD_RANGE_KEY)).thenReturn("100000000-150000000"); + + // Call the method under test. + DocRouter docRouter = DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE); + + assertTrue("Expected to get a DBIDRangeRouter.", docRouter instanceof DBIDRangeRouter); + DBIDRangeRouter dbidRangeRouter = (DBIDRangeRouter) docRouter; + assertEquals("Unexpected start of range.", dbidRangeRouter.getStartRange(), 100000000L); + assertEquals("Unexpected end of range.", dbidRangeRouter.getEndRange(), 150000000L); + } + + /** Check that an exception is raised if the range information is missing. */ + @Test(expected = AlfrescoRuntimeException.class) + public void testDBIDRANGEWithoutShardRangeKey() + { + Properties mockProperties = mock(Properties.class); + DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDRouterIT.java index f8d2a9962..f412528ea 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -182,4 +182,4 @@ public class ExplicitIDRouterIT // Asserts that all documents have been assigned to the explicit shard ID. assertEquals(shardCount * howManyDocumentsPerShard, nodeDistributionMap.get(node.getExplicitShardId()).intValue()); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDWithLRISRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDWithLRISRouterIT.java index 966b915c3..f43e529d8 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDWithLRISRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDWithLRISRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -185,4 +185,4 @@ public class ExplicitIDWithLRISRouterIT nodeDistributionMap.values().toString() + ", SD = " + deviation + ", SD_NORM = " + norm + "%", norm < 30); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouterTest.java index fafd0aec1..16454ff11 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -155,4 +155,4 @@ public class ExplicitShardIdWithDynamicPropertyRouterTest assertFalse(router.routeNode(shardCount, shardInstance + 1, node)); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouterTest.java index 0b2d5bfb4..ed5a10d64 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -111,4 +111,4 @@ public class ExplicitShardIdWithStaticPropertyRouterTest assertFalse(router.routeNode(shardCount, shardInstance + 1, node)); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/MetadataTrackerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/MetadataTrackerTest.java index 163e30bad..55cb37764 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/MetadataTrackerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/MetadataTrackerTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ModelTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ModelTrackerIT.java index 69ec1f7c0..fb09fa42d 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ModelTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ModelTrackerIT.java @@ -1,200 +1,200 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Properties; - -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.client.AlfrescoModel; -import org.alfresco.solr.client.AlfrescoModelDiff; -import org.alfresco.solr.client.AlfrescoModelDiff.TYPE; -import org.alfresco.solr.client.SOLRAPIClient; -import org.json.JSONException; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class ModelTrackerIT -{ - private ModelTracker modelTracker; - - @Mock - private SOLRAPIClient repositoryClient; - private String coreName = "theCoreName"; - @Mock - private InformationServer srv; - @Mock - private Properties props; - @Mock - private TrackerStats trackerStats; - - private static File alfrescoModelDir; - - @BeforeClass - public static void setUpBeforeClass() throws Exception - { - alfrescoModelDir = new File("target/testalfrescoModels"); - alfrescoModelDir.mkdir(); - } - - @AfterClass - public static void tearDownAfterClass() throws Exception - { - try { - File[] listFiles = alfrescoModelDir.listFiles(); - for (File file : listFiles) - { - file.delete(); - } - alfrescoModelDir.delete(); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - - @Before - public void setUp() throws Exception - { - when(props.getProperty("alfresco.stores", "workspace://SpacesStore")).thenReturn("workspace://SpacesStore"); - when(props.getProperty("alfresco.batch.count", "5000")).thenReturn("5000"); - when(props.getProperty("alfresco.maxLiveSearchers", "2")).thenReturn("2"); - when(props.getProperty("shard.count", "1")).thenReturn("1"); - when(props.getProperty("shard.instance", "0")).thenReturn("0"); - when(this.srv.getTrackerStats()).thenReturn(trackerStats); - System.setProperty("solr.model.dir", alfrescoModelDir.getAbsolutePath()); - this.modelTracker = new ModelTracker(null, props, repositoryClient, coreName, srv); - } - - @After - public void tearDown() throws Exception - { - } - - @Test - public void testDoTrack() throws AuthenticationException, IOException, JSONException - { - ModelTracker spiedModelTracker = spy(this.modelTracker); - spiedModelTracker.doTrack("AnIterationId"); - - verify(this.srv).getRegisteredSearcherCount(); - verify(spiedModelTracker).trackModels(false); - } - - @Test - public void testTrackModels() throws AuthenticationException, IOException, JSONException - { - final String name = setUpTestTrackModels(); - - this.modelTracker.trackModels(false); - - verifyTestTrackModels(name); - } - - private void verifyTestTrackModels(final String name) - { - verify(this.srv).afterInitModels(); - - assertTrue(alfrescoModelDir.isDirectory()); - - File[] modelRepresentations = alfrescoModelDir.listFiles(new FileFilter() - { - @Override - public boolean accept(File pathname) - { - return pathname.getName().startsWith(name); - } - }); - assertEquals(1, modelRepresentations.length); - } - - @SuppressWarnings("unchecked") - private String setUpTestTrackModels() throws AuthenticationException, IOException, JSONException - { - QName modelName = QName.createQName("qname"); - TYPE type = TYPE.CHANGED; - Long oldChecksum = new Long(0); - Long newChecksum = new Long(1); - AlfrescoModelDiff diff = new AlfrescoModelDiff(modelName, type, oldChecksum, newChecksum); - List modelDiffs = new ArrayList<>(); - modelDiffs.add(diff); - when(this.repositoryClient.getModelsDiff(any(String.class),any(List.class))).thenReturn(modelDiffs); - - final String name = "a model name"; - M2Model model = M2Model.createModel(name); - M2Model spiedModel = spy(model); - model.createNamespace("uri", "prefix"); - AlfrescoModel alfrescoModel = new AlfrescoModel(spiedModel, newChecksum); - when(this.repositoryClient.getModel(any(String.class),eq(modelName))).thenReturn(alfrescoModel); - - NamespaceDAO namespaceDao = mock(NamespaceDAO.class); - Collection values = new ArrayList<>(); - values.add("prefix"); - when(namespaceDao.getPrefixes(anyString())).thenReturn(values); - when(this.srv.getNamespaceDAO()).thenReturn(namespaceDao); - when(this.srv.getM2Model(modelName)).thenReturn(spiedModel); - when(this.srv.putModel(spiedModel)).thenReturn(true); - return name; - } - - @Test - public void testEnsureFirstModelSync() throws AuthenticationException, IOException, JSONException - { - final String name = setUpTestTrackModels(); - - this.modelTracker.ensureFirstModelSync(); - - verifyTestTrackModels(name); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Properties; + +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.client.AlfrescoModel; +import org.alfresco.solr.client.AlfrescoModelDiff; +import org.alfresco.solr.client.AlfrescoModelDiff.TYPE; +import org.alfresco.solr.client.SOLRAPIClient; +import org.json.JSONException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ModelTrackerIT +{ + private ModelTracker modelTracker; + + @Mock + private SOLRAPIClient repositoryClient; + private String coreName = "theCoreName"; + @Mock + private InformationServer srv; + @Mock + private Properties props; + @Mock + private TrackerStats trackerStats; + + private static File alfrescoModelDir; + + @BeforeClass + public static void setUpBeforeClass() throws Exception + { + alfrescoModelDir = new File("target/testalfrescoModels"); + alfrescoModelDir.mkdir(); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception + { + try { + File[] listFiles = alfrescoModelDir.listFiles(); + for (File file : listFiles) + { + file.delete(); + } + alfrescoModelDir.delete(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + @Before + public void setUp() throws Exception + { + when(props.getProperty("alfresco.stores", "workspace://SpacesStore")).thenReturn("workspace://SpacesStore"); + when(props.getProperty("alfresco.batch.count", "5000")).thenReturn("5000"); + when(props.getProperty("alfresco.maxLiveSearchers", "2")).thenReturn("2"); + when(props.getProperty("shard.count", "1")).thenReturn("1"); + when(props.getProperty("shard.instance", "0")).thenReturn("0"); + when(this.srv.getTrackerStats()).thenReturn(trackerStats); + System.setProperty("solr.model.dir", alfrescoModelDir.getAbsolutePath()); + this.modelTracker = new ModelTracker(null, props, repositoryClient, coreName, srv); + } + + @After + public void tearDown() throws Exception + { + } + + @Test + public void testDoTrack() throws AuthenticationException, IOException, JSONException + { + ModelTracker spiedModelTracker = spy(this.modelTracker); + spiedModelTracker.doTrack("AnIterationId"); + + verify(this.srv).getRegisteredSearcherCount(); + verify(spiedModelTracker).trackModels(false); + } + + @Test + public void testTrackModels() throws AuthenticationException, IOException, JSONException + { + final String name = setUpTestTrackModels(); + + this.modelTracker.trackModels(false); + + verifyTestTrackModels(name); + } + + private void verifyTestTrackModels(final String name) + { + verify(this.srv).afterInitModels(); + + assertTrue(alfrescoModelDir.isDirectory()); + + File[] modelRepresentations = alfrescoModelDir.listFiles(new FileFilter() + { + @Override + public boolean accept(File pathname) + { + return pathname.getName().startsWith(name); + } + }); + assertEquals(1, modelRepresentations.length); + } + + @SuppressWarnings("unchecked") + private String setUpTestTrackModels() throws AuthenticationException, IOException, JSONException + { + QName modelName = QName.createQName("qname"); + TYPE type = TYPE.CHANGED; + Long oldChecksum = new Long(0); + Long newChecksum = new Long(1); + AlfrescoModelDiff diff = new AlfrescoModelDiff(modelName, type, oldChecksum, newChecksum); + List modelDiffs = new ArrayList<>(); + modelDiffs.add(diff); + when(this.repositoryClient.getModelsDiff(any(String.class),any(List.class))).thenReturn(modelDiffs); + + final String name = "a model name"; + M2Model model = M2Model.createModel(name); + M2Model spiedModel = spy(model); + model.createNamespace("uri", "prefix"); + AlfrescoModel alfrescoModel = new AlfrescoModel(spiedModel, newChecksum); + when(this.repositoryClient.getModel(any(String.class),eq(modelName))).thenReturn(alfrescoModel); + + NamespaceDAO namespaceDao = mock(NamespaceDAO.class); + Collection values = new ArrayList<>(); + values.add("prefix"); + when(namespaceDao.getPrefixes(anyString())).thenReturn(values); + when(this.srv.getNamespaceDAO()).thenReturn(namespaceDao); + when(this.srv.getM2Model(modelName)).thenReturn(spiedModel); + when(this.srv.putModel(spiedModel)).thenReturn(true); + return name; + } + + @Test + public void testEnsureFirstModelSync() throws AuthenticationException, IOException, JSONException + { + final String name = setUpTestTrackModels(); + + this.modelTracker.ensureFirstModelSync(); + + verifyTestTrackModels(name); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/PropertyRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/PropertyRouterIT.java index 33731a695..0c0774d96 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/PropertyRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/PropertyRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -248,4 +248,4 @@ public class PropertyRouterIT nodeDistributionMap.values().toString() + ", SD = " + deviation + ", SD_NORM = " + norm + "%", norm < 30); } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/SolrTrackerSchedulerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/SolrTrackerSchedulerTest.java index 9aa8ab969..9ba01ffff 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/SolrTrackerSchedulerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/SolrTrackerSchedulerTest.java @@ -1,216 +1,216 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.util.Arrays; -import java.util.List; -import java.util.Properties; - -import org.alfresco.solr.AlfrescoCoreAdminHandler; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.client.SOLRAPIClient; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.quartz.CronTrigger; -import org.quartz.JobDetail; -import org.quartz.JobKey; -import org.quartz.Scheduler; -import org.quartz.SchedulerException; -import org.quartz.Trigger; -import org.quartz.impl.matchers.GroupMatcher; - - -@RunWith(MockitoJUnitRunner.class) -public class SolrTrackerSchedulerTest -{ - @Mock - private AlfrescoCoreAdminHandler adminHandler; - private SolrTrackerScheduler trackerScheduler; - private String CORE_NAME = "coreName"; - private Scheduler spiedQuartzScheduler; - @Mock - SOLRAPIClient client; - String coreName = "alfresco"; - @Mock - InformationServer informationServer; - Properties props; - @Before - public void setUp() throws Exception - { - this.trackerScheduler = new SolrTrackerScheduler(adminHandler); - this.spiedQuartzScheduler = spy(this.trackerScheduler.scheduler); - this.trackerScheduler.scheduler = spiedQuartzScheduler; - props = new Properties(); - props.put("alfresco.stores", "workspace://SpacesStore"); - props.put("alfresco.batch.count", "5000"); - props.put("alfresco.maxLiveSearchers", "2"); - props.put("shard.count", "1"); - props.put("shard.instance", "0"); - props.put("shard.method", "SHARD_METHOD_DBID"); - props.put("alfresco.template", ""); - props.put("alfresco.index.transformContent", "true"); - } - - @After - public void tearDown() throws Exception - { - if (this.trackerScheduler != null && !this.trackerScheduler.isShutdown()) - { - this.trackerScheduler.shutdown(); - } - } - - @Test - public void testAclSchedule() throws SchedulerException - { - String exp = "0/12 * * * * ? *"; - props.put("alfresco.acl.tracker.cron", exp); - AclTracker aclTracker = new AclTracker(props, client, coreName, informationServer); - this.trackerScheduler.schedule(aclTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } - @Test - public void testContentTrackerSchedule() throws SchedulerException - { - String exp = "0/5 * * * * ? *"; - props.put("alfresco.content.tracker.cron", exp); - ContentTracker contentTracker = new ContentTracker(props, client, coreName, informationServer); - this.trackerScheduler.schedule(contentTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } - - private void checkCronExpression(String exp) throws SchedulerException - { - for (JobKey jobKey : this.trackerScheduler.scheduler.getJobKeys(GroupMatcher.jobGroupEquals(SolrTrackerScheduler.SOLR_JOB_GROUP))) - { - List triggers = this.trackerScheduler.scheduler.getTriggersOfJob(jobKey); - CronTrigger t = (CronTrigger) triggers.get(0); - String cronExp = t.getCronExpression(); - Assert.assertEquals(exp, cronExp); - } - } - @Test - public void testMetaDataTrackerSchedule() throws SchedulerException - { - String exp = "0/4 * * * * ? *"; - props.put("alfresco.metadata.tracker.cron", exp); - MetadataTracker metadataTracker = new MetadataTracker(props, client, exp, informationServer); - this.trackerScheduler.schedule(metadataTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } - @Test - public void testSchedule() throws SchedulerException - { - String exp = "0/1 * * * * ? *"; - props.put("alfresco.cascade.tracker.cron",exp); - CascadeTracker cascadeTracker = new CascadeTracker(props, client, exp, informationServer); - this.trackerScheduler.schedule(cascadeTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } - @Test - public void testShutdown() throws SchedulerException - { - this.trackerScheduler.shutdown(); - assertTrue(trackerScheduler.isShutdown()); - } - - @Test - public void testDeleteTrackerJobs() throws SchedulerException - { - ContentTracker contentTracker = new ContentTracker(); - MetadataTracker metadataTracker = new MetadataTracker(); - AclTracker aclTracker = new AclTracker(); - this.trackerScheduler.deleteTrackerJobs(CORE_NAME, - Arrays.asList(new Tracker[] { contentTracker, metadataTracker, aclTracker })); - verify(spiedQuartzScheduler, times(3)).deleteJob(any(JobKey.class)); - } - - @Test - public void testDeleteTrackerJob() throws SchedulerException - { - ModelTracker modelTracker = new ModelTracker(); - this.trackerScheduler.deleteTrackerJob(CORE_NAME, modelTracker); - verify(spiedQuartzScheduler).deleteJob(any(JobKey.class)); - } - - @Test - public void testDeleteTrackerInstanceJob() throws SchedulerException - { - CascadeTracker cascadeTracker = new CascadeTracker(props, client, coreName, informationServer); - CascadeTracker cascadeTracker2 = new CascadeTracker(props, client, coreName, informationServer); - this.trackerScheduler.schedule(cascadeTracker, CORE_NAME, props); - - //Try deleting the same class but a different instance. It not possible. - this.trackerScheduler.deleteJobForTrackerInstance(CORE_NAME, cascadeTracker2); - verify(spiedQuartzScheduler, never()).deleteJob(any(JobKey.class)); - - //No try deleting the exact instance of the tracker class - this.trackerScheduler.deleteJobForTrackerInstance(CORE_NAME, cascadeTracker); - verify(spiedQuartzScheduler, times(1)).deleteJob(any(JobKey.class)); - - } - - @Test - public void newSchedulerIsNotShutdown() throws SchedulerException - { - assertFalse(trackerScheduler.isShutdown()); - } - - @Test - public void testPauseAll() throws SchedulerException - { - this.trackerScheduler.pauseAll(); - verify(this.spiedQuartzScheduler).pauseAll(); - } - @Test - public void testModeltTrackerSchedule() throws SchedulerException - { - String exp = "0/20 * * * * ? *"; - props.put("alfresco.model.tracker.cron", exp); - ModelTracker modelTracker = new ModelTracker("alfresco", props, client, CORE_NAME, informationServer); - this.trackerScheduler.schedule(modelTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.alfresco.solr.AlfrescoCoreAdminHandler; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.client.SOLRAPIClient; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.quartz.CronTrigger; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.impl.matchers.GroupMatcher; + + +@RunWith(MockitoJUnitRunner.class) +public class SolrTrackerSchedulerTest +{ + @Mock + private AlfrescoCoreAdminHandler adminHandler; + private SolrTrackerScheduler trackerScheduler; + private String CORE_NAME = "coreName"; + private Scheduler spiedQuartzScheduler; + @Mock + SOLRAPIClient client; + String coreName = "alfresco"; + @Mock + InformationServer informationServer; + Properties props; + @Before + public void setUp() throws Exception + { + this.trackerScheduler = new SolrTrackerScheduler(adminHandler); + this.spiedQuartzScheduler = spy(this.trackerScheduler.scheduler); + this.trackerScheduler.scheduler = spiedQuartzScheduler; + props = new Properties(); + props.put("alfresco.stores", "workspace://SpacesStore"); + props.put("alfresco.batch.count", "5000"); + props.put("alfresco.maxLiveSearchers", "2"); + props.put("shard.count", "1"); + props.put("shard.instance", "0"); + props.put("shard.method", "SHARD_METHOD_DBID"); + props.put("alfresco.template", ""); + props.put("alfresco.index.transformContent", "true"); + } + + @After + public void tearDown() throws Exception + { + if (this.trackerScheduler != null && !this.trackerScheduler.isShutdown()) + { + this.trackerScheduler.shutdown(); + } + } + + @Test + public void testAclSchedule() throws SchedulerException + { + String exp = "0/12 * * * * ? *"; + props.put("alfresco.acl.tracker.cron", exp); + AclTracker aclTracker = new AclTracker(props, client, coreName, informationServer); + this.trackerScheduler.schedule(aclTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } + @Test + public void testContentTrackerSchedule() throws SchedulerException + { + String exp = "0/5 * * * * ? *"; + props.put("alfresco.content.tracker.cron", exp); + ContentTracker contentTracker = new ContentTracker(props, client, coreName, informationServer); + this.trackerScheduler.schedule(contentTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } + + private void checkCronExpression(String exp) throws SchedulerException + { + for (JobKey jobKey : this.trackerScheduler.scheduler.getJobKeys(GroupMatcher.jobGroupEquals(SolrTrackerScheduler.SOLR_JOB_GROUP))) + { + List triggers = this.trackerScheduler.scheduler.getTriggersOfJob(jobKey); + CronTrigger t = (CronTrigger) triggers.get(0); + String cronExp = t.getCronExpression(); + Assert.assertEquals(exp, cronExp); + } + } + @Test + public void testMetaDataTrackerSchedule() throws SchedulerException + { + String exp = "0/4 * * * * ? *"; + props.put("alfresco.metadata.tracker.cron", exp); + MetadataTracker metadataTracker = new MetadataTracker(props, client, exp, informationServer); + this.trackerScheduler.schedule(metadataTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } + @Test + public void testSchedule() throws SchedulerException + { + String exp = "0/1 * * * * ? *"; + props.put("alfresco.cascade.tracker.cron",exp); + CascadeTracker cascadeTracker = new CascadeTracker(props, client, exp, informationServer); + this.trackerScheduler.schedule(cascadeTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } + @Test + public void testShutdown() throws SchedulerException + { + this.trackerScheduler.shutdown(); + assertTrue(trackerScheduler.isShutdown()); + } + + @Test + public void testDeleteTrackerJobs() throws SchedulerException + { + ContentTracker contentTracker = new ContentTracker(); + MetadataTracker metadataTracker = new MetadataTracker(); + AclTracker aclTracker = new AclTracker(); + this.trackerScheduler.deleteTrackerJobs(CORE_NAME, + Arrays.asList(new Tracker[] { contentTracker, metadataTracker, aclTracker })); + verify(spiedQuartzScheduler, times(3)).deleteJob(any(JobKey.class)); + } + + @Test + public void testDeleteTrackerJob() throws SchedulerException + { + ModelTracker modelTracker = new ModelTracker(); + this.trackerScheduler.deleteTrackerJob(CORE_NAME, modelTracker); + verify(spiedQuartzScheduler).deleteJob(any(JobKey.class)); + } + + @Test + public void testDeleteTrackerInstanceJob() throws SchedulerException + { + CascadeTracker cascadeTracker = new CascadeTracker(props, client, coreName, informationServer); + CascadeTracker cascadeTracker2 = new CascadeTracker(props, client, coreName, informationServer); + this.trackerScheduler.schedule(cascadeTracker, CORE_NAME, props); + + //Try deleting the same class but a different instance. It not possible. + this.trackerScheduler.deleteJobForTrackerInstance(CORE_NAME, cascadeTracker2); + verify(spiedQuartzScheduler, never()).deleteJob(any(JobKey.class)); + + //No try deleting the exact instance of the tracker class + this.trackerScheduler.deleteJobForTrackerInstance(CORE_NAME, cascadeTracker); + verify(spiedQuartzScheduler, times(1)).deleteJob(any(JobKey.class)); + + } + + @Test + public void newSchedulerIsNotShutdown() throws SchedulerException + { + assertFalse(trackerScheduler.isShutdown()); + } + + @Test + public void testPauseAll() throws SchedulerException + { + this.trackerScheduler.pauseAll(); + verify(this.spiedQuartzScheduler).pauseAll(); + } + @Test + public void testModeltTrackerSchedule() throws SchedulerException + { + String exp = "0/20 * * * * ? *"; + props.put("alfresco.model.tracker.cron", exp); + ModelTracker modelTracker = new ModelTracker("alfresco", props, client, CORE_NAME, informationServer); + this.trackerScheduler.schedule(modelTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerJobTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerJobTest.java index 0146e5a79..a83158741 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerJobTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerJobTest.java @@ -1,79 +1,79 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.quartz.JobDataMap; -import org.quartz.JobDetail; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; - -/** - * Tests for the {@link TrackerJob} class. - * - * @author Matt Ward - */ -@RunWith(MockitoJUnitRunner.class) -public class TrackerJobTest -{ - private TrackerJob trackerJob; // Class under test - private @Mock Tracker tracker; - private @Mock JobExecutionContext jec; - private @Mock JobDetail jobDetail; - private JobDataMap jobDataMap; - - @Before - public void setUp() throws Exception - - { - trackerJob = new TrackerJob(); - - jobDataMap = new JobDataMap(); - jobDataMap.put("TRACKER", tracker); - - when(jec.getJobDetail()).thenReturn(jobDetail); - when(jobDetail.getJobDataMap()).thenReturn(jobDataMap); - } - - @Test - public void canExecuteTrackerJob() throws JobExecutionException - { - trackerJob.execute(jec); - - // When the TrackerJob is triggered, then the Tracker's execute() method - // should be invoked. - verify(tracker).track(); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * Tests for the {@link TrackerJob} class. + * + * @author Matt Ward + */ +@RunWith(MockitoJUnitRunner.class) +public class TrackerJobTest +{ + private TrackerJob trackerJob; // Class under test + private @Mock Tracker tracker; + private @Mock JobExecutionContext jec; + private @Mock JobDetail jobDetail; + private JobDataMap jobDataMap; + + @Before + public void setUp() throws Exception + + { + trackerJob = new TrackerJob(); + + jobDataMap = new JobDataMap(); + jobDataMap.put("TRACKER", tracker); + + when(jec.getJobDetail()).thenReturn(jobDetail); + when(jobDetail.getJobDataMap()).thenReturn(jobDataMap); + } + + @Test + public void canExecuteTrackerJob() throws JobExecutionException + { + trackerJob.execute(jec); + + // When the TrackerJob is triggered, then the Tracker's execute() method + // should be invoked. + verify(tracker).track(); + } + +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerRegistryTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerRegistryTest.java index d002b31c2..580b802af 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerRegistryTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerRegistryTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactoryTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactoryTest.java index 5e89a44f7..46a016c21 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactoryTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactoryTest.java @@ -1,161 +1,161 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.tracker.pool; - -import static org.junit.Assert.assertEquals; - -import java.util.Properties; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Tests for the {@link DefaultTrackerPoolFactory}. - * - * @author Matt Ward - */ -public class DefaultTrackerPoolFactoryTest -{ - private DefaultTrackerPoolFactory poolFactory; - private Properties properties; - ThreadPoolExecutor tpe; - @Before - public void setup() - { - poolFactory = null; // Ensure we don't accidentally reuse between runs. - properties = new Properties(); - } - @After - public void teardown() - { - tpe.shutdownNow(); - tpe = null; - } - - @Test - public void testDefaults() - { - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "TrackerName"); - - tpe = poolFactory.create(); - - assertEquals(4, tpe.getCorePoolSize()); - assertEquals(4, tpe.getMaximumPoolSize()); - assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - - @Test - public void testNonDefaultProperties() - { - properties.put("alfresco.corePoolSize", "30"); - properties.put("alfresco.maximumPoolSize", "40"); - properties.put("alfresco.keepAliveTime", "200"); - - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "TrackerName"); - - tpe = poolFactory.create(); - - assertEquals(30, tpe.getCorePoolSize()); - assertEquals(40, tpe.getMaximumPoolSize()); - assertEquals(200, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testAclDefaultProperties() - { - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker"); - - tpe = poolFactory.create(); - assertEquals(4, tpe.getCorePoolSize()); - assertEquals(4, tpe.getMaximumPoolSize()); - assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testAclProperties() - { - properties.put("alfresco.acl.tracker.corePoolSize", "30"); - properties.put("alfresco.acl.tracker.maximumPoolSize", "40"); - properties.put("alfresco.acl.tracker.keepAliveTime", "200"); - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker"); - tpe = poolFactory.create(); - - assertEquals(30, tpe.getCorePoolSize()); - assertEquals(40, tpe.getMaximumPoolSize()); - assertEquals(200, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testContentDefaultProperties() - { - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "ContentTracker"); - - tpe = poolFactory.create(); - - assertEquals(4, tpe.getCorePoolSize()); - assertEquals(4, tpe.getMaximumPoolSize()); - assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testContentProperties() - { - properties.put("alfresco.content.tracker.corePoolSize", "100"); - properties.put("alfresco.content.tracker.maximumPoolSize", "140"); - properties.put("alfresco.content.tracker.keepAliveTime", "201"); - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "ContentTracker"); - tpe = poolFactory.create(); - - assertEquals(100, tpe.getCorePoolSize()); - assertEquals(140, tpe.getMaximumPoolSize()); - assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testMetaDataDefaultProperties() - { - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker"); - - tpe = poolFactory.create(); - - assertEquals(4, tpe.getCorePoolSize()); - assertEquals(4, tpe.getMaximumPoolSize()); - assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testMetaDataProperties() - { - properties.put("alfresco.metadata.tracker.corePoolSize", "100"); - properties.put("alfresco.metadata.tracker.maximumPoolSize", "140"); - properties.put("alfresco.metadata.tracker.keepAliveTime", "201"); - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker"); - - tpe = poolFactory.create(); - - assertEquals(100, tpe.getCorePoolSize()); - assertEquals(140, tpe.getMaximumPoolSize()); - assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.tracker.pool; + +import static org.junit.Assert.assertEquals; + +import java.util.Properties; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link DefaultTrackerPoolFactory}. + * + * @author Matt Ward + */ +public class DefaultTrackerPoolFactoryTest +{ + private DefaultTrackerPoolFactory poolFactory; + private Properties properties; + ThreadPoolExecutor tpe; + @Before + public void setup() + { + poolFactory = null; // Ensure we don't accidentally reuse between runs. + properties = new Properties(); + } + @After + public void teardown() + { + tpe.shutdownNow(); + tpe = null; + } + + @Test + public void testDefaults() + { + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "TrackerName"); + + tpe = poolFactory.create(); + + assertEquals(4, tpe.getCorePoolSize()); + assertEquals(4, tpe.getMaximumPoolSize()); + assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + + @Test + public void testNonDefaultProperties() + { + properties.put("alfresco.corePoolSize", "30"); + properties.put("alfresco.maximumPoolSize", "40"); + properties.put("alfresco.keepAliveTime", "200"); + + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "TrackerName"); + + tpe = poolFactory.create(); + + assertEquals(30, tpe.getCorePoolSize()); + assertEquals(40, tpe.getMaximumPoolSize()); + assertEquals(200, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testAclDefaultProperties() + { + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker"); + + tpe = poolFactory.create(); + assertEquals(4, tpe.getCorePoolSize()); + assertEquals(4, tpe.getMaximumPoolSize()); + assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testAclProperties() + { + properties.put("alfresco.acl.tracker.corePoolSize", "30"); + properties.put("alfresco.acl.tracker.maximumPoolSize", "40"); + properties.put("alfresco.acl.tracker.keepAliveTime", "200"); + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker"); + tpe = poolFactory.create(); + + assertEquals(30, tpe.getCorePoolSize()); + assertEquals(40, tpe.getMaximumPoolSize()); + assertEquals(200, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testContentDefaultProperties() + { + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "ContentTracker"); + + tpe = poolFactory.create(); + + assertEquals(4, tpe.getCorePoolSize()); + assertEquals(4, tpe.getMaximumPoolSize()); + assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testContentProperties() + { + properties.put("alfresco.content.tracker.corePoolSize", "100"); + properties.put("alfresco.content.tracker.maximumPoolSize", "140"); + properties.put("alfresco.content.tracker.keepAliveTime", "201"); + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "ContentTracker"); + tpe = poolFactory.create(); + + assertEquals(100, tpe.getCorePoolSize()); + assertEquals(140, tpe.getMaximumPoolSize()); + assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testMetaDataDefaultProperties() + { + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker"); + + tpe = poolFactory.create(); + + assertEquals(4, tpe.getCorePoolSize()); + assertEquals(4, tpe.getMaximumPoolSize()); + assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testMetaDataProperties() + { + properties.put("alfresco.metadata.tracker.corePoolSize", "100"); + properties.put("alfresco.metadata.tracker.maximumPoolSize", "140"); + properties.put("alfresco.metadata.tracker.keepAliveTime", "201"); + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker"); + + tpe = poolFactory.create(); + + assertEquals(100, tpe.getCorePoolSize()); + assertEquals(140, tpe.getMaximumPoolSize()); + assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java index b2f69d937..3d47f504f 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandlerIT.java b/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandlerIT.java index 7ed7d0358..f237a7112 100644 --- a/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoSearchHandlerFacetQueryIT.java b/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoSearchHandlerFacetQueryIT.java index d910cdd21..dba98a7c5 100644 --- a/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoSearchHandlerFacetQueryIT.java +++ b/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoSearchHandlerFacetQueryIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AclReport.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AclReport.java index 5e0cdc407..2bdf9891a 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AclReport.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AclReport.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AlfrescoClientDataModelServicesFactory.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AlfrescoClientDataModelServicesFactory.java index 6897461a4..fdcff5877 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AlfrescoClientDataModelServicesFactory.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AlfrescoClientDataModelServicesFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/BoundedDeque.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/BoundedDeque.java index a4eb7dd89..decdbbf64 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/BoundedDeque.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/BoundedDeque.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/IndexTrackingShutdownException.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/IndexTrackingShutdownException.java index c528ea19e..5462a746e 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/IndexTrackingShutdownException.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/IndexTrackingShutdownException.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/InformationServerCollectionProvider.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/InformationServerCollectionProvider.java index a43cbcc3b..9ad50919c 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/InformationServerCollectionProvider.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/InformationServerCollectionProvider.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/NodeReport.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/NodeReport.java index 8bda05107..52105b4b6 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/NodeReport.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/NodeReport.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/TrackerState.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/TrackerState.java index b7c2a9909..1674d62cc 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/TrackerState.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/TrackerState.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/IOpenBitSet.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/IOpenBitSet.java index a2ac19197..180340658 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/IOpenBitSet.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/IOpenBitSet.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -23,25 +23,25 @@ * along with Alfresco. If not, see . * #L% */ - -package org.alfresco.solr.adapters; - -/** - * The reason we have this interface is so that lucene-free dependent classes can be dependent on IOpenBitSet instead of the - * lucene-version-specific OpenBitSet. - * @author Ahmed Owian - */ -public interface IOpenBitSet -{ - + +package org.alfresco.solr.adapters; + +/** + * The reason we have this interface is so that lucene-free dependent classes can be dependent on IOpenBitSet instead of the + * lucene-version-specific OpenBitSet. + * @author Ahmed Owian + */ +public interface IOpenBitSet +{ + public void set(long txid); - + public void or(IOpenBitSet duplicatedTxInIndex); - + public long nextSetBit(long l); - + public long cardinality(); - + public boolean get(long i); - -} \ No newline at end of file + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/ISimpleOrderedMap.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/ISimpleOrderedMap.java index 645409e5d..b73dde5c1 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/ISimpleOrderedMap.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/ISimpleOrderedMap.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,17 +24,17 @@ * #L% */ -package org.alfresco.solr.adapters; - - -/** - * The reason we have this interface is so that lucene-free dependent classes can be dependent on ISimpleOrderedMap instead of the - * lucene-version-specific SimpleOrderedMap. - * @author Ahmed Owian - */ -public interface ISimpleOrderedMap -{ - - void add(String name, T val); - -} +package org.alfresco.solr.adapters; + + +/** + * The reason we have this interface is so that lucene-free dependent classes can be dependent on ISimpleOrderedMap instead of the + * lucene-version-specific SimpleOrderedMap. + * @author Ahmed Owian + */ +public interface ISimpleOrderedMap +{ + + void add(String name, T val); + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Acl.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Acl.java index ae9b54a6e..7bbbc00a9 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Acl.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Acl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,59 +24,59 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * SOLR-side representation of basic ACL information. - * - * @since 4.0 - */ -public class Acl -{ - private final long aclChangeSetId; - private final long id; - - public Acl(long aclChangeSetId, long id) - { - this.aclChangeSetId = aclChangeSetId; - this.id = id; - } - - @Override - public String toString() - { - return "Acl [aclChangeSetId=" + aclChangeSetId + ", id=" + id + "]"; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + (int) (aclChangeSetId ^ (aclChangeSetId >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - Acl other = (Acl) obj; - if (aclChangeSetId != other.aclChangeSetId) return false; - if (id != other.id) return false; - return true; - } - - public long getAclChangeSetId() - { - return aclChangeSetId; - } - - public long getId() - { - return id; - } -} +package org.alfresco.solr.client; + +/** + * SOLR-side representation of basic ACL information. + * + * @since 4.0 + */ +public class Acl +{ + private final long aclChangeSetId; + private final long id; + + public Acl(long aclChangeSetId, long id) + { + this.aclChangeSetId = aclChangeSetId; + this.id = id; + } + + @Override + public String toString() + { + return "Acl [aclChangeSetId=" + aclChangeSetId + ", id=" + id + "]"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (int) (aclChangeSetId ^ (aclChangeSetId >>> 32)); + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Acl other = (Acl) obj; + if (aclChangeSetId != other.aclChangeSetId) return false; + if (id != other.id) return false; + return true; + } + + public long getAclChangeSetId() + { + return aclChangeSetId; + } + + public long getId() + { + return id; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSet.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSet.java index 029d937c4..2ab27de82 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSet.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSet.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,68 +24,68 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * SOLR-side representation of ACL ChangeSet information. - * - * @since 4.0 - */ -public class AclChangeSet -{ - private final long id; - private final long commitTimeMs; - private final int aclCount; - - public AclChangeSet(long id, long commitTimeMs, int aclCount) - { - super(); - this.id = id; - this.commitTimeMs = commitTimeMs; - this.aclCount = aclCount; - } - - @Override - public String toString() - { - return "AclChangeSet [id=" + id + ", commitTimeMs=" + commitTimeMs + ", aclCount=" - + aclCount + "]"; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + aclCount; - result = prime * result + (int) (commitTimeMs ^ (commitTimeMs >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - AclChangeSet other = (AclChangeSet) obj; - if (aclCount != other.aclCount) return false; - if (commitTimeMs != other.commitTimeMs) return false; - if (id != other.id) return false; - return true; - } - - public long getId() - { - return id; - } - public long getCommitTimeMs() - { - return commitTimeMs; - } - public int getAclCount() - { - return aclCount; - } -} +package org.alfresco.solr.client; + +/** + * SOLR-side representation of ACL ChangeSet information. + * + * @since 4.0 + */ +public class AclChangeSet +{ + private final long id; + private final long commitTimeMs; + private final int aclCount; + + public AclChangeSet(long id, long commitTimeMs, int aclCount) + { + super(); + this.id = id; + this.commitTimeMs = commitTimeMs; + this.aclCount = aclCount; + } + + @Override + public String toString() + { + return "AclChangeSet [id=" + id + ", commitTimeMs=" + commitTimeMs + ", aclCount=" + + aclCount + "]"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + aclCount; + result = prime * result + (int) (commitTimeMs ^ (commitTimeMs >>> 32)); + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + AclChangeSet other = (AclChangeSet) obj; + if (aclCount != other.aclCount) return false; + if (commitTimeMs != other.commitTimeMs) return false; + if (id != other.id) return false; + return true; + } + + public long getId() + { + return id; + } + public long getCommitTimeMs() + { + return commitTimeMs; + } + public int getAclCount() + { + return aclCount; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java index 052064051..b3a54f7d2 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclReaders.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclReaders.java index 04f5c1db1..682ac418e 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclReaders.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclReaders.java @@ -1,125 +1,125 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -import static java.util.Collections.unmodifiableList; - -import java.util.ArrayList; -import java.util.List; - -/** - * SOLR-side representation of ACL Readers information. - * - * @since 4.0 - */ -public class AclReaders -{ - private final long id; - - private List readers; - - private final List denied; - - private final long aclChangeSetId; - - private final String tenantDomain; - - public AclReaders(long id, List readers, List denied, long aclChangeSetId, String tenantDomain) - { - this.id = id; - this.readers = (readers == null ? null : new ArrayList<>(readers)); - this.denied = (denied == null ? null : new ArrayList<>(denied)); - this.aclChangeSetId = aclChangeSetId; - this.tenantDomain = tenantDomain; - } - - @Override - public String toString() - { - return "AclReaders [id=" + id + ", readers=" + readers + ", denied=" + denied + ", tenantDomain=" + tenantDomain + "]"; - } - - /** - * ID should be enough for hashCode() and equals(). - */ - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + (int) (id ^ (id >>> 32)); - return result; - } - - /** - * ID should be enough for hashCode() and equals(). - */ - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - AclReaders other = (AclReaders) obj; - if (id != other.id) - return false; - return true; - } - - public long getId() - { - return id; - } - - public List getReaders() - { - return unmodifiableList(readers); - } - - public void setReaders(List readers) - { - this.readers = new ArrayList<>(readers); - } - - public List getDenied() - { - return unmodifiableList(denied); - } - - public long getAclChangeSetId() - { - return aclChangeSetId; - } - - public String getTenantDomain() - { - return tenantDomain; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +import static java.util.Collections.unmodifiableList; + +import java.util.ArrayList; +import java.util.List; + +/** + * SOLR-side representation of ACL Readers information. + * + * @since 4.0 + */ +public class AclReaders +{ + private final long id; + + private List readers; + + private final List denied; + + private final long aclChangeSetId; + + private final String tenantDomain; + + public AclReaders(long id, List readers, List denied, long aclChangeSetId, String tenantDomain) + { + this.id = id; + this.readers = (readers == null ? null : new ArrayList<>(readers)); + this.denied = (denied == null ? null : new ArrayList<>(denied)); + this.aclChangeSetId = aclChangeSetId; + this.tenantDomain = tenantDomain; + } + + @Override + public String toString() + { + return "AclReaders [id=" + id + ", readers=" + readers + ", denied=" + denied + ", tenantDomain=" + tenantDomain + "]"; + } + + /** + * ID should be enough for hashCode() and equals(). + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + /** + * ID should be enough for hashCode() and equals(). + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AclReaders other = (AclReaders) obj; + if (id != other.id) + return false; + return true; + } + + public long getId() + { + return id; + } + + public List getReaders() + { + return unmodifiableList(readers); + } + + public void setReaders(List readers) + { + this.readers = new ArrayList<>(readers); + } + + public List getDenied() + { + return unmodifiableList(denied); + } + + public long getAclChangeSetId() + { + return aclChangeSetId; + } + + public String getTenantDomain() + { + return tenantDomain; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModel.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModel.java index d789064e4..38e401978 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModel.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModel.java @@ -1,83 +1,83 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -import org.alfresco.repo.dictionary.M2Model; - -/** - * Represents an alfresco model and checksum. - * - * @since 4.0 - */ -public class AlfrescoModel -{ - private M2Model model; - private Long checksum; - - public AlfrescoModel(M2Model model, Long checksum) - { - this.model = model; - this.checksum = checksum; - } - - public M2Model getModel() - { - return model; - } - - public Long getChecksum() - { - return checksum; - } - - @Override - public boolean equals(Object other) - { - if (this == other) - { - return true; - } - - if(!(other instanceof AlfrescoModel)) - { - return false; - } - - AlfrescoModel model = (AlfrescoModel)other; - return (this.model.getName().equals(model.getModel().getName()) && - checksum.equals(model.getChecksum())); - } - - @Override - public int hashCode() - { - int result = 17; - result = 31 * result + model.hashCode(); - result = 31 * result + Long.valueOf(checksum).hashCode(); - return result; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +import org.alfresco.repo.dictionary.M2Model; + +/** + * Represents an alfresco model and checksum. + * + * @since 4.0 + */ +public class AlfrescoModel +{ + private M2Model model; + private Long checksum; + + public AlfrescoModel(M2Model model, Long checksum) + { + this.model = model; + this.checksum = checksum; + } + + public M2Model getModel() + { + return model; + } + + public Long getChecksum() + { + return checksum; + } + + @Override + public boolean equals(Object other) + { + if (this == other) + { + return true; + } + + if(!(other instanceof AlfrescoModel)) + { + return false; + } + + AlfrescoModel model = (AlfrescoModel)other; + return (this.model.getName().equals(model.getModel().getName()) && + checksum.equals(model.getChecksum())); + } + + @Override + public int hashCode() + { + int result = 17; + result = 31 * result + model.hashCode(); + result = 31 * result + Long.valueOf(checksum).hashCode(); + return result; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModelDiff.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModelDiff.java index 6bfcd7e18..a13830907 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModelDiff.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModelDiff.java @@ -1,106 +1,106 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -import org.alfresco.service.namespace.QName; - -/** - * Represents a diff between the set of current repository Alfresco models and the set maintained in SOLR. - * The diff can represent a new, changed or removed Alfresco model. For a new model the newChecksum is - * populated; for a changed model both checksums are populated; for a removed model neither checksum is populated. - * - * @since 4.0 - */ -public class AlfrescoModelDiff -{ - public enum TYPE - { - NEW, CHANGED, REMOVED; - } - - private QName modelName; - private TYPE type; - private Long oldChecksum; - private Long newChecksum; - - public AlfrescoModelDiff(QName modelName, TYPE type, Long oldChecksum, Long newChecksum) - { - super(); - this.modelName = modelName; - this.type = type; - this.oldChecksum = oldChecksum; - this.newChecksum = newChecksum; - } - - public QName getModelName() - { - return modelName; - } - - public TYPE getType() - { - return type; - } - - public Long getOldChecksum() - { - return oldChecksum; - } - - public Long getNewChecksum() - { - return newChecksum; - } - - @Override - public boolean equals(Object other) - { - if(this == other) - { - return true; - } - if(!(other instanceof AlfrescoModelDiff)) - { - return false; - } - AlfrescoModelDiff diff = (AlfrescoModelDiff)other; - return(diff.getModelName().equals(getModelName()) && - diff.getType().equals(getType()) && - diff.getOldChecksum().equals(getOldChecksum()) && - diff.getNewChecksum().equals(getNewChecksum())); - } - - @Override - public int hashCode() - { - int result = 17; - result = 31 * result + getModelName().hashCode(); - result = 31 * result + getType().hashCode(); - result = 31 * result + (int)(getOldChecksum().longValue() ^ (getOldChecksum().longValue() >>> 32)); - return result; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +import org.alfresco.service.namespace.QName; + +/** + * Represents a diff between the set of current repository Alfresco models and the set maintained in SOLR. + * The diff can represent a new, changed or removed Alfresco model. For a new model the newChecksum is + * populated; for a changed model both checksums are populated; for a removed model neither checksum is populated. + * + * @since 4.0 + */ +public class AlfrescoModelDiff +{ + public enum TYPE + { + NEW, CHANGED, REMOVED; + } + + private QName modelName; + private TYPE type; + private Long oldChecksum; + private Long newChecksum; + + public AlfrescoModelDiff(QName modelName, TYPE type, Long oldChecksum, Long newChecksum) + { + super(); + this.modelName = modelName; + this.type = type; + this.oldChecksum = oldChecksum; + this.newChecksum = newChecksum; + } + + public QName getModelName() + { + return modelName; + } + + public TYPE getType() + { + return type; + } + + public Long getOldChecksum() + { + return oldChecksum; + } + + public Long getNewChecksum() + { + return newChecksum; + } + + @Override + public boolean equals(Object other) + { + if(this == other) + { + return true; + } + if(!(other instanceof AlfrescoModelDiff)) + { + return false; + } + AlfrescoModelDiff diff = (AlfrescoModelDiff)other; + return(diff.getModelName().equals(getModelName()) && + diff.getType().equals(getType()) && + diff.getOldChecksum().equals(getOldChecksum()) && + diff.getNewChecksum().equals(getNewChecksum())); + } + + @Override + public int hashCode() + { + int result = 17; + result = 31 * result + getModelName().hashCode(); + result = 31 * result + getType().hashCode(); + result = 31 * result + (int)(getOldChecksum().longValue() ^ (getOldChecksum().longValue() >>> 32)); + return result; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/ContentPropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/ContentPropertyValue.java index fa8e1d32f..610ab38ac 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/ContentPropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/ContentPropertyValue.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,62 +24,62 @@ * #L% */ -package org.alfresco.solr.client; - -import java.util.Locale; - -/** - * Represents a content property value, including locale, length, content id, encoding, mime type - * - * @since 4.0 - */ -public class ContentPropertyValue extends PropertyValue -{ - private Locale locale; - private long length; - private String encoding; - private String mimetype; - private Long id; - - public ContentPropertyValue(Locale locale, long length, String encoding, String mimetype, Long id) - { - super(); - this.locale = locale; - this.length = length; - this.encoding = encoding; - this.mimetype = mimetype; - this.id = id; - } - - public String getEncoding() - { - return encoding; - } - - public String getMimetype() - { - return mimetype; - } - - public Locale getLocale() - { - return locale; - } - - public long getLength() - { - return length; - } - - public Long getId() - { - return id; - } - - @Override - public String toString() - { - return "ContentPropertyValue [locale=" + locale + ", length=" + length + ", encoding=" - + encoding + ", mimetype=" + mimetype + ", id="+id+"]"; - } -} +package org.alfresco.solr.client; + +import java.util.Locale; + +/** + * Represents a content property value, including locale, length, content id, encoding, mime type + * + * @since 4.0 + */ +public class ContentPropertyValue extends PropertyValue +{ + private Locale locale; + private long length; + private String encoding; + private String mimetype; + private Long id; + + public ContentPropertyValue(Locale locale, long length, String encoding, String mimetype, Long id) + { + super(); + this.locale = locale; + this.length = length; + this.encoding = encoding; + this.mimetype = mimetype; + this.id = id; + } + + public String getEncoding() + { + return encoding; + } + + public String getMimetype() + { + return mimetype; + } + + public Locale getLocale() + { + return locale; + } + + public long getLength() + { + return length; + } + + public Long getId() + { + return id; + } + + @Override + public String toString() + { + return "ContentPropertyValue [locale=" + locale + ", length=" + length + ", encoding=" + + encoding + ", mimetype=" + mimetype + ", id="+id+"]"; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/GetNodesParameters.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/GetNodesParameters.java index 2e168e4d6..ad109e976 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/GetNodesParameters.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/GetNodesParameters.java @@ -1,173 +1,173 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -import java.util.List; -import java.util.Set; - -import org.alfresco.service.namespace.QName; - -/** - * Query parameters for fetching nodes. - * - * @since 4.0 - */ -public class GetNodesParameters -{ - private List transactionIds; - private Long fromNodeId; - private Long toNodeId; - - private String storeProtocol; - private String storeIdentifier; - - private Set includeNodeTypes; - private Set excludeNodeTypes; - - private Set includeAspects; - private Set excludeAspects; - - private QName shardProperty; - private String coreName; - - public boolean getStoreFilter() - { - return (storeProtocol != null || storeIdentifier != null); - } - - public void setStoreProtocol(String storeProtocol) - { - this.storeProtocol = storeProtocol; - } - - public String getStoreProtocol() - { - return storeProtocol; - } - - public void setStoreIdentifier(String storeIdentifier) - { - this.storeIdentifier = storeIdentifier; - } - - public String getStoreIdentifier() - { - return storeIdentifier; - } - - public void setTransactionIds(List txnIds) - { - this.transactionIds = txnIds; - } - - public List getTransactionIds() - { - return transactionIds; - } - - public Long getFromNodeId() - { - return fromNodeId; - } - - public void setFromNodeId(Long fromNodeId) - { - this.fromNodeId = fromNodeId; - } - - public Long getToNodeId() - { - return toNodeId; - } - - public void setToNodeId(Long toNodeId) - { - this.toNodeId = toNodeId; - } - - public Set getIncludeNodeTypes() - { - return includeNodeTypes; - } - - public Set getExcludeNodeTypes() - { - return excludeNodeTypes; - } - - public Set getIncludeAspects() - { - return includeAspects; - } - - public Set getExcludeAspects() - { - return excludeAspects; - } - - public void setIncludeNodeTypes(Set includeNodeTypes) - { - this.includeNodeTypes = includeNodeTypes; - } - - public void setExcludeNodeTypes(Set excludeNodeTypes) - { - this.excludeNodeTypes = excludeNodeTypes; - } - - public void setIncludeAspects(Set includeAspects) - { - this.includeAspects = includeAspects; - } - - public void setExcludeAspects(Set excludeAspects) - { - this.excludeAspects = excludeAspects; - } - - public QName getShardProperty() - { - return this.shardProperty; - } - - public void setShardProperty(QName shardProperty) - { - this.shardProperty = shardProperty; - } - - public String getCoreName() - { - return this.coreName; - } - - public void setCoreName(String coreName) - { - this.coreName = coreName; - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +import java.util.List; +import java.util.Set; + +import org.alfresco.service.namespace.QName; + +/** + * Query parameters for fetching nodes. + * + * @since 4.0 + */ +public class GetNodesParameters +{ + private List transactionIds; + private Long fromNodeId; + private Long toNodeId; + + private String storeProtocol; + private String storeIdentifier; + + private Set includeNodeTypes; + private Set excludeNodeTypes; + + private Set includeAspects; + private Set excludeAspects; + + private QName shardProperty; + private String coreName; + + public boolean getStoreFilter() + { + return (storeProtocol != null || storeIdentifier != null); + } + + public void setStoreProtocol(String storeProtocol) + { + this.storeProtocol = storeProtocol; + } + + public String getStoreProtocol() + { + return storeProtocol; + } + + public void setStoreIdentifier(String storeIdentifier) + { + this.storeIdentifier = storeIdentifier; + } + + public String getStoreIdentifier() + { + return storeIdentifier; + } + + public void setTransactionIds(List txnIds) + { + this.transactionIds = txnIds; + } + + public List getTransactionIds() + { + return transactionIds; + } + + public Long getFromNodeId() + { + return fromNodeId; + } + + public void setFromNodeId(Long fromNodeId) + { + this.fromNodeId = fromNodeId; + } + + public Long getToNodeId() + { + return toNodeId; + } + + public void setToNodeId(Long toNodeId) + { + this.toNodeId = toNodeId; + } + + public Set getIncludeNodeTypes() + { + return includeNodeTypes; + } + + public Set getExcludeNodeTypes() + { + return excludeNodeTypes; + } + + public Set getIncludeAspects() + { + return includeAspects; + } + + public Set getExcludeAspects() + { + return excludeAspects; + } + + public void setIncludeNodeTypes(Set includeNodeTypes) + { + this.includeNodeTypes = includeNodeTypes; + } + + public void setExcludeNodeTypes(Set excludeNodeTypes) + { + this.excludeNodeTypes = excludeNodeTypes; + } + + public void setIncludeAspects(Set includeAspects) + { + this.includeAspects = includeAspects; + } + + public void setExcludeAspects(Set excludeAspects) + { + this.excludeAspects = excludeAspects; + } + + public QName getShardProperty() + { + return this.shardProperty; + } + + public void setShardProperty(QName shardProperty) + { + this.shardProperty = shardProperty; + } + + public String getCoreName() + { + return this.coreName; + } + + public void setCoreName(String coreName) + { + this.coreName = coreName; + } + + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/LookAheadBufferedReader.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/LookAheadBufferedReader.java index ab24a1119..80adc9f57 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/LookAheadBufferedReader.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/LookAheadBufferedReader.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -250,4 +250,4 @@ public class LookAheadBufferedReader extends BufferedReader { return bufferingMode instanceof NoOp; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MLTextPropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MLTextPropertyValue.java index 1403e3818..9b419e27f 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MLTextPropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MLTextPropertyValue.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,57 +24,57 @@ * #L% */ -package org.alfresco.solr.client; - -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -/** - * Represents a multi-lingual property value, comprising a map from locale to string value - * - * @since 4.0 - */ -public class MLTextPropertyValue extends PropertyValue -{ - private Map values; - - public MLTextPropertyValue() - { - super(); - values = new HashMap(10); - } - - public MLTextPropertyValue(Map values) - { - super(); - this.values = values; - } - - public void addValue(Locale locale, String value) - { - values.put(locale, value); - } - - public Map getValues() - { - return values; - } - - public Set getLocales() - { - return values.keySet(); - } - - public String getValue(Locale locale) - { - return values.get(locale); - } - - @Override - public String toString() - { - return "MLTextPropertyValue [values=" + values + "]"; - } -} +package org.alfresco.solr.client; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + * Represents a multi-lingual property value, comprising a map from locale to string value + * + * @since 4.0 + */ +public class MLTextPropertyValue extends PropertyValue +{ + private Map values; + + public MLTextPropertyValue() + { + super(); + values = new HashMap(10); + } + + public MLTextPropertyValue(Map values) + { + super(); + this.values = values; + } + + public void addValue(Locale locale, String value) + { + values.put(locale, value); + } + + public Map getValues() + { + return values; + } + + public Set getLocales() + { + return values.keySet(); + } + + public String getValue(Locale locale) + { + return values.get(locale); + } + + @Override + public String toString() + { + return "MLTextPropertyValue [values=" + values + "]"; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MultiPropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MultiPropertyValue.java index 98580073f..023059221 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MultiPropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MultiPropertyValue.java @@ -1,66 +1,66 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents a multi property value, comprising a list of other property values - * - * @since 4.0 - */ -public class MultiPropertyValue extends PropertyValue -{ - private final List values; - - public MultiPropertyValue() - { - this.values = new ArrayList<>(); - } - - public MultiPropertyValue(List values) - { - this.values = values; - } - - public void addValue(PropertyValue value) - { - values.add(value); - } - - public List getValues() - { - return values; - } - - @Override - public String toString() - { - return "MultiPropertyValue [values=" + values + "]"; - } -} \ No newline at end of file +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a multi property value, comprising a list of other property values + * + * @since 4.0 + */ +public class MultiPropertyValue extends PropertyValue +{ + private final List values; + + public MultiPropertyValue() + { + this.values = new ArrayList<>(); + } + + public MultiPropertyValue(List values) + { + this.values = values; + } + + public void addValue(PropertyValue value) + { + values.add(value); + } + + public List getValues() + { + return values; + } + + @Override + public String toString() + { + return "MultiPropertyValue [values=" + values + "]"; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Node.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Node.java index 15dd43b47..0bd3ecf4a 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Node.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Node.java @@ -1,144 +1,144 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -public class Node -{ - public static enum SolrApiNodeStatus - { - UPDATED, DELETED, UNKNOWN, NON_SHARD_DELETED, NON_SHARD_UPDATED; - }; - - private long id; - private String nodeRef; - private long txnId; - private SolrApiNodeStatus status; - private String tenant; - private long aclId; - private String shardPropertyValue; - private Integer explicitShardId; - - public long getId() - { - return id; - } - public void setId(long id) - { - this.id = id; - } - public String getNodeRef() - { - return nodeRef; - } - public void setNodeRef(String nodeRef) - { - this.nodeRef = nodeRef; - } - public long getTxnId() - { - return txnId; - } - public void setTxnId(long txnId) - { - this.txnId = txnId; - } - public SolrApiNodeStatus getStatus() - { - return status; - } - public void setStatus(SolrApiNodeStatus status) - { - this.status = status; - } - /** - * @return the tenant - */ - public String getTenant() - { - return tenant; - } - /** - * @param tenant the tenant to set - */ - public void setTenant(String tenant) - { - this.tenant = tenant; - } - /** - * @return the aclId - */ - public long getAclId() - { - return aclId; - } - /** - * @param aclId the aclId to set - */ - public void setAclId(long aclId) - { - this.aclId = aclId; - } - - - /** - * The property value to use for sharding - as requested - * - * @return null - if the node does not have the property, the standard "String" value of the property if it is present on the node. - * For dates and datetime properties this will be the ISO formatted datetime. - */ - public String getShardPropertyValue() - { - return this.shardPropertyValue; - } - - public void setShardPropertyValue(String shardPropertyValue) - { - this.shardPropertyValue = shardPropertyValue; - } - - public Integer getExplicitShardId() - { - return this.explicitShardId; - } - - public void setExplicitShardId(Integer explicitShardId) - { - this.explicitShardId = explicitShardId; - } - - @Override - public String toString() - { - return "Node [id=" + this.id + ", nodeRef=" + this.nodeRef + ", txnId=" + this.txnId - + ", status=" + this.status + ", tenant=" + this.tenant + ", aclId=" - + this.aclId + ", shardPropertyValue=" + this.shardPropertyValue - + ", explicitShardId=" + this.explicitShardId + "]"; - } - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +public class Node +{ + public static enum SolrApiNodeStatus + { + UPDATED, DELETED, UNKNOWN, NON_SHARD_DELETED, NON_SHARD_UPDATED; + }; + + private long id; + private String nodeRef; + private long txnId; + private SolrApiNodeStatus status; + private String tenant; + private long aclId; + private String shardPropertyValue; + private Integer explicitShardId; + + public long getId() + { + return id; + } + public void setId(long id) + { + this.id = id; + } + public String getNodeRef() + { + return nodeRef; + } + public void setNodeRef(String nodeRef) + { + this.nodeRef = nodeRef; + } + public long getTxnId() + { + return txnId; + } + public void setTxnId(long txnId) + { + this.txnId = txnId; + } + public SolrApiNodeStatus getStatus() + { + return status; + } + public void setStatus(SolrApiNodeStatus status) + { + this.status = status; + } + /** + * @return the tenant + */ + public String getTenant() + { + return tenant; + } + /** + * @param tenant the tenant to set + */ + public void setTenant(String tenant) + { + this.tenant = tenant; + } + /** + * @return the aclId + */ + public long getAclId() + { + return aclId; + } + /** + * @param aclId the aclId to set + */ + public void setAclId(long aclId) + { + this.aclId = aclId; + } + + + /** + * The property value to use for sharding - as requested + * + * @return null - if the node does not have the property, the standard "String" value of the property if it is present on the node. + * For dates and datetime properties this will be the ISO formatted datetime. + */ + public String getShardPropertyValue() + { + return this.shardPropertyValue; + } + + public void setShardPropertyValue(String shardPropertyValue) + { + this.shardPropertyValue = shardPropertyValue; + } + + public Integer getExplicitShardId() + { + return this.explicitShardId; + } + + public void setExplicitShardId(Integer explicitShardId) + { + this.explicitShardId = explicitShardId; + } + + @Override + public String toString() + { + return "Node [id=" + this.id + ", nodeRef=" + this.nodeRef + ", txnId=" + this.txnId + + ", status=" + this.status + ", tenant=" + this.tenant + ", aclId=" + + this.aclId + ", shardPropertyValue=" + this.shardPropertyValue + + ", explicitShardId=" + this.explicitShardId + "]"; + } + + + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaData.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaData.java index 44578a649..0078d13ae 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaData.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaData.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,101 +24,101 @@ * #L% */ -package org.alfresco.solr.client; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.Pair; - -/** - * SOLR-side representation of node metadata information. - * - * @since 4.0 - */ -public class NodeMetaData -{ - private long id; - private NodeRef nodeRef; - private QName type; - private long aclId; - private Map properties; - private Set aspects; - private List> paths; - private List> namePaths; - private long parentAssocsCrc; - private List parentAssocs; - private List childAssocs; - private List childIds; - private String owner; - private long txnId; - private Set ancestors; +package org.alfresco.solr.client; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +/** + * SOLR-side representation of node metadata information. + * + * @since 4.0 + */ +public class NodeMetaData +{ + private long id; + private NodeRef nodeRef; + private QName type; + private long aclId; + private Map properties; + private Set aspects; + private List> paths; + private List> namePaths; + private long parentAssocsCrc; + private List parentAssocs; + private List childAssocs; + private List childIds; + private String owner; + private long txnId; + private Set ancestors; private String tenantDomain; - private List ancestorPaths; - - public long getId() - { - return id; - } - public void setId(long id) - { - this.id = id; - } - public NodeRef getNodeRef() - { - return nodeRef; - } - public void setNodeRef(NodeRef nodeRef) - { - this.nodeRef = nodeRef; - } - public QName getType() - { - return type; - } - public void setType(QName type) - { - this.type = type; - } - public long getAclId() - { - return aclId; - } - public void setAclId(long aclId) - { - this.aclId = aclId; - } - - /** - * A property value is either null or a subclass of PropertyValue - */ - public Map getProperties() - { - return properties; - } - public void setProperties(Map properties) - { - this.properties = properties; - } - public Set getAspects() - { - return aspects; - } - public void setAspects(Set aspects) - { - this.aspects = aspects; - } - public List> getPaths() - { - return paths; - } - public void setPaths(List> paths) - { - this.paths = paths; + private List ancestorPaths; + + public long getId() + { + return id; + } + public void setId(long id) + { + this.id = id; + } + public NodeRef getNodeRef() + { + return nodeRef; + } + public void setNodeRef(NodeRef nodeRef) + { + this.nodeRef = nodeRef; + } + public QName getType() + { + return type; + } + public void setType(QName type) + { + this.type = type; + } + public long getAclId() + { + return aclId; + } + public void setAclId(long aclId) + { + this.aclId = aclId; + } + + /** + * A property value is either null or a subclass of PropertyValue + */ + public Map getProperties() + { + return properties; + } + public void setProperties(Map properties) + { + this.properties = properties; + } + public Set getAspects() + { + return aspects; + } + public void setAspects(Set aspects) + { + this.aspects = aspects; + } + public List> getPaths() + { + return paths; + } + public void setPaths(List> paths) + { + this.paths = paths; } public List getAncestorPaths() @@ -129,90 +129,90 @@ public class NodeMetaData public void setAncestorPaths(List ancestorPaths) { this.ancestorPaths = ancestorPaths; - } - - public List> getNamePaths() - { - return namePaths; - } - - public void setNamePaths(List> namePaths) - { - this.namePaths = namePaths; - } - public void setParentAssocsCrc(long parentAssocsCrc) - { - this.parentAssocsCrc = parentAssocsCrc; - } - public long getParentAssocsCrc() - { - return parentAssocsCrc; - } - public void setParentAssocs(List parentAssocs) - { - this.parentAssocs = parentAssocs; - } - public List getParentAssocs() - { - return parentAssocs; - } - public void setChildAssocs(List childAssocs) - { - this.childAssocs = childAssocs; - } - public List getChildAssocs() - { - return childAssocs; - } - public List getChildIds() - { - return childIds; - } - public void setChildIds(List childIds) - { - this.childIds = childIds; - } - - public String getOwner() - { - return owner; - } - public void setOwner(String owner) - { - this.owner = owner; - } - - public long getTxnId() - { - return txnId; - } - public void setTxnId(long txnId) - { - this.txnId = txnId; - } - public Set getAncestors() - { - return ancestors; - } - public void setAncestors(Set ancestors) - { - this.ancestors = ancestors; - } - public String getTenantDomain() - { - return tenantDomain; - } - public void setTenantDomain(String tenantDomain) - { - this.tenantDomain = tenantDomain; - } - - @Override - public String toString() - { - return "NodeMetaData [id=" - + id + ", nodeRef=" + nodeRef + ", type=" + type + ", aclId=" + aclId + ", properties=" + properties + ", aspects=" + aspects + ", paths=" + paths - + ", parentAssocsCrc=" + parentAssocsCrc + ", parentAssocs=" + parentAssocs + ", childAssocs=" + childAssocs + ", childIds=" + childIds + ", owner=" + owner - + ", txnId=" + txnId + ", ancestors=" + ancestors + ", tenantDomain=" + tenantDomain + "]"; - } -} + } + + public List> getNamePaths() + { + return namePaths; + } + + public void setNamePaths(List> namePaths) + { + this.namePaths = namePaths; + } + public void setParentAssocsCrc(long parentAssocsCrc) + { + this.parentAssocsCrc = parentAssocsCrc; + } + public long getParentAssocsCrc() + { + return parentAssocsCrc; + } + public void setParentAssocs(List parentAssocs) + { + this.parentAssocs = parentAssocs; + } + public List getParentAssocs() + { + return parentAssocs; + } + public void setChildAssocs(List childAssocs) + { + this.childAssocs = childAssocs; + } + public List getChildAssocs() + { + return childAssocs; + } + public List getChildIds() + { + return childIds; + } + public void setChildIds(List childIds) + { + this.childIds = childIds; + } + + public String getOwner() + { + return owner; + } + public void setOwner(String owner) + { + this.owner = owner; + } + + public long getTxnId() + { + return txnId; + } + public void setTxnId(long txnId) + { + this.txnId = txnId; + } + public Set getAncestors() + { + return ancestors; + } + public void setAncestors(Set ancestors) + { + this.ancestors = ancestors; + } + public String getTenantDomain() + { + return tenantDomain; + } + public void setTenantDomain(String tenantDomain) + { + this.tenantDomain = tenantDomain; + } + + @Override + public String toString() + { + return "NodeMetaData [id=" + + id + ", nodeRef=" + nodeRef + ", type=" + type + ", aclId=" + aclId + ", properties=" + properties + ", aspects=" + aspects + ", paths=" + paths + + ", parentAssocsCrc=" + parentAssocsCrc + ", parentAssocs=" + parentAssocs + ", childAssocs=" + childAssocs + ", childIds=" + childIds + ", owner=" + owner + + ", txnId=" + txnId + ", ancestors=" + ancestors + ", tenantDomain=" + tenantDomain + "]"; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaDataParameters.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaDataParameters.java index b9938ea13..1a1020ed6 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaDataParameters.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaDataParameters.java @@ -1,242 +1,242 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -import java.util.List; -import java.util.OptionalInt; - -/** - * Stores node meta data query parameters for use in SOLR remote api calls - * - * @since 4.0 - */ -public class NodeMetaDataParameters -{ - private List transactionIds; - private Long fromTxnId; - private Long toTxnId; - - // default is 'all' results - private OptionalInt maxResults = OptionalInt.empty(); - - private Long fromNodeId; - private Long toNodeId; - private List nodeIds; - - private boolean includeAclId = true; - private boolean includeAspects = true; - private boolean includeNodeRef = true; - private boolean includeOwner = true; - private boolean includeProperties = true; - private boolean includePaths = true; - private boolean includeType = true; - private boolean includeChildAssociations = true; - private boolean includeParentAssociations = true; - private boolean includeChildIds = true; - private boolean includeTxnId = true; - - public boolean isIncludeAclId() - { - return includeAclId; - } - - public void setIncludeAclId(boolean includeAclId) - { - this.includeAclId = includeAclId; - } - - public boolean isIncludeAspects() - { - return includeAspects; - } - - public void setIncludeAspects(boolean includeAspects) - { - this.includeAspects = includeAspects; - } - - public boolean isIncludeNodeRef() - { - return includeNodeRef; - } - - public void setIncludeNodeRef(boolean includeNodeRef) - { - this.includeNodeRef = includeNodeRef; - } - - public boolean isIncludeOwner() - { - return includeOwner; - } - - public void setIncludeOwner(boolean includeOwner) - { - this.includeOwner = includeOwner; - } - - public boolean isIncludeProperties() - { - return includeProperties; - } - - public void setIncludeProperties(boolean includeProperties) - { - this.includeProperties = includeProperties; - } - - public boolean isIncludePaths() - { - return includePaths; - } - - public void setIncludePaths(boolean includePaths) - { - this.includePaths = includePaths; - } - - public boolean isIncludeType() - { - return includeType; - } - - public void setIncludeType(boolean includeType) - { - this.includeType = includeType; - } - - public boolean isIncludeChildAssociations() - { - return includeChildAssociations; - } - - public void setIncludeChildAssociations(boolean includeChildAssociations) - { - this.includeChildAssociations = includeChildAssociations; - } - - public boolean isIncludeParentAssociations() - { - return includeParentAssociations; - } - - public void setIncludeParentAssociations(boolean includeParentAssociations) - { - this.includeParentAssociations = includeParentAssociations; - } - - public OptionalInt getMaxResults() - { - return maxResults; - } - - public void setMaxResults(int maxResults) - { - this.maxResults = OptionalInt.of(maxResults); - } - - public List getNodeIds() - { - return nodeIds; - } - - public void setNodeIds(List nodeIds) - { - this.nodeIds = nodeIds; - } - - public void setTransactionIds(List txnIds) - { - this.transactionIds = txnIds; - } - - public List getTransactionIds() - { - return transactionIds; - } - - public Long getFromTxnId() - { - return fromTxnId; - } - - public void setFromTxnId(Long fromTxnId) - { - this.fromTxnId = fromTxnId; - } - - public Long getToTxnId() - { - return toTxnId; - } - - public void setToTxnId(Long toTxnId) - { - this.toTxnId = toTxnId; - } - - public Long getFromNodeId() - { - return fromNodeId; - } - - public void setFromNodeId(Long fromNodeId) - { - this.fromNodeId = fromNodeId; - } - - public Long getToNodeId() - { - return toNodeId; - } - - public void setToNodeId(Long toNodeId) - { - this.toNodeId = toNodeId; - } - - public boolean isIncludeChildIds() - { - return includeChildIds; - } - - public void setIncludeChildIds(boolean includeChildIds) - { - this.includeChildIds = includeChildIds; - } - - public boolean isIncludeTxnId() - { - return includeTxnId; - } - - public void setIncludeTxnId(boolean includeTxnId) - { - this.includeTxnId = includeTxnId; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +import java.util.List; +import java.util.OptionalInt; + +/** + * Stores node meta data query parameters for use in SOLR remote api calls + * + * @since 4.0 + */ +public class NodeMetaDataParameters +{ + private List transactionIds; + private Long fromTxnId; + private Long toTxnId; + + // default is 'all' results + private OptionalInt maxResults = OptionalInt.empty(); + + private Long fromNodeId; + private Long toNodeId; + private List nodeIds; + + private boolean includeAclId = true; + private boolean includeAspects = true; + private boolean includeNodeRef = true; + private boolean includeOwner = true; + private boolean includeProperties = true; + private boolean includePaths = true; + private boolean includeType = true; + private boolean includeChildAssociations = true; + private boolean includeParentAssociations = true; + private boolean includeChildIds = true; + private boolean includeTxnId = true; + + public boolean isIncludeAclId() + { + return includeAclId; + } + + public void setIncludeAclId(boolean includeAclId) + { + this.includeAclId = includeAclId; + } + + public boolean isIncludeAspects() + { + return includeAspects; + } + + public void setIncludeAspects(boolean includeAspects) + { + this.includeAspects = includeAspects; + } + + public boolean isIncludeNodeRef() + { + return includeNodeRef; + } + + public void setIncludeNodeRef(boolean includeNodeRef) + { + this.includeNodeRef = includeNodeRef; + } + + public boolean isIncludeOwner() + { + return includeOwner; + } + + public void setIncludeOwner(boolean includeOwner) + { + this.includeOwner = includeOwner; + } + + public boolean isIncludeProperties() + { + return includeProperties; + } + + public void setIncludeProperties(boolean includeProperties) + { + this.includeProperties = includeProperties; + } + + public boolean isIncludePaths() + { + return includePaths; + } + + public void setIncludePaths(boolean includePaths) + { + this.includePaths = includePaths; + } + + public boolean isIncludeType() + { + return includeType; + } + + public void setIncludeType(boolean includeType) + { + this.includeType = includeType; + } + + public boolean isIncludeChildAssociations() + { + return includeChildAssociations; + } + + public void setIncludeChildAssociations(boolean includeChildAssociations) + { + this.includeChildAssociations = includeChildAssociations; + } + + public boolean isIncludeParentAssociations() + { + return includeParentAssociations; + } + + public void setIncludeParentAssociations(boolean includeParentAssociations) + { + this.includeParentAssociations = includeParentAssociations; + } + + public OptionalInt getMaxResults() + { + return maxResults; + } + + public void setMaxResults(int maxResults) + { + this.maxResults = OptionalInt.of(maxResults); + } + + public List getNodeIds() + { + return nodeIds; + } + + public void setNodeIds(List nodeIds) + { + this.nodeIds = nodeIds; + } + + public void setTransactionIds(List txnIds) + { + this.transactionIds = txnIds; + } + + public List getTransactionIds() + { + return transactionIds; + } + + public Long getFromTxnId() + { + return fromTxnId; + } + + public void setFromTxnId(Long fromTxnId) + { + this.fromTxnId = fromTxnId; + } + + public Long getToTxnId() + { + return toTxnId; + } + + public void setToTxnId(Long toTxnId) + { + this.toTxnId = toTxnId; + } + + public Long getFromNodeId() + { + return fromNodeId; + } + + public void setFromNodeId(Long fromNodeId) + { + this.fromNodeId = fromNodeId; + } + + public Long getToNodeId() + { + return toNodeId; + } + + public void setToNodeId(Long toNodeId) + { + this.toNodeId = toNodeId; + } + + public boolean isIncludeChildIds() + { + return includeChildIds; + } + + public void setIncludeChildIds(boolean includeChildIds) + { + this.includeChildIds = includeChildIds; + } + + public boolean isIncludeTxnId() + { + return includeTxnId; + } + + public void setIncludeTxnId(boolean includeTxnId) + { + this.includeTxnId = includeTxnId; + } + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/PropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/PropertyValue.java index b0d788a5a..5edb306c8 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/PropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/PropertyValue.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,14 +24,14 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * Base class for property values returned to SOLR for indexing. - * - * @since 4.0 - */ -public abstract class PropertyValue -{ - -} +package org.alfresco.solr.client; + +/** + * Base class for property values returned to SOLR for indexing. + * + * @since 4.0 + */ +public abstract class PropertyValue +{ + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClient.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClient.java index 233905369..ac4f83801 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClient.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClient.java @@ -1,1637 +1,1637 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.httpclient.AlfrescoHttpClient; -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.httpclient.GetRequest; -import org.alfresco.httpclient.PostRequest; -import org.alfresco.httpclient.Request; -import org.alfresco.httpclient.Response; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.repo.index.shard.ShardState; -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.AssociationRef; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.MLText; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.Path; -import org.alfresco.service.cmr.repository.Path.AttributeElement; -import org.alfresco.service.cmr.repository.Path.ChildAssocElement; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; -import org.alfresco.service.cmr.repository.datatype.TypeConversionException; -import org.alfresco.service.cmr.repository.datatype.TypeConverter; -import org.alfresco.service.cmr.repository.datatype.TypeConverter.Converter; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.ISO8601DateFormat; -import org.alfresco.util.Pair; -import org.apache.commons.codec.EncoderException; -import org.apache.commons.codec.net.URLCodec; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.util.DateUtil; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.extensions.surf.util.URLEncoder; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import static java.util.Optional.ofNullable; - -// TODO error handling, including dealing with a repository that is not responsive (ConnectException in sendRemoteRequest) -// TODO get text content transform status handling -/** - * Http client to handle SOLR-Alfresco remote calls. - * - * @since 4.0 - */ -public class SOLRAPIClient -{ - protected final static Logger LOGGER = LoggerFactory.getLogger(SOLRAPIClient.class); - private static final String GET_ACL_CHANGESETS_URL = "api/solr/aclchangesets"; - private static final String GET_ACLS = "api/solr/acls"; - private static final String GET_ACLS_READERS = "api/solr/aclsReaders"; - private static final String GET_TRANSACTIONS_URL = "api/solr/transactions"; - private static final String GET_METADATA_URL = "api/solr/metadata"; - private static final String GET_NODES_URL = "api/solr/nodes"; - private static final String GET_CONTENT = "api/solr/textContent"; - private static final String GET_MODEL = "api/solr/model"; - private static final String GET_MODELS_DIFF = "api/solr/modelsdiff"; - private static final String GET_NEXT_TX_COMMIT_TIME = "api/solr/nextTransaction"; - private static final String GET_TX_INTERVAL_COMMIT_TIME = "api/solr/transactionInterval"; - - private static final String CHECKSUM_HEADER = "XAlfresco-modelChecksum"; - - private AlfrescoHttpClient repositoryHttpClient; - private SOLRDeserializer deserializer; - private DictionaryService dictionaryService; - private JsonFactory jsonFactory; - private NamespaceDAO namespaceDAO; - - /** - * This option enables ("Accept-Encoding": "gzip") header for compression - * in GET_CONTENT requests. Additional configuration is required in - * Alfresco Repository Tomcat Connector or HTTP Web Proxy to deal - * with compressed requests. - */ - private boolean compression; - - public SOLRAPIClient(AlfrescoHttpClient repositoryHttpClient, - DictionaryService dictionaryService, - NamespaceDAO namespaceDAO) - { - this(repositoryHttpClient, dictionaryService, namespaceDAO, false); - } - - public SOLRAPIClient(AlfrescoHttpClient repositoryHttpClient, - DictionaryService dictionaryService, - NamespaceDAO namespaceDAO, - boolean compression) - { - this.repositoryHttpClient = repositoryHttpClient; - this.dictionaryService = dictionaryService; - this.namespaceDAO = namespaceDAO; - this.deserializer = new SOLRDeserializer(namespaceDAO); - this.jsonFactory = new JsonFactory(); - this.compression = compression; - } - - /** - * Get the ACL ChangeSets - * - * @param fromCommitTime the lowest commit time (optional) - * @param minAclChangeSetId the lowest ChangeSet ID (optional) - * @param maxResults the maximum number of results (a reasonable value only) - * @return the ACL ChangeSets in order of commit time and ID - */ - public AclChangeSets getAclChangeSets(Long fromCommitTime, Long minAclChangeSetId, Long toCommitTime, Long maxAclChangeSetId, int maxResults) - throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_ACL_CHANGESETS_URL); - StringBuilder args = new StringBuilder(); - if (fromCommitTime != null) - { - args.append("?").append("fromTime").append("=").append(fromCommitTime); - } - if (minAclChangeSetId != null) - { - args.append(args.length() == 0 ? "?" : "&").append("fromId").append("=").append(minAclChangeSetId); - } - if (toCommitTime != null) - { - args.append(args.length() == 0 ? "?" : "&").append("toTime").append("=").append(toCommitTime); - } - if (maxAclChangeSetId != null) - { - args.append(args.length() == 0 ? "?" : "&").append("toId").append("=").append(maxAclChangeSetId); - } - if (maxResults != 0 && maxResults != Integer.MAX_VALUE) - { - args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); - } - url.append(args); - - GetRequest req = new GetRequest(url.toString()); - JSONObject json = callRepository(GET_ACL_CHANGESETS_URL, req); - - JSONArray aclChangeSetsJSON = json.getJSONArray("aclChangeSets"); - List aclChangeSets = new ArrayList(aclChangeSetsJSON.length()); - for (int i = 0; i < aclChangeSetsJSON.length(); i++) - { - JSONObject aclChangeSetJSON = aclChangeSetsJSON.getJSONObject(i); - long aclChangeSetId = aclChangeSetJSON.getLong("id"); - long commitTimeMs = aclChangeSetJSON.getLong("commitTimeMs"); - int aclCount = aclChangeSetJSON.getInt("aclCount"); - AclChangeSet aclChangeSet = new AclChangeSet(aclChangeSetId, commitTimeMs, aclCount); - aclChangeSets.add(aclChangeSet); - } - - Long maxChangeSetCommitTime = null; - if(json.has("maxChangeSetCommitTime")) - { - maxChangeSetCommitTime = json.getLong("maxChangeSetCommitTime"); - } - - Long maxChangeSetIdOnServer = null; - if(json.has("maxChangeSetId")) - { - maxChangeSetIdOnServer = json.getLong("maxChangeSetId"); - } - - // Done - return new AclChangeSets(aclChangeSets, maxChangeSetCommitTime, maxChangeSetIdOnServer); - } - - /** - * Get the ACLs associated with a given list of ACL ChangeSets. The ACLs may be truncated for - * the last ACL ChangeSet in the return values - the ACL count from the - * {@link #getAclChangeSets(Long, Long, Long, Long, int) ACL ChangeSets}. - * - * @param aclChangeSets the ACL ChangeSets to include - * @param minAclId the lowest ACL ID (may be null) - * @param maxResults the maximum number of results to retrieve - * @return the ACLs (includes ChangeSet ID) - */ - public List getAcls(List aclChangeSets, Long minAclId, int maxResults) throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_ACLS); - StringBuilder args = new StringBuilder(); - if (minAclId != null) - { - args.append("?").append("fromId").append("=").append(minAclId); - } - - if(maxResults >= 0) - { - args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); - } - - url.append(args); - - JSONObject jsonReq = new JSONObject(); - JSONArray aclChangeSetIdsJSON = new JSONArray(); - List aclChangeSetIds = new ArrayList(); - for (AclChangeSet aclChangeSet : aclChangeSets) - { - Long aclChangeSetId = aclChangeSet.getId(); - aclChangeSetIdsJSON.put(aclChangeSetId); - aclChangeSetIds.add(aclChangeSetId); - } - jsonReq.put("aclChangeSetIds", aclChangeSetIdsJSON); - - PostRequest req = new PostRequest(url.toString(), jsonReq.toString(), "application/json"); - JSONObject json = callRepository(GET_ACL_CHANGESETS_URL, req); - - JSONArray aclsJSON = json.getJSONArray("acls"); - List acls = new ArrayList(aclsJSON.length()); - for (int i = 0; i < aclsJSON.length(); i++) - { - JSONObject aclJSON = aclsJSON.getJSONObject(i); - long aclChangeSetId = aclJSON.getLong("aclChangeSetId"); - long aclId = aclJSON.getLong("id"); - Acl acl = new Acl(aclChangeSetId, aclId); - acls.add(acl); - } - // Done - return acls; - } - - /** - * Get the ACL readers for a given list of ACLs - * - * @param acls the ACLs - * @return the readers for the ACLs - */ - public List getAclReaders(List acls) throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_ACLS_READERS); - - JSONObject jsonReq = new JSONObject(); - JSONArray aclIdsJSON = new JSONArray(); - List aclIds = new ArrayList(); - for (Acl acl : acls) - { - Long aclId = acl.getId(); - aclIdsJSON.put(aclId); - aclIds.add(aclId); - } - jsonReq.put("aclIds", aclIdsJSON); - - PostRequest req = new PostRequest(url.toString(), jsonReq.toString(), "application/json"); - JSONObject json = callRepository(GET_ACLS_READERS, req); - - JSONArray aclsReadersJSON = json.getJSONArray("aclsReaders"); - List aclsReaders = new ArrayList(aclsReadersJSON.length()); - for (int i = 0; i < aclsReadersJSON.length(); i++) - { - JSONObject aclReadersJSON = aclsReadersJSON.getJSONObject(i); - long aclId = aclReadersJSON.getLong("aclId"); - JSONArray readersJSON = aclReadersJSON.getJSONArray("readers"); - List readers = authorityListFromJSON(readersJSON); - JSONArray deniedJSON = aclReadersJSON.getJSONArray("denied"); - List denied = authorityListFromJSON(deniedJSON); - long aclChangeSetId = aclReadersJSON.getLong("aclChangeSetId"); - - String tenantDomain = aclReadersJSON.getString("tenantDomain"); - if (tenantDomain == null) - { - tenantDomain = TenantService.DEFAULT_DOMAIN; - } - - AclReaders aclReaders = new AclReaders(aclId, readers, denied, aclChangeSetId, tenantDomain); - aclsReaders.add(aclReaders); - } - // Done - return aclsReaders; - } - - /** - * Convert a JSON array of authorities to a simple Java List<String> - * - * @param jsonArray JSONArray - * @return List<String> - * @throws JSONException - */ - private List authorityListFromJSON(JSONArray jsonArray) throws JSONException - { - List authorities = new ArrayList(jsonArray.length()); - for (int j = 0; j < jsonArray.length(); j++) - { - String authority = jsonArray.getString(j); - authorities.add(authority); - } - return authorities; - } - - public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, Long maxTxnId, int maxResults) throws AuthenticationException, IOException, JSONException - { - try - { - return getTransactions(fromCommitTime, minTxnId, toCommitTime, maxTxnId, maxResults, null); - } - catch (EncoderException e) - { - // Can not happen .... - throw new IOException(e); - } - } - - public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, - Long maxTxnId, int maxResults, ShardState shardState) - throws AuthenticationException, IOException, JSONException, EncoderException - { - URLCodec encoder = new URLCodec(); - - StringBuilder url = new StringBuilder(GET_TRANSACTIONS_URL); - StringBuilder args = new StringBuilder(); - if (fromCommitTime != null) - { - args.append("?").append("fromCommitTime").append("=").append(fromCommitTime); - } - if (minTxnId != null) - { - args.append(args.length() == 0 ? "?" : "&").append("minTxnId").append("=").append(minTxnId); - } - if (toCommitTime != null) - { - args.append(args.length() == 0 ? "?" : "&").append("toCommitTime").append("=").append(toCommitTime); - } - if (maxTxnId != null) - { - args.append(args.length() == 0 ? "?" : "&").append("maxTxnId").append("=").append(maxTxnId); - } - if (maxResults != 0 && maxResults != Integer.MAX_VALUE) - { - args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); - } - if(shardState != null) - { - LOGGER.debug("### Shard state exists ###"); - args.append(args.length() == 0 ? "?" : "&"); - args.append(encoder.encode("baseUrl")).append("=").append(encoder.encode(shardState.getShardInstance().getBaseUrl())); - args.append("&").append(encoder.encode("hostName")).append("=").append(encoder.encode(shardState.getShardInstance().getHostName())); - args.append("&").append(encoder.encode("template")).append("=").append(encoder.encode(shardState.getShardInstance().getShard().getFloc().getTemplate())); - - for(String key : shardState.getShardInstance().getShard().getFloc().getPropertyBag().keySet()) - { - String value = shardState.getShardInstance().getShard().getFloc().getPropertyBag().get(key); - if(value != null) - { - args.append("&").append(encoder.encode("floc.property."+key)).append("=").append(encoder.encode(value)); - } - } - - for(String key : shardState.getPropertyBag().keySet()) - { - String value = shardState.getPropertyBag().get(key); - if(value != null) - { - args.append("&").append(encoder.encode("state.property."+key)).append("=").append(encoder.encode(value)); - } - } - - args.append("&").append(encoder.encode("instance")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getInstance())); - args.append("&").append(encoder.encode("numberOfShards")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getFloc().getNumberOfShards())); - args.append("&").append(encoder.encode("port")).append("=").append(encoder.encode("" + shardState.getShardInstance().getPort())); - args.append("&").append(encoder.encode("stores")).append("="); - for(StoreRef store : shardState.getShardInstance().getShard().getFloc().getStoreRefs()) - { - if(args.charAt(args.length()-1) != '=') - { - args.append(encoder.encode(",")); - } - args.append(encoder.encode(store.toString())); - } - args.append("&").append(encoder.encode("isMaster")).append("=").append(encoder.encode("" + shardState.isMaster())); - args.append("&").append(encoder.encode("hasContent")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getFloc().hasContent())); - args.append("&").append(encoder.encode("shardMethod")).append("=").append(encoder.encode(shardState.getShardInstance().getShard().getFloc().getShardMethod().toString())); - - args.append("&").append(encoder.encode("lastUpdated")).append("=").append(encoder.encode("" + shardState.getLastUpdated())); - args.append("&").append(encoder.encode("lastIndexedChangeSetCommitTime")).append("=").append(encoder.encode("" + shardState.getLastIndexedChangeSetCommitTime())); - args.append("&").append(encoder.encode("lastIndexedChangeSetId")).append("=").append(encoder.encode("" + shardState.getLastIndexedChangeSetId())); - args.append("&").append(encoder.encode("lastIndexedTxCommitTime")).append("=").append(encoder.encode("" + shardState.getLastIndexedTxCommitTime())); - args.append("&").append(encoder.encode("lastIndexedTxId")).append("=").append(encoder.encode("" + shardState.getLastIndexedTxId())); - - } - - url.append(args); - LOGGER.debug("### GetRequest: " + url.toString()); - GetRequest req = new GetRequest(url.toString()); - Response response = null; - List transactions = new ArrayList(); - Long maxTxnCommitTime = null; - Long maxTxnIdOnServer = null; - - LookAheadBufferedReader reader = null; - try - { - response = repositoryHttpClient.sendRequest(req); - if(response.getStatus() != HttpStatus.SC_OK) - { - throw new AlfrescoRuntimeException("GetTransactions return status is " + response.getStatus()); - } - - reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); - JsonParser parser = jsonFactory.createParser(reader); - - JsonToken token = parser.nextValue(); - while (token != null) - { - if ("transactions".equals(parser.getCurrentName())) - { - token = parser.nextToken(); //START_ARRAY - while (token == JsonToken.START_OBJECT) - { - token = parser.nextValue(); - long id = parser.getLongValue(); - - token = parser.nextValue(); - long commitTime = parser.getLongValue(); - - token = parser.nextValue(); - long updates = parser.getLongValue(); - - token = parser.nextValue(); - long deletes = parser.getLongValue(); - - Transaction txn = new Transaction(); - txn.setCommitTimeMs(commitTime); - txn.setDeletes(deletes); - txn.setId(id); - txn.setUpdates(updates); - - transactions.add(txn); - - token = parser.nextToken(); //END_OBJECT - token = parser.nextToken(); // START_OBJECT or END_ARRAY; - } - } - else if ("maxTxnCommitTime".equals(parser.getCurrentName())) - { - maxTxnCommitTime = parser.getLongValue(); - } - else if ("maxTxnId".equals(parser.getCurrentName())) - { - maxTxnIdOnServer = parser.getLongValue(); - } - token = parser.nextValue(); - } - parser.close(); - } - catch (JSONException exception) - { - String message = "Received a malformed JSON payload. Request was \"" + - req.getFullUri() + - "Data: " - + ofNullable(reader) - .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) - .orElse("Not available"); - LOGGER.error(message); - throw exception; - } - finally - { - ofNullable(response).ifPresent(Response::release); - ofNullable(reader).ifPresent(this::silentlyClose); - } - - LOGGER.debug("### Transactions found maxTxnCommitTime: " + maxTxnCommitTime ); - return new Transactions(transactions, maxTxnCommitTime, maxTxnIdOnServer); - } - - public List getNodes(GetNodesParameters parameters, int maxResults) throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_NODES_URL); - - JSONObject body = new JSONObject(); - - if(parameters.getTransactionIds() != null) - { - JSONArray jsonTxnIds = new JSONArray(); - for(Long txnId : parameters.getTransactionIds()) - { - jsonTxnIds.put(txnId); - } - body.put("txnIds", jsonTxnIds); - } - - if(parameters.getFromNodeId() != null) - { - body.put("fromNodeId", parameters.getFromNodeId()); - } - if(parameters.getToNodeId() != null) - { - body.put("toNodeId", parameters.getToNodeId()); - } - if(parameters.getExcludeAspects() != null) - { - JSONArray jsonExcludeAspects = new JSONArray(); - for(QName excludeAspect : parameters.getExcludeAspects()) - { - jsonExcludeAspects.put(excludeAspect.toString()); - } - body.put("excludeAspects", jsonExcludeAspects); - } - if(parameters.getIncludeAspects() != null) - { - JSONArray jsonIncludeAspects = new JSONArray(); - for(QName includeAspect : parameters.getIncludeAspects()) - { - jsonIncludeAspects.put(includeAspect.toString()); - } - body.put("includeAspects", jsonIncludeAspects); - } - - if(parameters.getStoreProtocol() != null) - { - body.put("storeProtocol", parameters.getStoreProtocol()); - } - - if(parameters.getStoreIdentifier() != null) - { - body.put("storeIdentifier", parameters.getStoreIdentifier()); - } - - body.put("maxResults", maxResults); - - if(parameters.getShardProperty() != null) - { - body.put("shardProperty", parameters.getShardProperty().toString()); - } - - if (parameters.getCoreName() != null){ - body.put("coreName", parameters.getCoreName()); - } - - - PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); - JSONObject json = callRepository(GET_NODES_URL, req); - - JSONArray jsonNodes = json.getJSONArray("nodes"); - List nodes = new ArrayList<>(jsonNodes.length()); - for(int i = 0; i < jsonNodes.length(); i++) - { - JSONObject jsonNodeInfo = jsonNodes.getJSONObject(i); - Node nodeInfo = new Node(); - if(jsonNodeInfo.has("id")) - { - nodeInfo.setId(jsonNodeInfo.getLong("id")); - } - - if(jsonNodeInfo.has("nodeRef")) - { - nodeInfo.setNodeRef(jsonNodeInfo.getString("nodeRef")); - } - - if(jsonNodeInfo.has("txnId")) - { - nodeInfo.setTxnId(jsonNodeInfo.getLong("txnId")); - } - - if(jsonNodeInfo.has("aclId")) - { - nodeInfo.setAclId(jsonNodeInfo.getLong("aclId")); - } - - if(jsonNodeInfo.has("shardPropertyValue")) - { - nodeInfo.setShardPropertyValue(jsonNodeInfo.getString("shardPropertyValue")); - } - - if(jsonNodeInfo.has("explicitShardId")) - { - nodeInfo.setExplicitShardId(jsonNodeInfo.getInt("explicitShardId")); - } - - if(jsonNodeInfo.has("tenant")) - { - nodeInfo.setTenant(jsonNodeInfo.getString("tenant")); - } - - if(jsonNodeInfo.has("status")) - { - Node.SolrApiNodeStatus status; - String statusStr = jsonNodeInfo.getString("status"); - if(statusStr.equals("u")) - { - status = Node.SolrApiNodeStatus.UPDATED; - } - else if(statusStr.equals("d")) - { - status = Node.SolrApiNodeStatus.DELETED; - } - else - { - status = Node.SolrApiNodeStatus.UNKNOWN; - } - nodeInfo.setStatus(status); - } - - nodes.add(nodeInfo); - } - - return nodes; - } - - private PropertyValue getSinglePropertyValue(DataTypeDefinition dataType, Object value) throws JSONException - { - PropertyValue ret = null; - QName dataTypeName = dataType.getName(); - - if(value == null || value == JSONObject.NULL) - { - ret = null; - } - else if(dataTypeName.equals(DataTypeDefinition.MLTEXT)) - { - JSONArray a = (JSONArray)value; - Map mlValues = new HashMap(a.length()); - - for(int k = 0; k < a.length(); k++) - { - JSONObject pair = a.getJSONObject(k); - Locale locale = deserializer.deserializeValue(Locale.class, pair.getString("locale")); - String mlValue = pair.has("value") && !pair.isNull("value") ? pair.getString("value") : null; - mlValues.put(locale, mlValue); - } - - ret = new MLTextPropertyValue(mlValues); - } - else if(dataTypeName.equals(DataTypeDefinition.CONTENT)) - { - JSONObject o = (JSONObject)value; - - String localeStr = o.has("locale") && !o.isNull("locale") ? o.getString("locale") : null; - Locale locale = (o.has("locale") && !o.isNull("locale") ? deserializer.deserializeValue(Locale.class, localeStr) : null); - - long size = o.has("size") && !o.isNull("size") ? o.getLong("size") : 0; - - String encoding = o.has("encoding") && !o.isNull("encoding") ? o.getString("encoding") : null; - String mimetype = o.has("mimetype") && !o.isNull("mimetype") ? o.getString("mimetype") : null; - - Long id = o.has("contentId") && !o.isNull("contentId") ? o.getLong("contentId") : null; - - ret = new ContentPropertyValue(locale, size, encoding, mimetype, id); - } - else - { - ret = new StringPropertyValue((String)value); - } - - return ret; - } - - private PropertyValue getPropertyValue(PropertyDefinition propertyDef, Object value) throws JSONException - { - PropertyValue ret = null; - - if(value == null || value == JSONObject.NULL) - { - ret = null; - } - else if(propertyDef == null) - { - // assume a string - ret = new StringPropertyValue((String)value); - } - else - { - DataTypeDefinition dataType = propertyDef.getDataType(); - - boolean isMulti = propertyDef.isMultiValued(); - if(isMulti) - { - if(!(value instanceof JSONArray)) - { - throw new IllegalArgumentException("Expected json array, got " + value.getClass().getName()); - } - - MultiPropertyValue multi = new MultiPropertyValue(); - JSONArray array = (JSONArray)value; - for(int j = 0; j < array.length(); j++) - { - multi.addValue(getSinglePropertyValue(dataType, array.get(j))); - } - - ret = multi; - } - else - { - ret = getSinglePropertyValue(dataType, value); - } - } - - return ret; - } - - public List getNodesMetaData(NodeMetaDataParameters params) throws AuthenticationException, IOException, JSONException - { - List nodeIds = params.getNodeIds(); - - StringBuilder url = new StringBuilder(GET_METADATA_URL); - - JSONObject body = new JSONObject(); - if(nodeIds != null && nodeIds.size() > 0) - { - JSONArray jsonNodeIds = new JSONArray(); - for(Long nodeId : nodeIds) - { - jsonNodeIds.put(nodeId); - } - body.put("nodeIds", jsonNodeIds); - - } - if(params.getFromNodeId() != null) - { - body.put("fromNodeId", params.getFromNodeId()); - } - if(params.getToNodeId() != null) - { - body.put("toNodeId", params.getToNodeId()); - } - - // only need to set in cases where we don't want them in the response - // because they default to true - if(!params.isIncludeAclId()) - { - body.put("includeAclId", params.isIncludeAclId()); - } - if(!params.isIncludeAspects()) - { - body.put("includeAspects", params.isIncludeAspects()); - } - if(!params.isIncludeProperties()) - { - body.put("includeProperties", params.isIncludeProperties()); - } - if(!params.isIncludeChildAssociations()) - { - body.put("includeChildAssociations", params.isIncludeChildAssociations()); - } - if(!params.isIncludeParentAssociations()) - { - body.put("includeParentAssociations", params.isIncludeParentAssociations()); - } - if(!params.isIncludeChildIds()) - { - body.put("includeChildIds", params.isIncludeChildIds()); - } - if(!params.isIncludePaths()) - { - body.put("includePaths", params.isIncludePaths()); - } - if(!params.isIncludeOwner()) - { - body.put("includeOwner", params.isIncludeOwner()); - } - if(!params.isIncludeNodeRef()) - { - body.put("includeNodeRef", params.isIncludeNodeRef()); - } - if(!params.isIncludeTxnId()) - { - body.put("includeTxnId", params.isIncludeTxnId()); - } - - if (params.getMaxResults().isPresent()) - { - body.put("maxResults", params.getMaxResults().getAsInt()); - } - - PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); - JSONObject json = callRepository(GET_METADATA_URL, req); - - JSONArray jsonNodes = json.getJSONArray("nodes"); - List nodes = new ArrayList<>(jsonNodes.length()); - for(int i = 0; i < jsonNodes.length(); i++) - { - JSONObject jsonNodeInfo = jsonNodes.getJSONObject(i); - NodeMetaData metaData = new NodeMetaData(); - - if(jsonNodeInfo.has("id")) - { - metaData.setId(jsonNodeInfo.getLong("id")); - } - - if(jsonNodeInfo.has("tenantDomain")) - { - metaData.setTenantDomain(jsonNodeInfo.getString("tenantDomain")); - } - - if(jsonNodeInfo.has("txnId")) - { - metaData.setTxnId(jsonNodeInfo.getLong("txnId")); - } - - if(jsonNodeInfo.has("aclId")) - { - metaData.setAclId(jsonNodeInfo.getLong("aclId")); - } - - if(jsonNodeInfo.has("nodeRef")) - { - metaData.setNodeRef(new NodeRef(jsonNodeInfo.getString("nodeRef"))); - } - - if(jsonNodeInfo.has("type")) - { - metaData.setType(deserializer.deserializeValue(QName.class, jsonNodeInfo.getString("type"))); - } - - if(jsonNodeInfo.has("aspects")) - { - JSONArray jsonAspects = jsonNodeInfo.getJSONArray("aspects"); - Set aspects = new HashSet(jsonAspects.length()); - for(int j = 0; j < jsonAspects.length(); j++) - { - String jsonAspect = (String)jsonAspects.get(j); - aspects.add(deserializer.deserializeValue(QName.class, jsonAspect)); - } - metaData.setAspects(aspects); - } - - if(jsonNodeInfo.has("paths")) - { - JSONArray jsonPaths = jsonNodeInfo.getJSONArray("paths"); - List> paths = new ArrayList>(jsonPaths.length()); - List ancestorPaths = new ArrayList(); - for(int j = 0; j < jsonPaths.length(); j++) - { - JSONObject path = jsonPaths.getJSONObject(j); - String pathValue = path.getString("path"); - QName qname = path.has("qname") ? deserializer.deserializeValue(QName.class, path.getString("qname")) : null; - paths.add(new Pair(pathValue, qname)); - if(path.has("apath")) - { - String ancestorPath = path.getString("apath"); - ancestorPaths.add(ancestorPath); - } - } - metaData.setPaths(paths); - metaData.setAncestorPaths(ancestorPaths); - } - - if(jsonNodeInfo.has("namePaths")) - { - JSONArray jsonNamePaths = jsonNodeInfo.getJSONArray("namePaths"); - List> namePaths = new ArrayList>(jsonNamePaths.length()); - for(int j = 0; j < jsonNamePaths.length(); j++) - { - JSONObject jsonNamePath = jsonNamePaths.getJSONObject(j); - JSONArray jsonNameElements = jsonNamePath.getJSONArray("namePath"); - List namePath = new ArrayList(jsonNameElements.length()); - for(int k = 0; k < jsonNameElements.length(); k++) - { - String namePathElement = jsonNameElements.getString(k); - namePath.add(namePathElement); - } - namePaths.add(namePath); - } - metaData.setNamePaths(namePaths); - } - - if(jsonNodeInfo.has("ancestors")) - { - JSONArray jsonAncestors = jsonNodeInfo.getJSONArray("ancestors"); - HashSet ancestors = new HashSet(jsonAncestors.length()); - for(int j = 0; j < jsonAncestors.length(); j++) - { - String ancestorNodeRefString = jsonAncestors.getString(j); - NodeRef ancestorNodeRef = new NodeRef(ancestorNodeRefString); - ancestors.add(ancestorNodeRef); - } - metaData.setAncestors(ancestors); - } - - if(jsonNodeInfo.has("properties")) - { - JSONObject jsonProperties = jsonNodeInfo.getJSONObject("properties"); - Map properties = new HashMap(jsonProperties.length()); - @SuppressWarnings("rawtypes") - Iterator propKeysIterator = jsonProperties.keys(); - while(propKeysIterator.hasNext()) - { - String propName = (String)propKeysIterator.next(); - QName propQName = deserializer.deserializeValue(QName.class, propName); - Object propValueObj = jsonProperties.opt(propName); - - // check the expected property type to determine how to process the value - PropertyDefinition propertyDef = dictionaryService.getProperty(propQName); -// if(propertyDef == null) -// { -// // TODO which exception here? -// throw new IllegalArgumentException("Could not find property definition for property " + propName); -// } - - properties.put(propQName, getPropertyValue(propertyDef, propValueObj)); - } - metaData.setProperties(properties); - } - - if(jsonNodeInfo.has("parentAssocsCrc")) - { - metaData.setParentAssocsCrc(jsonNodeInfo.getLong("parentAssocsCrc")); - } - - if(jsonNodeInfo.has("parentAssocs")) - { - JSONArray jsonParentAssocs = jsonNodeInfo.getJSONArray("parentAssocs"); - List assocs = new ArrayList(jsonParentAssocs.length()); - for(int j = 0; j < jsonParentAssocs.length(); j++) - { - String childAssocRefStr = jsonParentAssocs.getString(j); - ChildAssociationRef childAssociationRef = new ChildAssociationRef(childAssocRefStr); - assocs.add(childAssociationRef); - } - metaData.setParentAssocs(assocs); - } - - if(jsonNodeInfo.has("childAssocs")) - { - JSONArray jsonParentAssocs = jsonNodeInfo.getJSONArray("childAssocs"); - List assocs = new ArrayList(jsonParentAssocs.length()); - for(int j = 0; j < jsonParentAssocs.length(); j++) - { - String childAssocRefStr = jsonParentAssocs.getString(j); - ChildAssociationRef childAssociationRef = new ChildAssociationRef(childAssocRefStr); - assocs.add(childAssociationRef); - } - metaData.setChildAssocs(assocs); - } - - if(jsonNodeInfo.has("childIds")) - { - JSONArray jsonChildIds = jsonNodeInfo.getJSONArray("childIds"); - List childIds = new ArrayList(jsonChildIds.length()); - for(int j = 0; j < jsonChildIds.length(); j++) - { - Long childId = jsonChildIds.getLong(j); - childIds.add(childId); - } - metaData.setChildIds(childIds); - } - - if(jsonNodeInfo.has("owner")) - { - metaData.setOwner(jsonNodeInfo.getString("owner")); - } - - nodes.add(metaData); - } - - return nodes; - } - - public GetTextContentResponse getTextContent(Long nodeId, QName propertyQName, Long modifiedSince) throws AuthenticationException, IOException { - StringBuilder url = new StringBuilder(128); - url.append(GET_CONTENT); - - StringBuilder args = new StringBuilder(128); - if(nodeId != null) - { - args.append("?"); - args.append("nodeId"); - args.append("="); - args.append(nodeId); - } - else - { - throw new NullPointerException("getTextContent(): nodeId cannot be null."); - } - if(propertyQName != null) - { - if(args.length() == 0) - { - args.append("?"); - } - else - { - args.append("&"); - } - args.append("propertyQName"); - args.append("="); - args.append(URLEncoder.encode(propertyQName.toString())); - } - - url.append(args); - - GetRequest req = new GetRequest(url.toString()); - - Map headers = new HashMap<>(); - if(modifiedSince != null) - { - headers.put("If-Modified-Since", String.valueOf(DateUtil.formatDate(new Date(modifiedSince)))); - } - if (compression) - { - headers.put("Accept-Encoding", "gzip"); - } - req.setHeaders(headers); - - Response response = repositoryHttpClient.sendRequest(req); - - if(response.getStatus() != Status.STATUS_NOT_MODIFIED && response.getStatus() != Status.STATUS_NO_CONTENT && response.getStatus() != Status.STATUS_OK) - { - int status = response.getStatus(); - response.release(); - throw new AlfrescoRuntimeException("GetTextContentResponse return status is " + status); - } - - return new GetTextContentResponse(response); - } - - public AlfrescoModel getModel(String coreName, QName modelName) throws AuthenticationException, IOException - { - // If the model is new to the SOLR side the prefix will be unknown so we can not generate prefixes for the request! - // Always use the full QName with explicit URI - StringBuilder url = new StringBuilder(GET_MODEL); - - URLCodec encoder = new URLCodec(); - // must send the long name as we may not have the prefix registered - url.append("?modelQName=").append(encoder.encode(modelName.toString(), "UTF-8")); - - GetRequest req = new GetRequest(url.toString()); - - Response response = null; - try - { - response = repositoryHttpClient.sendRequest(req); - if(response.getStatus() != HttpStatus.SC_OK) - { - throw new AlfrescoRuntimeException(coreName + " GetModel return status is " + response.getStatus()); - } - - return new AlfrescoModel(M2Model.createModel(response.getContentAsStream()), - Long.valueOf(response.getHeader(CHECKSUM_HEADER))); - } - finally - { - if(response != null) - { - response.release(); - } - } - } - - public List getModelsDiff(String coreName, List currentModels) throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_MODELS_DIFF); - - JSONObject body = new JSONObject(); - JSONArray jsonModels = new JSONArray(); - for(AlfrescoModel model : currentModels) - { - JSONObject jsonModel = new JSONObject(); - QName modelQName = QName.createQName( model.getModel().getName(), namespaceDAO); - jsonModel.put("name", modelQName.toString()); - jsonModel.put("checksum", model.getChecksum()); - jsonModels.put(jsonModel); - } - body.put("models", jsonModels); - - PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); - JSONObject json = callRepository(GET_MODELS_DIFF, req); - - JSONArray jsonDiffs = json.getJSONArray("diffs"); - if(jsonDiffs == null) - { - throw new AlfrescoRuntimeException("GetModelsDiff badly formatted response"); - } - - List diffs = new ArrayList(jsonDiffs.length()); - for(int i = 0; i < jsonDiffs.length(); i++) - { - JSONObject jsonDiff = jsonDiffs.getJSONObject(i); - diffs.add(new AlfrescoModelDiff( - QName.createQName(jsonDiff.getString("name")), - AlfrescoModelDiff.TYPE.valueOf(jsonDiff.getString("type")), - (jsonDiff.isNull("oldChecksum") ? null : jsonDiff.getLong("oldChecksum")), - (jsonDiff.isNull("newChecksum") ? null : jsonDiff.getLong("newChecksum")))); - } - - return diffs; - } - - /** - * Returns the next commit time from a given commit time. - * - * @param coreName alfresco, archive - * @param fromCommitTime initial transaction commit time - * @return Time of the next transaction - * @throws IOException - * @throws AuthenticationException - * @throws NoSuchMethodException - */ - public Long getNextTxCommitTime(String coreName, Long fromCommitTime) throws AuthenticationException, IOException, NoSuchMethodException - { - StringBuilder url = new StringBuilder(GET_NEXT_TX_COMMIT_TIME); - url.append("?").append("fromCommitTime").append("=").append(fromCommitTime); - GetRequest get = new GetRequest(url.toString()); - Response response = null; - JSONObject json = null; - LookAheadBufferedReader reader = null; - try - { - response = repositoryHttpClient.sendRequest(get); - if (response.getStatus() != HttpStatus.SC_OK) - { - throw new NoSuchMethodException(coreName + " - GetNextTxCommitTime return status is " - + response.getStatus() + " when invoking " + url); - } - - reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); - json = new JSONObject(new JSONTokener(reader)); - } - catch (JSONException exception) - { - String message = "Received a malformed JSON payload. Request was \"" + - get.getFullUri() + - "Data: " - + ofNullable(reader) - .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) - .orElse("Not available"); - LOGGER.error(message); - throw exception; - } - finally - { - ofNullable(response).ifPresent(Response::release); - ofNullable(reader).ifPresent(this::silentlyClose); - } - - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug(json.toString()); - } - - return Long.parseLong(json.get("nextTransactionCommitTimeMs").toString()); - } - - /** - * Returns the minimum and the maximum commit time for transactions in a node id range. - * - * @param coreName alfresco, archive - * @param fromNodeId Id of the initial node - * @param toNodeId Id of the final node - * @return Time of the first transaction, time of the last transaction - * @throws IOException - * @throws AuthenticationException - * @throws NoSuchMethodException - */ - public Pair getTxIntervalCommitTime(String coreName, Long fromNodeId, Long toNodeId) - throws AuthenticationException, IOException, NoSuchMethodException - { - StringBuilder url = new StringBuilder(GET_TX_INTERVAL_COMMIT_TIME); - url.append("?").append("fromNodeId").append("=").append(fromNodeId); - url.append("&").append("toNodeId").append("=").append(toNodeId); - GetRequest get = new GetRequest(url.toString()); - Response response = null; - JSONObject json = null; - LookAheadBufferedReader reader = null; - try - { - response = repositoryHttpClient.sendRequest(get); - if (response.getStatus() != HttpStatus.SC_OK) - { - throw new NoSuchMethodException(coreName + " - GetTxIntervalCommitTime return status is " - + response.getStatus() + " when invoking " + url); - } - - reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); - json = new JSONObject(new JSONTokener(reader)); - } - catch(JSONException exception) - { - String message = "Received a malformed JSON payload. Request was \"" + - get.getFullUri() + - "Data: " - + ofNullable(reader) - .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) - .orElse("Not available"); - LOGGER.error(message); - throw exception; - } - finally - { - ofNullable(response).ifPresent(Response::release); - ofNullable(reader).ifPresent(this::silentlyClose); - } - - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug(json.toString()); - } - - return new Pair(Long.parseLong(json.get("minTransactionCommitTimeMs").toString()), - Long.parseLong(json.get("maxTransactionCommitTimeMs").toString())); - } - - /* - * type conversions from serialized JSON values to SOLR-consumable objects - */ - @SuppressWarnings("rawtypes") - private class SOLRTypeConverter - { - /** - * Default Type Converter - */ - private TypeConverter instance = new TypeConverter(); - private NamespaceDAO namespaceDAO; - - @SuppressWarnings("unchecked") - SOLRTypeConverter(NamespaceDAO namespaceDAO) - { - this.namespaceDAO = namespaceDAO; - - // add all default converters to this converter - for (Entry, Map, Converter>> source : DefaultTypeConverter.INSTANCE.getConverters().entrySet()) - { - for (Entry, Converter> dest : source.getValue().entrySet()) - { - instance.addConverter((Class) source.getKey(), (Class) dest.getKey(), dest.getValue()); - } - } - - // dates - instance.addConverter(String.class, Date.class, new TypeConverter.Converter() - { - public Date convert(String source) - { - try - { - return ISO8601DateFormat.parse(source); - } - catch (Exception e) - { - throw new TypeConversionException("Failed to convert date " + source + " to string", e); - } - } - }); - - // node refs - instance.addConverter(String.class, NodeRef.class, new TypeConverter.Converter() - { - public NodeRef convert(String source) - { - return new NodeRef(source); - } - }); - - // paths - instance.addConverter(String.class, AttributeElement.class, new TypeConverter.Converter() - { - public AttributeElement convert(String source) - { - return new Path.AttributeElement(source); - } - }); - - instance.addConverter(String.class, ChildAssocElement.class, new TypeConverter.Converter() - { - public ChildAssocElement convert(String source) - { - return new Path.ChildAssocElement(instance.convert(ChildAssociationRef.class, source)); - } - }); - - instance.addConverter(String.class, Path.DescendentOrSelfElement.class, new TypeConverter.Converter() - { - public Path.DescendentOrSelfElement convert(String source) - { - return new Path.DescendentOrSelfElement(); - } - }); - - instance.addConverter(String.class, Path.ParentElement.class, new TypeConverter.Converter() - { - public Path.ParentElement convert(String source) - { - return new Path.ParentElement(); - } - }); - - instance.addConverter(String.class, Path.SelfElement.class, new TypeConverter.Converter() - { - public Path.SelfElement convert(String source) - { - return new Path.SelfElement(); - } - }); - - // associations - instance.addConverter(String.class, ChildAssociationRef.class, new TypeConverter.Converter() - { - public ChildAssociationRef convert(String source) - { - return new ChildAssociationRef(source); - } - }); - - instance.addConverter(String.class, AssociationRef.class, new TypeConverter.Converter() - { - public AssociationRef convert(String source) - { - return new AssociationRef(source); - } - }); - - // qnames - instance.addConverter(String.class, QName.class, new TypeConverter.Converter() - { - public QName convert(String source) - { - return QName.resolveToQName(SOLRTypeConverter.this.namespaceDAO, source); - } - }); - - instance.addConverter(String.class, MLText.class, new TypeConverter.Converter() - { - public MLText convert(String source) - { - return new MLText(source); - } - }); - } - - public final T convert(Class c, Object value) - { - return instance.convert(c, value); - } - } - - /* - * Deserializes JSON values from the remote API into objects consumable by SOLR - */ - private class SOLRDeserializer - { - private SOLRTypeConverter typeConverter; - - public SOLRDeserializer(NamespaceDAO namespaceDAO) - { - typeConverter = new SOLRTypeConverter(namespaceDAO); - } - - public T deserializeValue(Class targetClass, Object value) throws JSONException - { - return typeConverter.convert(targetClass, value); - } - } - - private static class SOLRResponse - { - protected Response response; - - public SOLRResponse(Response response) - { - super(); - this.response = response; - } - - public Response getResponse() - { - return response; - } - } - - public static class GetTransactionsResponse extends SOLRResponse - { - private List txns; - - public GetTransactionsResponse(Response response, List txns) - { - super(response); - this.txns = txns; - } - - public List getTransaction() - { - return txns; - } - } - - public static class GetNodesResponse extends SOLRResponse - { - private List nodes; - - public GetNodesResponse(Response response, List nodes) - { - super(response); - this.nodes = nodes; - } - - public List getNodes() - { - return nodes; - } - } - - public static class GetNodesMetaDataResponse extends SOLRResponse - { - private List nodes; - - public GetNodesMetaDataResponse(Response response, List nodes) - { - super(response); - this.nodes = nodes; - } - - public List getNodes() - { - return nodes; - } - } - - public static enum SolrApiContentStatus - { - NOT_MODIFIED, OK, NO_TRANSFORM, NO_CONTENT, UNKNOWN, TRANSFORM_FAILED, GENERAL_FAILURE; - - public static SolrApiContentStatus getStatus(String statusStr) - { - if(statusStr.equals("ok")) - { - return OK; - } - else if(statusStr.equals("transformFailed")) - { - return TRANSFORM_FAILED; - } - else if(statusStr.equals("noTransform")) - { - return NO_TRANSFORM; - } - else if(statusStr.equals("noContent")) - { - return NO_CONTENT; - } - else - { - return UNKNOWN; - } - } - } - - // TODO register a stream close listener that release the response when the response has been read - public static class GetTextContentResponse extends SOLRResponse implements AutoCloseable - { - private InputStream content; - private SolrApiContentStatus status; - private String transformException; - private String transformStatusStr; - private Long transformDuration; - private String contentEncoding; - - public GetTextContentResponse(Response response) throws IOException - { - super(response); - - this.content = response.getContentAsStream(); - this.transformStatusStr = response.getHeader("X-Alfresco-transformStatus"); - this.transformException = response.getHeader("X-Alfresco-transformException"); - String tmp = response.getHeader("X-Alfresco-transformDuration"); - this.transformDuration = (tmp != null ? Long.valueOf(tmp) : null); - this.contentEncoding = response.getHeader("Content-Encoding"); - setStatus(); - } - - public InputStream getContent() - { - return content; - } - - public SolrApiContentStatus getStatus() - { - return status; - } - - private void setStatus() - { - int status = response.getStatus(); - if(status == HttpStatus.SC_NOT_MODIFIED) - { - this.status = SolrApiContentStatus.NOT_MODIFIED; - } - else if(status == HttpStatus.SC_INTERNAL_SERVER_ERROR) - { - this.status = SolrApiContentStatus.GENERAL_FAILURE; - } - else if(status == HttpStatus.SC_OK) - { - this.status = SolrApiContentStatus.OK; - } - else if(status == HttpStatus.SC_NO_CONTENT) - { - if(transformStatusStr == null) - { - this.status = SolrApiContentStatus.UNKNOWN; - } - else - { - if(transformStatusStr.equals("noTransform")) - { - this.status = SolrApiContentStatus.NO_TRANSFORM; - } - else if(transformStatusStr.equals("transformFailed")) - { - this.status = SolrApiContentStatus.TRANSFORM_FAILED; - } - else - { - this.status = SolrApiContentStatus.UNKNOWN; - } - } - } - } - - public String getTransformException() - { - return transformException; - } - - public void release() - { - response.release(); - } - - public Long getTransformDuration() - { - return transformDuration; - } - - public String getContentEncoding() - { - return contentEncoding; - } - - @Override - public void close() { - response.release(); - } - } - - public void close() - { - repositoryHttpClient.close(); - } - - private JSONObject callRepository(String msgId, Request req) throws IOException, AuthenticationException - { - Response response = null; - LookAheadBufferedReader reader = null; - JSONObject json; - try - { - response = repositoryHttpClient.sendRequest(req); - if (response.getStatus() != HttpStatus.SC_OK) - { - throw new AlfrescoRuntimeException(msgId + " return status:" + response.getStatus()); - } - - reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); - json = new JSONObject(new JSONTokener(reader)); - - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug(json.toString(3)); - } - return json; - } - catch (JSONException exception) - { - String message = "Received a malformed JSON payload. Request was \"" + - req.getFullUri() + - "Data: " - + ofNullable(reader) - .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) - .orElse("Not available"); - LOGGER.error(message); - throw exception; - } - finally - { - ofNullable(response).ifPresent(Response::release); - ofNullable(reader).ifPresent(this::silentlyClose); - } - } - - private void silentlyClose(Closeable closeable) - { - try - { - closeable.close(); - } - catch (Exception ignore) - { - // Nothing to be done here - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.AlfrescoHttpClient; +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.httpclient.GetRequest; +import org.alfresco.httpclient.PostRequest; +import org.alfresco.httpclient.Request; +import org.alfresco.httpclient.Response; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.index.shard.ShardState; +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.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.MLText; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.Path.AttributeElement; +import org.alfresco.service.cmr.repository.Path.ChildAssocElement; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConversionException; +import org.alfresco.service.cmr.repository.datatype.TypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConverter.Converter; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO8601DateFormat; +import org.alfresco.util.Pair; +import org.apache.commons.codec.EncoderException; +import org.apache.commons.codec.net.URLCodec; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.util.DateUtil; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.extensions.surf.util.URLEncoder; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import static java.util.Optional.ofNullable; + +// TODO error handling, including dealing with a repository that is not responsive (ConnectException in sendRemoteRequest) +// TODO get text content transform status handling +/** + * Http client to handle SOLR-Alfresco remote calls. + * + * @since 4.0 + */ +public class SOLRAPIClient +{ + protected final static Logger LOGGER = LoggerFactory.getLogger(SOLRAPIClient.class); + private static final String GET_ACL_CHANGESETS_URL = "api/solr/aclchangesets"; + private static final String GET_ACLS = "api/solr/acls"; + private static final String GET_ACLS_READERS = "api/solr/aclsReaders"; + private static final String GET_TRANSACTIONS_URL = "api/solr/transactions"; + private static final String GET_METADATA_URL = "api/solr/metadata"; + private static final String GET_NODES_URL = "api/solr/nodes"; + private static final String GET_CONTENT = "api/solr/textContent"; + private static final String GET_MODEL = "api/solr/model"; + private static final String GET_MODELS_DIFF = "api/solr/modelsdiff"; + private static final String GET_NEXT_TX_COMMIT_TIME = "api/solr/nextTransaction"; + private static final String GET_TX_INTERVAL_COMMIT_TIME = "api/solr/transactionInterval"; + + private static final String CHECKSUM_HEADER = "XAlfresco-modelChecksum"; + + private AlfrescoHttpClient repositoryHttpClient; + private SOLRDeserializer deserializer; + private DictionaryService dictionaryService; + private JsonFactory jsonFactory; + private NamespaceDAO namespaceDAO; + + /** + * This option enables ("Accept-Encoding": "gzip") header for compression + * in GET_CONTENT requests. Additional configuration is required in + * Alfresco Repository Tomcat Connector or HTTP Web Proxy to deal + * with compressed requests. + */ + private boolean compression; + + public SOLRAPIClient(AlfrescoHttpClient repositoryHttpClient, + DictionaryService dictionaryService, + NamespaceDAO namespaceDAO) + { + this(repositoryHttpClient, dictionaryService, namespaceDAO, false); + } + + public SOLRAPIClient(AlfrescoHttpClient repositoryHttpClient, + DictionaryService dictionaryService, + NamespaceDAO namespaceDAO, + boolean compression) + { + this.repositoryHttpClient = repositoryHttpClient; + this.dictionaryService = dictionaryService; + this.namespaceDAO = namespaceDAO; + this.deserializer = new SOLRDeserializer(namespaceDAO); + this.jsonFactory = new JsonFactory(); + this.compression = compression; + } + + /** + * Get the ACL ChangeSets + * + * @param fromCommitTime the lowest commit time (optional) + * @param minAclChangeSetId the lowest ChangeSet ID (optional) + * @param maxResults the maximum number of results (a reasonable value only) + * @return the ACL ChangeSets in order of commit time and ID + */ + public AclChangeSets getAclChangeSets(Long fromCommitTime, Long minAclChangeSetId, Long toCommitTime, Long maxAclChangeSetId, int maxResults) + throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_ACL_CHANGESETS_URL); + StringBuilder args = new StringBuilder(); + if (fromCommitTime != null) + { + args.append("?").append("fromTime").append("=").append(fromCommitTime); + } + if (minAclChangeSetId != null) + { + args.append(args.length() == 0 ? "?" : "&").append("fromId").append("=").append(minAclChangeSetId); + } + if (toCommitTime != null) + { + args.append(args.length() == 0 ? "?" : "&").append("toTime").append("=").append(toCommitTime); + } + if (maxAclChangeSetId != null) + { + args.append(args.length() == 0 ? "?" : "&").append("toId").append("=").append(maxAclChangeSetId); + } + if (maxResults != 0 && maxResults != Integer.MAX_VALUE) + { + args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); + } + url.append(args); + + GetRequest req = new GetRequest(url.toString()); + JSONObject json = callRepository(GET_ACL_CHANGESETS_URL, req); + + JSONArray aclChangeSetsJSON = json.getJSONArray("aclChangeSets"); + List aclChangeSets = new ArrayList(aclChangeSetsJSON.length()); + for (int i = 0; i < aclChangeSetsJSON.length(); i++) + { + JSONObject aclChangeSetJSON = aclChangeSetsJSON.getJSONObject(i); + long aclChangeSetId = aclChangeSetJSON.getLong("id"); + long commitTimeMs = aclChangeSetJSON.getLong("commitTimeMs"); + int aclCount = aclChangeSetJSON.getInt("aclCount"); + AclChangeSet aclChangeSet = new AclChangeSet(aclChangeSetId, commitTimeMs, aclCount); + aclChangeSets.add(aclChangeSet); + } + + Long maxChangeSetCommitTime = null; + if(json.has("maxChangeSetCommitTime")) + { + maxChangeSetCommitTime = json.getLong("maxChangeSetCommitTime"); + } + + Long maxChangeSetIdOnServer = null; + if(json.has("maxChangeSetId")) + { + maxChangeSetIdOnServer = json.getLong("maxChangeSetId"); + } + + // Done + return new AclChangeSets(aclChangeSets, maxChangeSetCommitTime, maxChangeSetIdOnServer); + } + + /** + * Get the ACLs associated with a given list of ACL ChangeSets. The ACLs may be truncated for + * the last ACL ChangeSet in the return values - the ACL count from the + * {@link #getAclChangeSets(Long, Long, Long, Long, int) ACL ChangeSets}. + * + * @param aclChangeSets the ACL ChangeSets to include + * @param minAclId the lowest ACL ID (may be null) + * @param maxResults the maximum number of results to retrieve + * @return the ACLs (includes ChangeSet ID) + */ + public List getAcls(List aclChangeSets, Long minAclId, int maxResults) throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_ACLS); + StringBuilder args = new StringBuilder(); + if (minAclId != null) + { + args.append("?").append("fromId").append("=").append(minAclId); + } + + if(maxResults >= 0) + { + args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); + } + + url.append(args); + + JSONObject jsonReq = new JSONObject(); + JSONArray aclChangeSetIdsJSON = new JSONArray(); + List aclChangeSetIds = new ArrayList(); + for (AclChangeSet aclChangeSet : aclChangeSets) + { + Long aclChangeSetId = aclChangeSet.getId(); + aclChangeSetIdsJSON.put(aclChangeSetId); + aclChangeSetIds.add(aclChangeSetId); + } + jsonReq.put("aclChangeSetIds", aclChangeSetIdsJSON); + + PostRequest req = new PostRequest(url.toString(), jsonReq.toString(), "application/json"); + JSONObject json = callRepository(GET_ACL_CHANGESETS_URL, req); + + JSONArray aclsJSON = json.getJSONArray("acls"); + List acls = new ArrayList(aclsJSON.length()); + for (int i = 0; i < aclsJSON.length(); i++) + { + JSONObject aclJSON = aclsJSON.getJSONObject(i); + long aclChangeSetId = aclJSON.getLong("aclChangeSetId"); + long aclId = aclJSON.getLong("id"); + Acl acl = new Acl(aclChangeSetId, aclId); + acls.add(acl); + } + // Done + return acls; + } + + /** + * Get the ACL readers for a given list of ACLs + * + * @param acls the ACLs + * @return the readers for the ACLs + */ + public List getAclReaders(List acls) throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_ACLS_READERS); + + JSONObject jsonReq = new JSONObject(); + JSONArray aclIdsJSON = new JSONArray(); + List aclIds = new ArrayList(); + for (Acl acl : acls) + { + Long aclId = acl.getId(); + aclIdsJSON.put(aclId); + aclIds.add(aclId); + } + jsonReq.put("aclIds", aclIdsJSON); + + PostRequest req = new PostRequest(url.toString(), jsonReq.toString(), "application/json"); + JSONObject json = callRepository(GET_ACLS_READERS, req); + + JSONArray aclsReadersJSON = json.getJSONArray("aclsReaders"); + List aclsReaders = new ArrayList(aclsReadersJSON.length()); + for (int i = 0; i < aclsReadersJSON.length(); i++) + { + JSONObject aclReadersJSON = aclsReadersJSON.getJSONObject(i); + long aclId = aclReadersJSON.getLong("aclId"); + JSONArray readersJSON = aclReadersJSON.getJSONArray("readers"); + List readers = authorityListFromJSON(readersJSON); + JSONArray deniedJSON = aclReadersJSON.getJSONArray("denied"); + List denied = authorityListFromJSON(deniedJSON); + long aclChangeSetId = aclReadersJSON.getLong("aclChangeSetId"); + + String tenantDomain = aclReadersJSON.getString("tenantDomain"); + if (tenantDomain == null) + { + tenantDomain = TenantService.DEFAULT_DOMAIN; + } + + AclReaders aclReaders = new AclReaders(aclId, readers, denied, aclChangeSetId, tenantDomain); + aclsReaders.add(aclReaders); + } + // Done + return aclsReaders; + } + + /** + * Convert a JSON array of authorities to a simple Java List<String> + * + * @param jsonArray JSONArray + * @return List<String> + * @throws JSONException + */ + private List authorityListFromJSON(JSONArray jsonArray) throws JSONException + { + List authorities = new ArrayList(jsonArray.length()); + for (int j = 0; j < jsonArray.length(); j++) + { + String authority = jsonArray.getString(j); + authorities.add(authority); + } + return authorities; + } + + public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, Long maxTxnId, int maxResults) throws AuthenticationException, IOException, JSONException + { + try + { + return getTransactions(fromCommitTime, minTxnId, toCommitTime, maxTxnId, maxResults, null); + } + catch (EncoderException e) + { + // Can not happen .... + throw new IOException(e); + } + } + + public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, + Long maxTxnId, int maxResults, ShardState shardState) + throws AuthenticationException, IOException, JSONException, EncoderException + { + URLCodec encoder = new URLCodec(); + + StringBuilder url = new StringBuilder(GET_TRANSACTIONS_URL); + StringBuilder args = new StringBuilder(); + if (fromCommitTime != null) + { + args.append("?").append("fromCommitTime").append("=").append(fromCommitTime); + } + if (minTxnId != null) + { + args.append(args.length() == 0 ? "?" : "&").append("minTxnId").append("=").append(minTxnId); + } + if (toCommitTime != null) + { + args.append(args.length() == 0 ? "?" : "&").append("toCommitTime").append("=").append(toCommitTime); + } + if (maxTxnId != null) + { + args.append(args.length() == 0 ? "?" : "&").append("maxTxnId").append("=").append(maxTxnId); + } + if (maxResults != 0 && maxResults != Integer.MAX_VALUE) + { + args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); + } + if(shardState != null) + { + LOGGER.debug("### Shard state exists ###"); + args.append(args.length() == 0 ? "?" : "&"); + args.append(encoder.encode("baseUrl")).append("=").append(encoder.encode(shardState.getShardInstance().getBaseUrl())); + args.append("&").append(encoder.encode("hostName")).append("=").append(encoder.encode(shardState.getShardInstance().getHostName())); + args.append("&").append(encoder.encode("template")).append("=").append(encoder.encode(shardState.getShardInstance().getShard().getFloc().getTemplate())); + + for(String key : shardState.getShardInstance().getShard().getFloc().getPropertyBag().keySet()) + { + String value = shardState.getShardInstance().getShard().getFloc().getPropertyBag().get(key); + if(value != null) + { + args.append("&").append(encoder.encode("floc.property."+key)).append("=").append(encoder.encode(value)); + } + } + + for(String key : shardState.getPropertyBag().keySet()) + { + String value = shardState.getPropertyBag().get(key); + if(value != null) + { + args.append("&").append(encoder.encode("state.property."+key)).append("=").append(encoder.encode(value)); + } + } + + args.append("&").append(encoder.encode("instance")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getInstance())); + args.append("&").append(encoder.encode("numberOfShards")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getFloc().getNumberOfShards())); + args.append("&").append(encoder.encode("port")).append("=").append(encoder.encode("" + shardState.getShardInstance().getPort())); + args.append("&").append(encoder.encode("stores")).append("="); + for(StoreRef store : shardState.getShardInstance().getShard().getFloc().getStoreRefs()) + { + if(args.charAt(args.length()-1) != '=') + { + args.append(encoder.encode(",")); + } + args.append(encoder.encode(store.toString())); + } + args.append("&").append(encoder.encode("isMaster")).append("=").append(encoder.encode("" + shardState.isMaster())); + args.append("&").append(encoder.encode("hasContent")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getFloc().hasContent())); + args.append("&").append(encoder.encode("shardMethod")).append("=").append(encoder.encode(shardState.getShardInstance().getShard().getFloc().getShardMethod().toString())); + + args.append("&").append(encoder.encode("lastUpdated")).append("=").append(encoder.encode("" + shardState.getLastUpdated())); + args.append("&").append(encoder.encode("lastIndexedChangeSetCommitTime")).append("=").append(encoder.encode("" + shardState.getLastIndexedChangeSetCommitTime())); + args.append("&").append(encoder.encode("lastIndexedChangeSetId")).append("=").append(encoder.encode("" + shardState.getLastIndexedChangeSetId())); + args.append("&").append(encoder.encode("lastIndexedTxCommitTime")).append("=").append(encoder.encode("" + shardState.getLastIndexedTxCommitTime())); + args.append("&").append(encoder.encode("lastIndexedTxId")).append("=").append(encoder.encode("" + shardState.getLastIndexedTxId())); + + } + + url.append(args); + LOGGER.debug("### GetRequest: " + url.toString()); + GetRequest req = new GetRequest(url.toString()); + Response response = null; + List transactions = new ArrayList(); + Long maxTxnCommitTime = null; + Long maxTxnIdOnServer = null; + + LookAheadBufferedReader reader = null; + try + { + response = repositoryHttpClient.sendRequest(req); + if(response.getStatus() != HttpStatus.SC_OK) + { + throw new AlfrescoRuntimeException("GetTransactions return status is " + response.getStatus()); + } + + reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); + JsonParser parser = jsonFactory.createParser(reader); + + JsonToken token = parser.nextValue(); + while (token != null) + { + if ("transactions".equals(parser.getCurrentName())) + { + token = parser.nextToken(); //START_ARRAY + while (token == JsonToken.START_OBJECT) + { + token = parser.nextValue(); + long id = parser.getLongValue(); + + token = parser.nextValue(); + long commitTime = parser.getLongValue(); + + token = parser.nextValue(); + long updates = parser.getLongValue(); + + token = parser.nextValue(); + long deletes = parser.getLongValue(); + + Transaction txn = new Transaction(); + txn.setCommitTimeMs(commitTime); + txn.setDeletes(deletes); + txn.setId(id); + txn.setUpdates(updates); + + transactions.add(txn); + + token = parser.nextToken(); //END_OBJECT + token = parser.nextToken(); // START_OBJECT or END_ARRAY; + } + } + else if ("maxTxnCommitTime".equals(parser.getCurrentName())) + { + maxTxnCommitTime = parser.getLongValue(); + } + else if ("maxTxnId".equals(parser.getCurrentName())) + { + maxTxnIdOnServer = parser.getLongValue(); + } + token = parser.nextValue(); + } + parser.close(); + } + catch (JSONException exception) + { + String message = "Received a malformed JSON payload. Request was \"" + + req.getFullUri() + + "Data: " + + ofNullable(reader) + .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) + .orElse("Not available"); + LOGGER.error(message); + throw exception; + } + finally + { + ofNullable(response).ifPresent(Response::release); + ofNullable(reader).ifPresent(this::silentlyClose); + } + + LOGGER.debug("### Transactions found maxTxnCommitTime: " + maxTxnCommitTime ); + return new Transactions(transactions, maxTxnCommitTime, maxTxnIdOnServer); + } + + public List getNodes(GetNodesParameters parameters, int maxResults) throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_NODES_URL); + + JSONObject body = new JSONObject(); + + if(parameters.getTransactionIds() != null) + { + JSONArray jsonTxnIds = new JSONArray(); + for(Long txnId : parameters.getTransactionIds()) + { + jsonTxnIds.put(txnId); + } + body.put("txnIds", jsonTxnIds); + } + + if(parameters.getFromNodeId() != null) + { + body.put("fromNodeId", parameters.getFromNodeId()); + } + if(parameters.getToNodeId() != null) + { + body.put("toNodeId", parameters.getToNodeId()); + } + if(parameters.getExcludeAspects() != null) + { + JSONArray jsonExcludeAspects = new JSONArray(); + for(QName excludeAspect : parameters.getExcludeAspects()) + { + jsonExcludeAspects.put(excludeAspect.toString()); + } + body.put("excludeAspects", jsonExcludeAspects); + } + if(parameters.getIncludeAspects() != null) + { + JSONArray jsonIncludeAspects = new JSONArray(); + for(QName includeAspect : parameters.getIncludeAspects()) + { + jsonIncludeAspects.put(includeAspect.toString()); + } + body.put("includeAspects", jsonIncludeAspects); + } + + if(parameters.getStoreProtocol() != null) + { + body.put("storeProtocol", parameters.getStoreProtocol()); + } + + if(parameters.getStoreIdentifier() != null) + { + body.put("storeIdentifier", parameters.getStoreIdentifier()); + } + + body.put("maxResults", maxResults); + + if(parameters.getShardProperty() != null) + { + body.put("shardProperty", parameters.getShardProperty().toString()); + } + + if (parameters.getCoreName() != null){ + body.put("coreName", parameters.getCoreName()); + } + + + PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); + JSONObject json = callRepository(GET_NODES_URL, req); + + JSONArray jsonNodes = json.getJSONArray("nodes"); + List nodes = new ArrayList<>(jsonNodes.length()); + for(int i = 0; i < jsonNodes.length(); i++) + { + JSONObject jsonNodeInfo = jsonNodes.getJSONObject(i); + Node nodeInfo = new Node(); + if(jsonNodeInfo.has("id")) + { + nodeInfo.setId(jsonNodeInfo.getLong("id")); + } + + if(jsonNodeInfo.has("nodeRef")) + { + nodeInfo.setNodeRef(jsonNodeInfo.getString("nodeRef")); + } + + if(jsonNodeInfo.has("txnId")) + { + nodeInfo.setTxnId(jsonNodeInfo.getLong("txnId")); + } + + if(jsonNodeInfo.has("aclId")) + { + nodeInfo.setAclId(jsonNodeInfo.getLong("aclId")); + } + + if(jsonNodeInfo.has("shardPropertyValue")) + { + nodeInfo.setShardPropertyValue(jsonNodeInfo.getString("shardPropertyValue")); + } + + if(jsonNodeInfo.has("explicitShardId")) + { + nodeInfo.setExplicitShardId(jsonNodeInfo.getInt("explicitShardId")); + } + + if(jsonNodeInfo.has("tenant")) + { + nodeInfo.setTenant(jsonNodeInfo.getString("tenant")); + } + + if(jsonNodeInfo.has("status")) + { + Node.SolrApiNodeStatus status; + String statusStr = jsonNodeInfo.getString("status"); + if(statusStr.equals("u")) + { + status = Node.SolrApiNodeStatus.UPDATED; + } + else if(statusStr.equals("d")) + { + status = Node.SolrApiNodeStatus.DELETED; + } + else + { + status = Node.SolrApiNodeStatus.UNKNOWN; + } + nodeInfo.setStatus(status); + } + + nodes.add(nodeInfo); + } + + return nodes; + } + + private PropertyValue getSinglePropertyValue(DataTypeDefinition dataType, Object value) throws JSONException + { + PropertyValue ret = null; + QName dataTypeName = dataType.getName(); + + if(value == null || value == JSONObject.NULL) + { + ret = null; + } + else if(dataTypeName.equals(DataTypeDefinition.MLTEXT)) + { + JSONArray a = (JSONArray)value; + Map mlValues = new HashMap(a.length()); + + for(int k = 0; k < a.length(); k++) + { + JSONObject pair = a.getJSONObject(k); + Locale locale = deserializer.deserializeValue(Locale.class, pair.getString("locale")); + String mlValue = pair.has("value") && !pair.isNull("value") ? pair.getString("value") : null; + mlValues.put(locale, mlValue); + } + + ret = new MLTextPropertyValue(mlValues); + } + else if(dataTypeName.equals(DataTypeDefinition.CONTENT)) + { + JSONObject o = (JSONObject)value; + + String localeStr = o.has("locale") && !o.isNull("locale") ? o.getString("locale") : null; + Locale locale = (o.has("locale") && !o.isNull("locale") ? deserializer.deserializeValue(Locale.class, localeStr) : null); + + long size = o.has("size") && !o.isNull("size") ? o.getLong("size") : 0; + + String encoding = o.has("encoding") && !o.isNull("encoding") ? o.getString("encoding") : null; + String mimetype = o.has("mimetype") && !o.isNull("mimetype") ? o.getString("mimetype") : null; + + Long id = o.has("contentId") && !o.isNull("contentId") ? o.getLong("contentId") : null; + + ret = new ContentPropertyValue(locale, size, encoding, mimetype, id); + } + else + { + ret = new StringPropertyValue((String)value); + } + + return ret; + } + + private PropertyValue getPropertyValue(PropertyDefinition propertyDef, Object value) throws JSONException + { + PropertyValue ret = null; + + if(value == null || value == JSONObject.NULL) + { + ret = null; + } + else if(propertyDef == null) + { + // assume a string + ret = new StringPropertyValue((String)value); + } + else + { + DataTypeDefinition dataType = propertyDef.getDataType(); + + boolean isMulti = propertyDef.isMultiValued(); + if(isMulti) + { + if(!(value instanceof JSONArray)) + { + throw new IllegalArgumentException("Expected json array, got " + value.getClass().getName()); + } + + MultiPropertyValue multi = new MultiPropertyValue(); + JSONArray array = (JSONArray)value; + for(int j = 0; j < array.length(); j++) + { + multi.addValue(getSinglePropertyValue(dataType, array.get(j))); + } + + ret = multi; + } + else + { + ret = getSinglePropertyValue(dataType, value); + } + } + + return ret; + } + + public List getNodesMetaData(NodeMetaDataParameters params) throws AuthenticationException, IOException, JSONException + { + List nodeIds = params.getNodeIds(); + + StringBuilder url = new StringBuilder(GET_METADATA_URL); + + JSONObject body = new JSONObject(); + if(nodeIds != null && nodeIds.size() > 0) + { + JSONArray jsonNodeIds = new JSONArray(); + for(Long nodeId : nodeIds) + { + jsonNodeIds.put(nodeId); + } + body.put("nodeIds", jsonNodeIds); + + } + if(params.getFromNodeId() != null) + { + body.put("fromNodeId", params.getFromNodeId()); + } + if(params.getToNodeId() != null) + { + body.put("toNodeId", params.getToNodeId()); + } + + // only need to set in cases where we don't want them in the response + // because they default to true + if(!params.isIncludeAclId()) + { + body.put("includeAclId", params.isIncludeAclId()); + } + if(!params.isIncludeAspects()) + { + body.put("includeAspects", params.isIncludeAspects()); + } + if(!params.isIncludeProperties()) + { + body.put("includeProperties", params.isIncludeProperties()); + } + if(!params.isIncludeChildAssociations()) + { + body.put("includeChildAssociations", params.isIncludeChildAssociations()); + } + if(!params.isIncludeParentAssociations()) + { + body.put("includeParentAssociations", params.isIncludeParentAssociations()); + } + if(!params.isIncludeChildIds()) + { + body.put("includeChildIds", params.isIncludeChildIds()); + } + if(!params.isIncludePaths()) + { + body.put("includePaths", params.isIncludePaths()); + } + if(!params.isIncludeOwner()) + { + body.put("includeOwner", params.isIncludeOwner()); + } + if(!params.isIncludeNodeRef()) + { + body.put("includeNodeRef", params.isIncludeNodeRef()); + } + if(!params.isIncludeTxnId()) + { + body.put("includeTxnId", params.isIncludeTxnId()); + } + + if (params.getMaxResults().isPresent()) + { + body.put("maxResults", params.getMaxResults().getAsInt()); + } + + PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); + JSONObject json = callRepository(GET_METADATA_URL, req); + + JSONArray jsonNodes = json.getJSONArray("nodes"); + List nodes = new ArrayList<>(jsonNodes.length()); + for(int i = 0; i < jsonNodes.length(); i++) + { + JSONObject jsonNodeInfo = jsonNodes.getJSONObject(i); + NodeMetaData metaData = new NodeMetaData(); + + if(jsonNodeInfo.has("id")) + { + metaData.setId(jsonNodeInfo.getLong("id")); + } + + if(jsonNodeInfo.has("tenantDomain")) + { + metaData.setTenantDomain(jsonNodeInfo.getString("tenantDomain")); + } + + if(jsonNodeInfo.has("txnId")) + { + metaData.setTxnId(jsonNodeInfo.getLong("txnId")); + } + + if(jsonNodeInfo.has("aclId")) + { + metaData.setAclId(jsonNodeInfo.getLong("aclId")); + } + + if(jsonNodeInfo.has("nodeRef")) + { + metaData.setNodeRef(new NodeRef(jsonNodeInfo.getString("nodeRef"))); + } + + if(jsonNodeInfo.has("type")) + { + metaData.setType(deserializer.deserializeValue(QName.class, jsonNodeInfo.getString("type"))); + } + + if(jsonNodeInfo.has("aspects")) + { + JSONArray jsonAspects = jsonNodeInfo.getJSONArray("aspects"); + Set aspects = new HashSet(jsonAspects.length()); + for(int j = 0; j < jsonAspects.length(); j++) + { + String jsonAspect = (String)jsonAspects.get(j); + aspects.add(deserializer.deserializeValue(QName.class, jsonAspect)); + } + metaData.setAspects(aspects); + } + + if(jsonNodeInfo.has("paths")) + { + JSONArray jsonPaths = jsonNodeInfo.getJSONArray("paths"); + List> paths = new ArrayList>(jsonPaths.length()); + List ancestorPaths = new ArrayList(); + for(int j = 0; j < jsonPaths.length(); j++) + { + JSONObject path = jsonPaths.getJSONObject(j); + String pathValue = path.getString("path"); + QName qname = path.has("qname") ? deserializer.deserializeValue(QName.class, path.getString("qname")) : null; + paths.add(new Pair(pathValue, qname)); + if(path.has("apath")) + { + String ancestorPath = path.getString("apath"); + ancestorPaths.add(ancestorPath); + } + } + metaData.setPaths(paths); + metaData.setAncestorPaths(ancestorPaths); + } + + if(jsonNodeInfo.has("namePaths")) + { + JSONArray jsonNamePaths = jsonNodeInfo.getJSONArray("namePaths"); + List> namePaths = new ArrayList>(jsonNamePaths.length()); + for(int j = 0; j < jsonNamePaths.length(); j++) + { + JSONObject jsonNamePath = jsonNamePaths.getJSONObject(j); + JSONArray jsonNameElements = jsonNamePath.getJSONArray("namePath"); + List namePath = new ArrayList(jsonNameElements.length()); + for(int k = 0; k < jsonNameElements.length(); k++) + { + String namePathElement = jsonNameElements.getString(k); + namePath.add(namePathElement); + } + namePaths.add(namePath); + } + metaData.setNamePaths(namePaths); + } + + if(jsonNodeInfo.has("ancestors")) + { + JSONArray jsonAncestors = jsonNodeInfo.getJSONArray("ancestors"); + HashSet ancestors = new HashSet(jsonAncestors.length()); + for(int j = 0; j < jsonAncestors.length(); j++) + { + String ancestorNodeRefString = jsonAncestors.getString(j); + NodeRef ancestorNodeRef = new NodeRef(ancestorNodeRefString); + ancestors.add(ancestorNodeRef); + } + metaData.setAncestors(ancestors); + } + + if(jsonNodeInfo.has("properties")) + { + JSONObject jsonProperties = jsonNodeInfo.getJSONObject("properties"); + Map properties = new HashMap(jsonProperties.length()); + @SuppressWarnings("rawtypes") + Iterator propKeysIterator = jsonProperties.keys(); + while(propKeysIterator.hasNext()) + { + String propName = (String)propKeysIterator.next(); + QName propQName = deserializer.deserializeValue(QName.class, propName); + Object propValueObj = jsonProperties.opt(propName); + + // check the expected property type to determine how to process the value + PropertyDefinition propertyDef = dictionaryService.getProperty(propQName); +// if(propertyDef == null) +// { +// // TODO which exception here? +// throw new IllegalArgumentException("Could not find property definition for property " + propName); +// } + + properties.put(propQName, getPropertyValue(propertyDef, propValueObj)); + } + metaData.setProperties(properties); + } + + if(jsonNodeInfo.has("parentAssocsCrc")) + { + metaData.setParentAssocsCrc(jsonNodeInfo.getLong("parentAssocsCrc")); + } + + if(jsonNodeInfo.has("parentAssocs")) + { + JSONArray jsonParentAssocs = jsonNodeInfo.getJSONArray("parentAssocs"); + List assocs = new ArrayList(jsonParentAssocs.length()); + for(int j = 0; j < jsonParentAssocs.length(); j++) + { + String childAssocRefStr = jsonParentAssocs.getString(j); + ChildAssociationRef childAssociationRef = new ChildAssociationRef(childAssocRefStr); + assocs.add(childAssociationRef); + } + metaData.setParentAssocs(assocs); + } + + if(jsonNodeInfo.has("childAssocs")) + { + JSONArray jsonParentAssocs = jsonNodeInfo.getJSONArray("childAssocs"); + List assocs = new ArrayList(jsonParentAssocs.length()); + for(int j = 0; j < jsonParentAssocs.length(); j++) + { + String childAssocRefStr = jsonParentAssocs.getString(j); + ChildAssociationRef childAssociationRef = new ChildAssociationRef(childAssocRefStr); + assocs.add(childAssociationRef); + } + metaData.setChildAssocs(assocs); + } + + if(jsonNodeInfo.has("childIds")) + { + JSONArray jsonChildIds = jsonNodeInfo.getJSONArray("childIds"); + List childIds = new ArrayList(jsonChildIds.length()); + for(int j = 0; j < jsonChildIds.length(); j++) + { + Long childId = jsonChildIds.getLong(j); + childIds.add(childId); + } + metaData.setChildIds(childIds); + } + + if(jsonNodeInfo.has("owner")) + { + metaData.setOwner(jsonNodeInfo.getString("owner")); + } + + nodes.add(metaData); + } + + return nodes; + } + + public GetTextContentResponse getTextContent(Long nodeId, QName propertyQName, Long modifiedSince) throws AuthenticationException, IOException { + StringBuilder url = new StringBuilder(128); + url.append(GET_CONTENT); + + StringBuilder args = new StringBuilder(128); + if(nodeId != null) + { + args.append("?"); + args.append("nodeId"); + args.append("="); + args.append(nodeId); + } + else + { + throw new NullPointerException("getTextContent(): nodeId cannot be null."); + } + if(propertyQName != null) + { + if(args.length() == 0) + { + args.append("?"); + } + else + { + args.append("&"); + } + args.append("propertyQName"); + args.append("="); + args.append(URLEncoder.encode(propertyQName.toString())); + } + + url.append(args); + + GetRequest req = new GetRequest(url.toString()); + + Map headers = new HashMap<>(); + if(modifiedSince != null) + { + headers.put("If-Modified-Since", String.valueOf(DateUtil.formatDate(new Date(modifiedSince)))); + } + if (compression) + { + headers.put("Accept-Encoding", "gzip"); + } + req.setHeaders(headers); + + Response response = repositoryHttpClient.sendRequest(req); + + if(response.getStatus() != Status.STATUS_NOT_MODIFIED && response.getStatus() != Status.STATUS_NO_CONTENT && response.getStatus() != Status.STATUS_OK) + { + int status = response.getStatus(); + response.release(); + throw new AlfrescoRuntimeException("GetTextContentResponse return status is " + status); + } + + return new GetTextContentResponse(response); + } + + public AlfrescoModel getModel(String coreName, QName modelName) throws AuthenticationException, IOException + { + // If the model is new to the SOLR side the prefix will be unknown so we can not generate prefixes for the request! + // Always use the full QName with explicit URI + StringBuilder url = new StringBuilder(GET_MODEL); + + URLCodec encoder = new URLCodec(); + // must send the long name as we may not have the prefix registered + url.append("?modelQName=").append(encoder.encode(modelName.toString(), "UTF-8")); + + GetRequest req = new GetRequest(url.toString()); + + Response response = null; + try + { + response = repositoryHttpClient.sendRequest(req); + if(response.getStatus() != HttpStatus.SC_OK) + { + throw new AlfrescoRuntimeException(coreName + " GetModel return status is " + response.getStatus()); + } + + return new AlfrescoModel(M2Model.createModel(response.getContentAsStream()), + Long.valueOf(response.getHeader(CHECKSUM_HEADER))); + } + finally + { + if(response != null) + { + response.release(); + } + } + } + + public List getModelsDiff(String coreName, List currentModels) throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_MODELS_DIFF); + + JSONObject body = new JSONObject(); + JSONArray jsonModels = new JSONArray(); + for(AlfrescoModel model : currentModels) + { + JSONObject jsonModel = new JSONObject(); + QName modelQName = QName.createQName( model.getModel().getName(), namespaceDAO); + jsonModel.put("name", modelQName.toString()); + jsonModel.put("checksum", model.getChecksum()); + jsonModels.put(jsonModel); + } + body.put("models", jsonModels); + + PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); + JSONObject json = callRepository(GET_MODELS_DIFF, req); + + JSONArray jsonDiffs = json.getJSONArray("diffs"); + if(jsonDiffs == null) + { + throw new AlfrescoRuntimeException("GetModelsDiff badly formatted response"); + } + + List diffs = new ArrayList(jsonDiffs.length()); + for(int i = 0; i < jsonDiffs.length(); i++) + { + JSONObject jsonDiff = jsonDiffs.getJSONObject(i); + diffs.add(new AlfrescoModelDiff( + QName.createQName(jsonDiff.getString("name")), + AlfrescoModelDiff.TYPE.valueOf(jsonDiff.getString("type")), + (jsonDiff.isNull("oldChecksum") ? null : jsonDiff.getLong("oldChecksum")), + (jsonDiff.isNull("newChecksum") ? null : jsonDiff.getLong("newChecksum")))); + } + + return diffs; + } + + /** + * Returns the next commit time from a given commit time. + * + * @param coreName alfresco, archive + * @param fromCommitTime initial transaction commit time + * @return Time of the next transaction + * @throws IOException + * @throws AuthenticationException + * @throws NoSuchMethodException + */ + public Long getNextTxCommitTime(String coreName, Long fromCommitTime) throws AuthenticationException, IOException, NoSuchMethodException + { + StringBuilder url = new StringBuilder(GET_NEXT_TX_COMMIT_TIME); + url.append("?").append("fromCommitTime").append("=").append(fromCommitTime); + GetRequest get = new GetRequest(url.toString()); + Response response = null; + JSONObject json = null; + LookAheadBufferedReader reader = null; + try + { + response = repositoryHttpClient.sendRequest(get); + if (response.getStatus() != HttpStatus.SC_OK) + { + throw new NoSuchMethodException(coreName + " - GetNextTxCommitTime return status is " + + response.getStatus() + " when invoking " + url); + } + + reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); + json = new JSONObject(new JSONTokener(reader)); + } + catch (JSONException exception) + { + String message = "Received a malformed JSON payload. Request was \"" + + get.getFullUri() + + "Data: " + + ofNullable(reader) + .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) + .orElse("Not available"); + LOGGER.error(message); + throw exception; + } + finally + { + ofNullable(response).ifPresent(Response::release); + ofNullable(reader).ifPresent(this::silentlyClose); + } + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug(json.toString()); + } + + return Long.parseLong(json.get("nextTransactionCommitTimeMs").toString()); + } + + /** + * Returns the minimum and the maximum commit time for transactions in a node id range. + * + * @param coreName alfresco, archive + * @param fromNodeId Id of the initial node + * @param toNodeId Id of the final node + * @return Time of the first transaction, time of the last transaction + * @throws IOException + * @throws AuthenticationException + * @throws NoSuchMethodException + */ + public Pair getTxIntervalCommitTime(String coreName, Long fromNodeId, Long toNodeId) + throws AuthenticationException, IOException, NoSuchMethodException + { + StringBuilder url = new StringBuilder(GET_TX_INTERVAL_COMMIT_TIME); + url.append("?").append("fromNodeId").append("=").append(fromNodeId); + url.append("&").append("toNodeId").append("=").append(toNodeId); + GetRequest get = new GetRequest(url.toString()); + Response response = null; + JSONObject json = null; + LookAheadBufferedReader reader = null; + try + { + response = repositoryHttpClient.sendRequest(get); + if (response.getStatus() != HttpStatus.SC_OK) + { + throw new NoSuchMethodException(coreName + " - GetTxIntervalCommitTime return status is " + + response.getStatus() + " when invoking " + url); + } + + reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); + json = new JSONObject(new JSONTokener(reader)); + } + catch(JSONException exception) + { + String message = "Received a malformed JSON payload. Request was \"" + + get.getFullUri() + + "Data: " + + ofNullable(reader) + .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) + .orElse("Not available"); + LOGGER.error(message); + throw exception; + } + finally + { + ofNullable(response).ifPresent(Response::release); + ofNullable(reader).ifPresent(this::silentlyClose); + } + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug(json.toString()); + } + + return new Pair(Long.parseLong(json.get("minTransactionCommitTimeMs").toString()), + Long.parseLong(json.get("maxTransactionCommitTimeMs").toString())); + } + + /* + * type conversions from serialized JSON values to SOLR-consumable objects + */ + @SuppressWarnings("rawtypes") + private class SOLRTypeConverter + { + /** + * Default Type Converter + */ + private TypeConverter instance = new TypeConverter(); + private NamespaceDAO namespaceDAO; + + @SuppressWarnings("unchecked") + SOLRTypeConverter(NamespaceDAO namespaceDAO) + { + this.namespaceDAO = namespaceDAO; + + // add all default converters to this converter + for (Entry, Map, Converter>> source : DefaultTypeConverter.INSTANCE.getConverters().entrySet()) + { + for (Entry, Converter> dest : source.getValue().entrySet()) + { + instance.addConverter((Class) source.getKey(), (Class) dest.getKey(), dest.getValue()); + } + } + + // dates + instance.addConverter(String.class, Date.class, new TypeConverter.Converter() + { + public Date convert(String source) + { + try + { + return ISO8601DateFormat.parse(source); + } + catch (Exception e) + { + throw new TypeConversionException("Failed to convert date " + source + " to string", e); + } + } + }); + + // node refs + instance.addConverter(String.class, NodeRef.class, new TypeConverter.Converter() + { + public NodeRef convert(String source) + { + return new NodeRef(source); + } + }); + + // paths + instance.addConverter(String.class, AttributeElement.class, new TypeConverter.Converter() + { + public AttributeElement convert(String source) + { + return new Path.AttributeElement(source); + } + }); + + instance.addConverter(String.class, ChildAssocElement.class, new TypeConverter.Converter() + { + public ChildAssocElement convert(String source) + { + return new Path.ChildAssocElement(instance.convert(ChildAssociationRef.class, source)); + } + }); + + instance.addConverter(String.class, Path.DescendentOrSelfElement.class, new TypeConverter.Converter() + { + public Path.DescendentOrSelfElement convert(String source) + { + return new Path.DescendentOrSelfElement(); + } + }); + + instance.addConverter(String.class, Path.ParentElement.class, new TypeConverter.Converter() + { + public Path.ParentElement convert(String source) + { + return new Path.ParentElement(); + } + }); + + instance.addConverter(String.class, Path.SelfElement.class, new TypeConverter.Converter() + { + public Path.SelfElement convert(String source) + { + return new Path.SelfElement(); + } + }); + + // associations + instance.addConverter(String.class, ChildAssociationRef.class, new TypeConverter.Converter() + { + public ChildAssociationRef convert(String source) + { + return new ChildAssociationRef(source); + } + }); + + instance.addConverter(String.class, AssociationRef.class, new TypeConverter.Converter() + { + public AssociationRef convert(String source) + { + return new AssociationRef(source); + } + }); + + // qnames + instance.addConverter(String.class, QName.class, new TypeConverter.Converter() + { + public QName convert(String source) + { + return QName.resolveToQName(SOLRTypeConverter.this.namespaceDAO, source); + } + }); + + instance.addConverter(String.class, MLText.class, new TypeConverter.Converter() + { + public MLText convert(String source) + { + return new MLText(source); + } + }); + } + + public final T convert(Class c, Object value) + { + return instance.convert(c, value); + } + } + + /* + * Deserializes JSON values from the remote API into objects consumable by SOLR + */ + private class SOLRDeserializer + { + private SOLRTypeConverter typeConverter; + + public SOLRDeserializer(NamespaceDAO namespaceDAO) + { + typeConverter = new SOLRTypeConverter(namespaceDAO); + } + + public T deserializeValue(Class targetClass, Object value) throws JSONException + { + return typeConverter.convert(targetClass, value); + } + } + + private static class SOLRResponse + { + protected Response response; + + public SOLRResponse(Response response) + { + super(); + this.response = response; + } + + public Response getResponse() + { + return response; + } + } + + public static class GetTransactionsResponse extends SOLRResponse + { + private List txns; + + public GetTransactionsResponse(Response response, List txns) + { + super(response); + this.txns = txns; + } + + public List getTransaction() + { + return txns; + } + } + + public static class GetNodesResponse extends SOLRResponse + { + private List nodes; + + public GetNodesResponse(Response response, List nodes) + { + super(response); + this.nodes = nodes; + } + + public List getNodes() + { + return nodes; + } + } + + public static class GetNodesMetaDataResponse extends SOLRResponse + { + private List nodes; + + public GetNodesMetaDataResponse(Response response, List nodes) + { + super(response); + this.nodes = nodes; + } + + public List getNodes() + { + return nodes; + } + } + + public static enum SolrApiContentStatus + { + NOT_MODIFIED, OK, NO_TRANSFORM, NO_CONTENT, UNKNOWN, TRANSFORM_FAILED, GENERAL_FAILURE; + + public static SolrApiContentStatus getStatus(String statusStr) + { + if(statusStr.equals("ok")) + { + return OK; + } + else if(statusStr.equals("transformFailed")) + { + return TRANSFORM_FAILED; + } + else if(statusStr.equals("noTransform")) + { + return NO_TRANSFORM; + } + else if(statusStr.equals("noContent")) + { + return NO_CONTENT; + } + else + { + return UNKNOWN; + } + } + } + + // TODO register a stream close listener that release the response when the response has been read + public static class GetTextContentResponse extends SOLRResponse implements AutoCloseable + { + private InputStream content; + private SolrApiContentStatus status; + private String transformException; + private String transformStatusStr; + private Long transformDuration; + private String contentEncoding; + + public GetTextContentResponse(Response response) throws IOException + { + super(response); + + this.content = response.getContentAsStream(); + this.transformStatusStr = response.getHeader("X-Alfresco-transformStatus"); + this.transformException = response.getHeader("X-Alfresco-transformException"); + String tmp = response.getHeader("X-Alfresco-transformDuration"); + this.transformDuration = (tmp != null ? Long.valueOf(tmp) : null); + this.contentEncoding = response.getHeader("Content-Encoding"); + setStatus(); + } + + public InputStream getContent() + { + return content; + } + + public SolrApiContentStatus getStatus() + { + return status; + } + + private void setStatus() + { + int status = response.getStatus(); + if(status == HttpStatus.SC_NOT_MODIFIED) + { + this.status = SolrApiContentStatus.NOT_MODIFIED; + } + else if(status == HttpStatus.SC_INTERNAL_SERVER_ERROR) + { + this.status = SolrApiContentStatus.GENERAL_FAILURE; + } + else if(status == HttpStatus.SC_OK) + { + this.status = SolrApiContentStatus.OK; + } + else if(status == HttpStatus.SC_NO_CONTENT) + { + if(transformStatusStr == null) + { + this.status = SolrApiContentStatus.UNKNOWN; + } + else + { + if(transformStatusStr.equals("noTransform")) + { + this.status = SolrApiContentStatus.NO_TRANSFORM; + } + else if(transformStatusStr.equals("transformFailed")) + { + this.status = SolrApiContentStatus.TRANSFORM_FAILED; + } + else + { + this.status = SolrApiContentStatus.UNKNOWN; + } + } + } + } + + public String getTransformException() + { + return transformException; + } + + public void release() + { + response.release(); + } + + public Long getTransformDuration() + { + return transformDuration; + } + + public String getContentEncoding() + { + return contentEncoding; + } + + @Override + public void close() { + response.release(); + } + } + + public void close() + { + repositoryHttpClient.close(); + } + + private JSONObject callRepository(String msgId, Request req) throws IOException, AuthenticationException + { + Response response = null; + LookAheadBufferedReader reader = null; + JSONObject json; + try + { + response = repositoryHttpClient.sendRequest(req); + if (response.getStatus() != HttpStatus.SC_OK) + { + throw new AlfrescoRuntimeException(msgId + " return status:" + response.getStatus()); + } + + reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); + json = new JSONObject(new JSONTokener(reader)); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug(json.toString(3)); + } + return json; + } + catch (JSONException exception) + { + String message = "Received a malformed JSON payload. Request was \"" + + req.getFullUri() + + "Data: " + + ofNullable(reader) + .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) + .orElse("Not available"); + LOGGER.error(message); + throw exception; + } + finally + { + ofNullable(response).ifPresent(Response::release); + ofNullable(reader).ifPresent(this::silentlyClose); + } + } + + private void silentlyClose(Closeable closeable) + { + try + { + closeable.close(); + } + catch (Exception ignore) + { + // Nothing to be done here + } + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClientFactory.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClientFactory.java index 98afbac4e..571a7a77d 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClientFactory.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClientFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIQueueClient.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIQueueClient.java index 2e68fe80c..afa992d92 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIQueueClient.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIQueueClient.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Status.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Status.java index 2c21b5541..ab392a6e4 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Status.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Status.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,193 +24,193 @@ * #L% */ -package org.alfresco.solr.client; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * Status code constants for SOLR. - * - * @since 4.0 - */ -public class Status -{ - /* Status code constants */ - public static final int STATUS_CONTINUE = HttpServletResponse.SC_CONTINUE; - public static final int STATUS_SWITCHING_PROTOCOLS = HttpServletResponse.SC_SWITCHING_PROTOCOLS; - public static final int STATUS_OK = HttpServletResponse.SC_OK; - public static final int STATUS_CREATED = HttpServletResponse.SC_CREATED; - public static final int STATUS_ACCEPTED = HttpServletResponse.SC_ACCEPTED; - public static final int STATUS_NON_AUTHORITATIVE_INFORMATION = HttpServletResponse.SC_NON_AUTHORITATIVE_INFORMATION; - public static final int STATUS_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT; - public static final int STATUS_RESET_CONTENT = HttpServletResponse.SC_RESET_CONTENT; - public static final int STATUS_PARTIAL_CONTENT = HttpServletResponse.SC_PARTIAL_CONTENT; - public static final int STATUS_MULTIPLE_CHOICES = HttpServletResponse.SC_MULTIPLE_CHOICES; - public static final int STATUS_MOVED_PERMANENTLY = HttpServletResponse.SC_MOVED_PERMANENTLY; - public static final int STATUS_MOVED_TEMPORARILY = HttpServletResponse.SC_MOVED_TEMPORARILY; - public static final int STATUS_FOUND = HttpServletResponse.SC_FOUND; - public static final int STATUS_SEE_OTHER = HttpServletResponse.SC_SEE_OTHER; - public static final int STATUS_NOT_MODIFIED = HttpServletResponse.SC_NOT_MODIFIED; - public static final int STATUS_USE_PROXY = HttpServletResponse.SC_USE_PROXY; - public static final int STATUS_TEMPORARY_REDIRECT = HttpServletResponse.SC_TEMPORARY_REDIRECT; - public static final int STATUS_BAD_REQUEST = HttpServletResponse.SC_BAD_REQUEST; - public static final int STATUS_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED; - public static final int STATUS_PAYMENT_REQUIRED = HttpServletResponse.SC_PAYMENT_REQUIRED; - public static final int STATUS_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN; - public static final int STATUS_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND; - public static final int STATUS_METHOD_NOT_ALLOWED = HttpServletResponse.SC_METHOD_NOT_ALLOWED; - public static final int STATUS_NOT_ACCEPTABLE = HttpServletResponse.SC_NOT_ACCEPTABLE; - public static final int STATUS_PROXY_AUTHENTICATION_REQUIRED = HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED; - public static final int STATUS_REQUEST_TIMEOUT = HttpServletResponse.SC_REQUEST_TIMEOUT; - public static final int STATUS_CONFLICT = HttpServletResponse.SC_CONFLICT; - public static final int STATUS_GONE = HttpServletResponse.SC_GONE; - public static final int STATUS_LENGTH_REQUIRED = HttpServletResponse.SC_LENGTH_REQUIRED; - public static final int STATUS_PRECONDITION_FAILED = HttpServletResponse.SC_PRECONDITION_FAILED; - public static final int STATUS_REQUEST_ENTITY_TOO_LARGE = HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE; - public static final int STATUS_REQUEST_URI_TOO_LONG = HttpServletResponse.SC_REQUEST_URI_TOO_LONG; - public static final int STATUS_UNSUPPORTED_MEDIA_TYPE = HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; - public static final int STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE; - public static final int STATUS_EXPECTATION_FAILED = HttpServletResponse.SC_EXPECTATION_FAILED; - public static final int STATUS_INTERNAL_SERVER_ERROR = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - public static final int STATUS_NOT_IMPLEMENTED = HttpServletResponse.SC_NOT_IMPLEMENTED; - public static final int STATUS_BAD_GATEWAY = HttpServletResponse.SC_BAD_GATEWAY; - public static final int STATUS_SERVICE_UNAVAILABLE = HttpServletResponse.SC_SERVICE_UNAVAILABLE; - public static final int STATUS_GATEWAY_TIMEOUT = HttpServletResponse.SC_GATEWAY_TIMEOUT; - public static final int STATUS_HTTP_VERSION_NOT_SUPPORTED = HttpServletResponse.SC_HTTP_VERSION_NOT_SUPPORTED; - - - private Throwable exception = null; - private String location = ""; - private int code = HttpServletResponse.SC_OK; - private String message = ""; - private boolean redirect = false; - - /** - * Helper method to set the code and message. - *

- * Redirect is set to true. - * - * @param code code - * @param message message - */ - public void setCode(int code, String message) - { - this.code = code; - this.message = message; - this.redirect = true; - } - - /** - * @param exception Throwable - */ - public void setException(Throwable exception) - { - this.exception = exception; - } - - /** - * @return exception - */ - public Throwable getException() - { - return exception; - } - - /** - * @param message String - */ - public void setMessage(String message) - { - this.message = message; - } - - /** - * @return message - */ - public String getMessage() - { - return message; - } - - /** - * @param redirect redirect to status code response - */ - public void setRedirect(boolean redirect) - { - this.redirect = redirect; - } - - /** - * @return redirect to status code response - */ - public boolean getRedirect() - { - return redirect; - } - - /** - * @see javax.servlet.http.HTTPServletResponse - * - * @param code status code - */ - public void setCode(int code) - { - this.code = code; - } - - /** - * @return status code - */ - public int getCode() - { - return code; - } - - /** - * Gets the short name of the status code - * - * @return status code name - */ - public String getCodeName() - { - String codeName = I18NUtil.getMessage("webscript.code." + code + ".name"); - return codeName == null ? "" : codeName; - } - - /** - * @see javax.servlet.http.HTTPServletResponse - * - * @param location location response-header - */ - public void setLocation(String location) - { - this.location = location; - } - - /** - * @return location - */ - public String getLocation() - { - return location; - } - - /** - * Gets the description of the status code - * - * @return status code description - */ - public String getCodeDescription() - { - String codeDescription = I18NUtil.getMessage("webscript.code." + code + ".description"); - return codeDescription == null ? "" : codeDescription; - } - - @Override - public String toString() - { - return Integer.toString(code); - } -} +package org.alfresco.solr.client; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Status code constants for SOLR. + * + * @since 4.0 + */ +public class Status +{ + /* Status code constants */ + public static final int STATUS_CONTINUE = HttpServletResponse.SC_CONTINUE; + public static final int STATUS_SWITCHING_PROTOCOLS = HttpServletResponse.SC_SWITCHING_PROTOCOLS; + public static final int STATUS_OK = HttpServletResponse.SC_OK; + public static final int STATUS_CREATED = HttpServletResponse.SC_CREATED; + public static final int STATUS_ACCEPTED = HttpServletResponse.SC_ACCEPTED; + public static final int STATUS_NON_AUTHORITATIVE_INFORMATION = HttpServletResponse.SC_NON_AUTHORITATIVE_INFORMATION; + public static final int STATUS_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT; + public static final int STATUS_RESET_CONTENT = HttpServletResponse.SC_RESET_CONTENT; + public static final int STATUS_PARTIAL_CONTENT = HttpServletResponse.SC_PARTIAL_CONTENT; + public static final int STATUS_MULTIPLE_CHOICES = HttpServletResponse.SC_MULTIPLE_CHOICES; + public static final int STATUS_MOVED_PERMANENTLY = HttpServletResponse.SC_MOVED_PERMANENTLY; + public static final int STATUS_MOVED_TEMPORARILY = HttpServletResponse.SC_MOVED_TEMPORARILY; + public static final int STATUS_FOUND = HttpServletResponse.SC_FOUND; + public static final int STATUS_SEE_OTHER = HttpServletResponse.SC_SEE_OTHER; + public static final int STATUS_NOT_MODIFIED = HttpServletResponse.SC_NOT_MODIFIED; + public static final int STATUS_USE_PROXY = HttpServletResponse.SC_USE_PROXY; + public static final int STATUS_TEMPORARY_REDIRECT = HttpServletResponse.SC_TEMPORARY_REDIRECT; + public static final int STATUS_BAD_REQUEST = HttpServletResponse.SC_BAD_REQUEST; + public static final int STATUS_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED; + public static final int STATUS_PAYMENT_REQUIRED = HttpServletResponse.SC_PAYMENT_REQUIRED; + public static final int STATUS_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN; + public static final int STATUS_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND; + public static final int STATUS_METHOD_NOT_ALLOWED = HttpServletResponse.SC_METHOD_NOT_ALLOWED; + public static final int STATUS_NOT_ACCEPTABLE = HttpServletResponse.SC_NOT_ACCEPTABLE; + public static final int STATUS_PROXY_AUTHENTICATION_REQUIRED = HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED; + public static final int STATUS_REQUEST_TIMEOUT = HttpServletResponse.SC_REQUEST_TIMEOUT; + public static final int STATUS_CONFLICT = HttpServletResponse.SC_CONFLICT; + public static final int STATUS_GONE = HttpServletResponse.SC_GONE; + public static final int STATUS_LENGTH_REQUIRED = HttpServletResponse.SC_LENGTH_REQUIRED; + public static final int STATUS_PRECONDITION_FAILED = HttpServletResponse.SC_PRECONDITION_FAILED; + public static final int STATUS_REQUEST_ENTITY_TOO_LARGE = HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE; + public static final int STATUS_REQUEST_URI_TOO_LONG = HttpServletResponse.SC_REQUEST_URI_TOO_LONG; + public static final int STATUS_UNSUPPORTED_MEDIA_TYPE = HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; + public static final int STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE; + public static final int STATUS_EXPECTATION_FAILED = HttpServletResponse.SC_EXPECTATION_FAILED; + public static final int STATUS_INTERNAL_SERVER_ERROR = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; + public static final int STATUS_NOT_IMPLEMENTED = HttpServletResponse.SC_NOT_IMPLEMENTED; + public static final int STATUS_BAD_GATEWAY = HttpServletResponse.SC_BAD_GATEWAY; + public static final int STATUS_SERVICE_UNAVAILABLE = HttpServletResponse.SC_SERVICE_UNAVAILABLE; + public static final int STATUS_GATEWAY_TIMEOUT = HttpServletResponse.SC_GATEWAY_TIMEOUT; + public static final int STATUS_HTTP_VERSION_NOT_SUPPORTED = HttpServletResponse.SC_HTTP_VERSION_NOT_SUPPORTED; + + + private Throwable exception = null; + private String location = ""; + private int code = HttpServletResponse.SC_OK; + private String message = ""; + private boolean redirect = false; + + /** + * Helper method to set the code and message. + *

+ * Redirect is set to true. + * + * @param code code + * @param message message + */ + public void setCode(int code, String message) + { + this.code = code; + this.message = message; + this.redirect = true; + } + + /** + * @param exception Throwable + */ + public void setException(Throwable exception) + { + this.exception = exception; + } + + /** + * @return exception + */ + public Throwable getException() + { + return exception; + } + + /** + * @param message String + */ + public void setMessage(String message) + { + this.message = message; + } + + /** + * @return message + */ + public String getMessage() + { + return message; + } + + /** + * @param redirect redirect to status code response + */ + public void setRedirect(boolean redirect) + { + this.redirect = redirect; + } + + /** + * @return redirect to status code response + */ + public boolean getRedirect() + { + return redirect; + } + + /** + * @see javax.servlet.http.HTTPServletResponse + * + * @param code status code + */ + public void setCode(int code) + { + this.code = code; + } + + /** + * @return status code + */ + public int getCode() + { + return code; + } + + /** + * Gets the short name of the status code + * + * @return status code name + */ + public String getCodeName() + { + String codeName = I18NUtil.getMessage("webscript.code." + code + ".name"); + return codeName == null ? "" : codeName; + } + + /** + * @see javax.servlet.http.HTTPServletResponse + * + * @param location location response-header + */ + public void setLocation(String location) + { + this.location = location; + } + + /** + * @return location + */ + public String getLocation() + { + return location; + } + + /** + * Gets the description of the status code + * + * @return status code description + */ + public String getCodeDescription() + { + String codeDescription = I18NUtil.getMessage("webscript.code." + code + ".description"); + return codeDescription == null ? "" : codeDescription; + } + + @Override + public String toString() + { + return Integer.toString(code); + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/StringPropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/StringPropertyValue.java index ae6b6d236..212da7461 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/StringPropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/StringPropertyValue.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,31 +24,31 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * Represents a property value as a string - * - * @since 4.0 - */ -public class StringPropertyValue extends PropertyValue -{ - private String value; - - public StringPropertyValue(String value) - { - super(); - this.value = value; - } - - public String getValue() - { - return value; - } - - @Override - public String toString() - { - return "StringPropertyValue [value=" + value + "]"; - } -} +package org.alfresco.solr.client; + +/** + * Represents a property value as a string + * + * @since 4.0 + */ +public class StringPropertyValue extends PropertyValue +{ + private String value; + + public StringPropertyValue(String value) + { + super(); + this.value = value; + } + + public String getValue() + { + return value; + } + + @Override + public String toString() + { + return "StringPropertyValue [value=" + value + "]"; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transaction.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transaction.java index 7e253de8e..19ed9adfe 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transaction.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transaction.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,89 +24,89 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * SOLR-side representation of transaction information. - * - * @since 4.0 - */ -public class Transaction -{ - private long id; - private long commitTimeMs; - private long updates; - private long deletes; - - public long getId() - { - return id; - } - public void setId(long id) - { - this.id = id; - } - public long getCommitTimeMs() - { - return commitTimeMs; - } - public void setCommitTimeMs(long commitTimeMs) - { - this.commitTimeMs = commitTimeMs; - } - public long getUpdates() - { - return updates; - } - public void setUpdates(long updates) - { - this.updates = updates; - } - public long getDeletes() - { - return deletes; - } - public void setDeletes(long deletes) - { - this.deletes = deletes; - } - @Override - public String toString() - { - return "Transaction [id=" + id + ", commitTimeMs=" + commitTimeMs + ", updates=" + updates + ", deletes=" - + deletes + "]"; - } - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + (int) (commitTimeMs ^ (commitTimeMs >>> 32)); - result = prime * result + (int) (deletes ^ (deletes >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (int) (updates ^ (updates >>> 32)); - return result; - } - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Transaction other = (Transaction) obj; - if (commitTimeMs != other.commitTimeMs) - return false; - if (deletes != other.deletes) - return false; - if (id != other.id) - return false; - if (updates != other.updates) - return false; - return true; - } - - -} +package org.alfresco.solr.client; + +/** + * SOLR-side representation of transaction information. + * + * @since 4.0 + */ +public class Transaction +{ + private long id; + private long commitTimeMs; + private long updates; + private long deletes; + + public long getId() + { + return id; + } + public void setId(long id) + { + this.id = id; + } + public long getCommitTimeMs() + { + return commitTimeMs; + } + public void setCommitTimeMs(long commitTimeMs) + { + this.commitTimeMs = commitTimeMs; + } + public long getUpdates() + { + return updates; + } + public void setUpdates(long updates) + { + this.updates = updates; + } + public long getDeletes() + { + return deletes; + } + public void setDeletes(long deletes) + { + this.deletes = deletes; + } + @Override + public String toString() + { + return "Transaction [id=" + id + ", commitTimeMs=" + commitTimeMs + ", updates=" + updates + ", deletes=" + + deletes + "]"; + } + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (int) (commitTimeMs ^ (commitTimeMs >>> 32)); + result = prime * result + (int) (deletes ^ (deletes >>> 32)); + result = prime * result + (int) (id ^ (id >>> 32)); + result = prime * result + (int) (updates ^ (updates >>> 32)); + return result; + } + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Transaction other = (Transaction) obj; + if (commitTimeMs != other.commitTimeMs) + return false; + if (deletes != other.deletes) + return false; + if (id != other.id) + return false; + if (updates != other.updates) + return false; + return true; + } + + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transactions.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transactions.java index 73c6da2ca..be6cdf25e 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transactions.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transactions.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,28 +24,28 @@ * #L% */ -package org.alfresco.solr.client; - +package org.alfresco.solr.client; + import java.util.Comparator; -import java.util.List; - -/** - * @author Andy - * - */ -public class Transactions -{ - private List transactions; - - private Long maxTxnCommitTime; - - private Long maxTxnId; - - public Transactions(List transactions, Long maxTxnCommitTime, Long maxTxnId) - { - this.transactions = transactions; - this.maxTxnCommitTime = maxTxnCommitTime; - this.maxTxnId = maxTxnId; +import java.util.List; + +/** + * @author Andy + * + */ +public class Transactions +{ + private List transactions; + + private Long maxTxnCommitTime; + + private Long maxTxnId; + + public Transactions(List transactions, Long maxTxnCommitTime, Long maxTxnId) + { + this.transactions = transactions; + this.maxTxnCommitTime = maxTxnCommitTime; + this.maxTxnId = maxTxnId; } public Transactions(List transactions) @@ -59,20 +59,20 @@ public class Transactions .max(Comparator.comparing(Transaction::getId)).get().getId(); } } - - public List getTransactions() - { - return transactions; - } - - public Long getMaxTxnCommitTime() - { - return maxTxnCommitTime; - } - - public Long getMaxTxnId() - { - return maxTxnId; - } - -} + + public List getTransactions() + { + return transactions; + } + + public Long getMaxTxnCommitTime() + { + return maxTxnCommitTime; + } + + public Long getMaxTxnId() + { + return maxTxnId; + } + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/IndexHealthReport.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/IndexHealthReport.java index 2edf71054..14539177b 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/IndexHealthReport.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/IndexHealthReport.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,412 +24,412 @@ * #L% */ -package org.alfresco.solr.tracker; - -import org.alfresco.solr.InformationServerCollectionProvider; -import org.alfresco.solr.adapters.IOpenBitSet; - - -public class IndexHealthReport -{ - long dbTransactionCount; - - IOpenBitSet missingTxFromIndex; - - IOpenBitSet duplicatedTxInIndex; - - IOpenBitSet txInIndexButNotInDb; - - IOpenBitSet duplicatedLeafInIndex; - - IOpenBitSet duplicatedAuxInIndex; - - long transactionDocsInIndex; - - long uniqueTransactionDocsInIndex; - - long uniqueAclTransactionDocsInIndex; - - long aclTransactionDocsInIndex; - - long leafDocCountInIndex; - - long auxDocCountInIndex; - - long lastIndexedCommitTime; - - long lastIndexedIdBeforeHoles; - - long dbAclTransactionCount; - - IOpenBitSet missingAclTxFromIndex; - - IOpenBitSet duplicatedAclTxInIndex; - - IOpenBitSet aclTxInIndexButNotInDb; - - IOpenBitSet duplicatedErrorInIndex; - - IOpenBitSet duplicatedUnindexedInIndex; - - long errorDocCountInIndex; - - long unindexedDocCountInIndex; - - public IndexHealthReport(InformationServerCollectionProvider srv) - { - this.missingTxFromIndex = srv.getOpenBitSetInstance(); - this.duplicatedTxInIndex = srv.getOpenBitSetInstance(); - this.txInIndexButNotInDb = srv.getOpenBitSetInstance(); - this.duplicatedLeafInIndex = srv.getOpenBitSetInstance(); - this.duplicatedAuxInIndex = srv.getOpenBitSetInstance(); - - this.missingAclTxFromIndex = srv.getOpenBitSetInstance(); - this.duplicatedAclTxInIndex = srv.getOpenBitSetInstance(); - this.aclTxInIndexButNotInDb = srv.getOpenBitSetInstance(); - this.duplicatedErrorInIndex = srv.getOpenBitSetInstance(); - this.duplicatedUnindexedInIndex = srv.getOpenBitSetInstance(); - } - - /** - * @return the transactionDocsInIndex - */ - public long getTransactionDocsInIndex() - { - return transactionDocsInIndex; - } - - public long getAclTransactionDocsInIndex() - { - return aclTransactionDocsInIndex; - } - - /** - * @param leafDocCountInIndex long - */ - public void setLeafDocCountInIndex(long leafDocCountInIndex) - { - this.leafDocCountInIndex = leafDocCountInIndex; - } - - /** - * @return the leafDocCountInIndex - */ - public long getLeafDocCountInIndex() - { - return leafDocCountInIndex; - } - - /** - * @param auxDocCountInIndex long - */ - public void setAuxDocCountInIndex(long auxDocCountInIndex) - { - this.auxDocCountInIndex = auxDocCountInIndex; - } - - /** - * @return the leafDocCountInIndex - */ - public long getAuxDocCountInIndex() - { - return auxDocCountInIndex; - } - - /** - * @param txid long - */ - public void setDuplicatedLeafInIndex(long txid) - { - duplicatedLeafInIndex.set(txid); - - } - - /** - * @return the duplicatedLeafInIndex - */ - public IOpenBitSet getDuplicatedLeafInIndex() - { - return duplicatedLeafInIndex; - } - - /** - * @param txid long - */ - public void setDuplicatedAuxInIndex(long txid) - { - duplicatedAuxInIndex.set(txid); - - } - - /** - * @return the duplicatedLeafInIndex - */ - public IOpenBitSet getDuplicatedAuxInIndex() - { - return duplicatedAuxInIndex; - } - - /** - * @param transactionDocsInIndex - * the transactionDocsInIndex to set - */ - public void setTransactionDocsInIndex(long transactionDocsInIndex) - { - this.transactionDocsInIndex = transactionDocsInIndex; - } - - /** - * @param aclTransactionDocsInIndex - * the transactionDocsInIndex to set - */ - public void setAclTransactionDocsInIndex(long aclTransactionDocsInIndex) - { - this.aclTransactionDocsInIndex = aclTransactionDocsInIndex; - } - - /** - * @return the missingFromIndex - */ - public IOpenBitSet getMissingTxFromIndex() - { - return missingTxFromIndex; - } - - /** - * @return the missingFromIndex - */ - public IOpenBitSet getMissingAclTxFromIndex() - { - return missingAclTxFromIndex; - } - - /** - * @return the duplicatedInIndex - */ - public IOpenBitSet getDuplicatedTxInIndex() - { - return duplicatedTxInIndex; - } - - /** - * @return the duplicatedInIndex - */ - public IOpenBitSet getDuplicatedAclTxInIndex() - { - return duplicatedAclTxInIndex; - } - - /** - * @return the inIndexButNotInDb - */ - public IOpenBitSet getTxInIndexButNotInDb() - { - return txInIndexButNotInDb; - } - - /** - * @return the inIndexButNotInDb - */ - public IOpenBitSet getAclTxInIndexButNotInDb() - { - return aclTxInIndexButNotInDb; - } - - /** - * @return the dbTransactionCount - */ - public long getDbTransactionCount() - { - return dbTransactionCount; - } - - /** - * @param dbTransactionCount - * the dbTransactionCount to set - */ - public void setDbTransactionCount(long dbTransactionCount) - { - this.dbTransactionCount = dbTransactionCount; - } - - public void setMissingTxFromIndex(long txid) - { - missingTxFromIndex.set(txid); - } - - public void setMissingAclTxFromIndex(long txid) - { - missingAclTxFromIndex.set(txid); - } - - public void setDuplicatedTxInIndex(long txid) - { - duplicatedTxInIndex.set(txid); - } - - public void setDuplicatedAclTxInIndex(long txid) - { - duplicatedAclTxInIndex.set(txid); - } - - public void setTxInIndexButNotInDb(long txid) - { - txInIndexButNotInDb.set(txid); - } - - public void setAclTxInIndexButNotInDb(long txid) - { - aclTxInIndexButNotInDb.set(txid); - } - - /** - * @param lastIndexedCommitTime - * the lastIndexCommitTime to set - */ - public void setLastIndexedCommitTime(long lastIndexedCommitTime) - { - this.lastIndexedCommitTime = lastIndexedCommitTime; - } - - /** - * @return the lastIndexedIdBeforeHoles - */ - public long getLastIndexedIdBeforeHoles() - { - return lastIndexedIdBeforeHoles; - } - - /** - * @param lastIndexedIdBeforeHoles - * the lastIndexedIdBeforeHoles to set - */ - public void setLastIndexedIdBeforeHoles(long lastIndexedIdBeforeHoles) - { - this.lastIndexedIdBeforeHoles = lastIndexedIdBeforeHoles; - } - - /** - * @param dbAclTransactionCount long - */ - public void setDbAclTransactionCount(long dbAclTransactionCount) - { - this.dbAclTransactionCount = dbAclTransactionCount; - } - - /** - * @return the dbAclTransactionCount - */ - public long getDbAclTransactionCount() - { - return dbAclTransactionCount; - } - - /** - * @return the uniqueTransactionDocsInIndex - */ - public long getUniqueTransactionDocsInIndex() - { - return uniqueTransactionDocsInIndex; - } - - /** - * @param uniqueTransactionDocsInIndex the uniqueTransactionDocsInIndex to set - */ - public void setUniqueTransactionDocsInIndex(long uniqueTransactionDocsInIndex) - { - this.uniqueTransactionDocsInIndex = uniqueTransactionDocsInIndex; - } - - /** - * @return the uniqueAclTransactionDocsInIndex - */ - public long getUniqueAclTransactionDocsInIndex() - { - return uniqueAclTransactionDocsInIndex; - } - - /** - * @param uniqueAclTransactionDocsInIndex the uniqueAclTransactionDocsInIndex to set - */ - public void setUniqueAclTransactionDocsInIndex(long uniqueAclTransactionDocsInIndex) - { - this.uniqueAclTransactionDocsInIndex = uniqueAclTransactionDocsInIndex; - } - - /** - * @return the lastIndexedCommitTime - */ - public long getLastIndexedCommitTime() - { - return lastIndexedCommitTime; - } - - /** - * @param unindexedDocCountInIndex long - */ - public void setUnindexedDocCountInIndex(long unindexedDocCountInIndex) - { - this.unindexedDocCountInIndex = unindexedDocCountInIndex; - } - - /** - * @param dbId long - */ - public void setDuplicatedUnindexedInIndex(long dbId) - { - duplicatedUnindexedInIndex.set(dbId); - } - - /** - * @param errorDocCountInIndex long - */ - public void setErrorDocCountInIndex(long errorDocCountInIndex) - { - this.errorDocCountInIndex = errorDocCountInIndex; - } - - /** - * @param dbId long - */ - public void setDuplicatedErrorInIndex(long dbId) - { - duplicatedErrorInIndex.set(dbId); - - } - - /** - * @return the duplicatedErrorInIndex - */ - public IOpenBitSet getDuplicatedErrorInIndex() - { - return duplicatedErrorInIndex; - } - - /** - * @return the duplicatedUnindexedInIndex - */ - public IOpenBitSet getDuplicatedUnindexedInIndex() - { - return duplicatedUnindexedInIndex; - } - - /** - * @return the errorDocCountInIndex - */ - public long getErrorDocCountInIndex() - { - return errorDocCountInIndex; - } - - /** - * @return the unindexedDocCountInIndex - */ - public long getUnindexedDocCountInIndex() - { - return unindexedDocCountInIndex; - } - - - -} \ No newline at end of file +package org.alfresco.solr.tracker; + +import org.alfresco.solr.InformationServerCollectionProvider; +import org.alfresco.solr.adapters.IOpenBitSet; + + +public class IndexHealthReport +{ + long dbTransactionCount; + + IOpenBitSet missingTxFromIndex; + + IOpenBitSet duplicatedTxInIndex; + + IOpenBitSet txInIndexButNotInDb; + + IOpenBitSet duplicatedLeafInIndex; + + IOpenBitSet duplicatedAuxInIndex; + + long transactionDocsInIndex; + + long uniqueTransactionDocsInIndex; + + long uniqueAclTransactionDocsInIndex; + + long aclTransactionDocsInIndex; + + long leafDocCountInIndex; + + long auxDocCountInIndex; + + long lastIndexedCommitTime; + + long lastIndexedIdBeforeHoles; + + long dbAclTransactionCount; + + IOpenBitSet missingAclTxFromIndex; + + IOpenBitSet duplicatedAclTxInIndex; + + IOpenBitSet aclTxInIndexButNotInDb; + + IOpenBitSet duplicatedErrorInIndex; + + IOpenBitSet duplicatedUnindexedInIndex; + + long errorDocCountInIndex; + + long unindexedDocCountInIndex; + + public IndexHealthReport(InformationServerCollectionProvider srv) + { + this.missingTxFromIndex = srv.getOpenBitSetInstance(); + this.duplicatedTxInIndex = srv.getOpenBitSetInstance(); + this.txInIndexButNotInDb = srv.getOpenBitSetInstance(); + this.duplicatedLeafInIndex = srv.getOpenBitSetInstance(); + this.duplicatedAuxInIndex = srv.getOpenBitSetInstance(); + + this.missingAclTxFromIndex = srv.getOpenBitSetInstance(); + this.duplicatedAclTxInIndex = srv.getOpenBitSetInstance(); + this.aclTxInIndexButNotInDb = srv.getOpenBitSetInstance(); + this.duplicatedErrorInIndex = srv.getOpenBitSetInstance(); + this.duplicatedUnindexedInIndex = srv.getOpenBitSetInstance(); + } + + /** + * @return the transactionDocsInIndex + */ + public long getTransactionDocsInIndex() + { + return transactionDocsInIndex; + } + + public long getAclTransactionDocsInIndex() + { + return aclTransactionDocsInIndex; + } + + /** + * @param leafDocCountInIndex long + */ + public void setLeafDocCountInIndex(long leafDocCountInIndex) + { + this.leafDocCountInIndex = leafDocCountInIndex; + } + + /** + * @return the leafDocCountInIndex + */ + public long getLeafDocCountInIndex() + { + return leafDocCountInIndex; + } + + /** + * @param auxDocCountInIndex long + */ + public void setAuxDocCountInIndex(long auxDocCountInIndex) + { + this.auxDocCountInIndex = auxDocCountInIndex; + } + + /** + * @return the leafDocCountInIndex + */ + public long getAuxDocCountInIndex() + { + return auxDocCountInIndex; + } + + /** + * @param txid long + */ + public void setDuplicatedLeafInIndex(long txid) + { + duplicatedLeafInIndex.set(txid); + + } + + /** + * @return the duplicatedLeafInIndex + */ + public IOpenBitSet getDuplicatedLeafInIndex() + { + return duplicatedLeafInIndex; + } + + /** + * @param txid long + */ + public void setDuplicatedAuxInIndex(long txid) + { + duplicatedAuxInIndex.set(txid); + + } + + /** + * @return the duplicatedLeafInIndex + */ + public IOpenBitSet getDuplicatedAuxInIndex() + { + return duplicatedAuxInIndex; + } + + /** + * @param transactionDocsInIndex + * the transactionDocsInIndex to set + */ + public void setTransactionDocsInIndex(long transactionDocsInIndex) + { + this.transactionDocsInIndex = transactionDocsInIndex; + } + + /** + * @param aclTransactionDocsInIndex + * the transactionDocsInIndex to set + */ + public void setAclTransactionDocsInIndex(long aclTransactionDocsInIndex) + { + this.aclTransactionDocsInIndex = aclTransactionDocsInIndex; + } + + /** + * @return the missingFromIndex + */ + public IOpenBitSet getMissingTxFromIndex() + { + return missingTxFromIndex; + } + + /** + * @return the missingFromIndex + */ + public IOpenBitSet getMissingAclTxFromIndex() + { + return missingAclTxFromIndex; + } + + /** + * @return the duplicatedInIndex + */ + public IOpenBitSet getDuplicatedTxInIndex() + { + return duplicatedTxInIndex; + } + + /** + * @return the duplicatedInIndex + */ + public IOpenBitSet getDuplicatedAclTxInIndex() + { + return duplicatedAclTxInIndex; + } + + /** + * @return the inIndexButNotInDb + */ + public IOpenBitSet getTxInIndexButNotInDb() + { + return txInIndexButNotInDb; + } + + /** + * @return the inIndexButNotInDb + */ + public IOpenBitSet getAclTxInIndexButNotInDb() + { + return aclTxInIndexButNotInDb; + } + + /** + * @return the dbTransactionCount + */ + public long getDbTransactionCount() + { + return dbTransactionCount; + } + + /** + * @param dbTransactionCount + * the dbTransactionCount to set + */ + public void setDbTransactionCount(long dbTransactionCount) + { + this.dbTransactionCount = dbTransactionCount; + } + + public void setMissingTxFromIndex(long txid) + { + missingTxFromIndex.set(txid); + } + + public void setMissingAclTxFromIndex(long txid) + { + missingAclTxFromIndex.set(txid); + } + + public void setDuplicatedTxInIndex(long txid) + { + duplicatedTxInIndex.set(txid); + } + + public void setDuplicatedAclTxInIndex(long txid) + { + duplicatedAclTxInIndex.set(txid); + } + + public void setTxInIndexButNotInDb(long txid) + { + txInIndexButNotInDb.set(txid); + } + + public void setAclTxInIndexButNotInDb(long txid) + { + aclTxInIndexButNotInDb.set(txid); + } + + /** + * @param lastIndexedCommitTime + * the lastIndexCommitTime to set + */ + public void setLastIndexedCommitTime(long lastIndexedCommitTime) + { + this.lastIndexedCommitTime = lastIndexedCommitTime; + } + + /** + * @return the lastIndexedIdBeforeHoles + */ + public long getLastIndexedIdBeforeHoles() + { + return lastIndexedIdBeforeHoles; + } + + /** + * @param lastIndexedIdBeforeHoles + * the lastIndexedIdBeforeHoles to set + */ + public void setLastIndexedIdBeforeHoles(long lastIndexedIdBeforeHoles) + { + this.lastIndexedIdBeforeHoles = lastIndexedIdBeforeHoles; + } + + /** + * @param dbAclTransactionCount long + */ + public void setDbAclTransactionCount(long dbAclTransactionCount) + { + this.dbAclTransactionCount = dbAclTransactionCount; + } + + /** + * @return the dbAclTransactionCount + */ + public long getDbAclTransactionCount() + { + return dbAclTransactionCount; + } + + /** + * @return the uniqueTransactionDocsInIndex + */ + public long getUniqueTransactionDocsInIndex() + { + return uniqueTransactionDocsInIndex; + } + + /** + * @param uniqueTransactionDocsInIndex the uniqueTransactionDocsInIndex to set + */ + public void setUniqueTransactionDocsInIndex(long uniqueTransactionDocsInIndex) + { + this.uniqueTransactionDocsInIndex = uniqueTransactionDocsInIndex; + } + + /** + * @return the uniqueAclTransactionDocsInIndex + */ + public long getUniqueAclTransactionDocsInIndex() + { + return uniqueAclTransactionDocsInIndex; + } + + /** + * @param uniqueAclTransactionDocsInIndex the uniqueAclTransactionDocsInIndex to set + */ + public void setUniqueAclTransactionDocsInIndex(long uniqueAclTransactionDocsInIndex) + { + this.uniqueAclTransactionDocsInIndex = uniqueAclTransactionDocsInIndex; + } + + /** + * @return the lastIndexedCommitTime + */ + public long getLastIndexedCommitTime() + { + return lastIndexedCommitTime; + } + + /** + * @param unindexedDocCountInIndex long + */ + public void setUnindexedDocCountInIndex(long unindexedDocCountInIndex) + { + this.unindexedDocCountInIndex = unindexedDocCountInIndex; + } + + /** + * @param dbId long + */ + public void setDuplicatedUnindexedInIndex(long dbId) + { + duplicatedUnindexedInIndex.set(dbId); + } + + /** + * @param errorDocCountInIndex long + */ + public void setErrorDocCountInIndex(long errorDocCountInIndex) + { + this.errorDocCountInIndex = errorDocCountInIndex; + } + + /** + * @param dbId long + */ + public void setDuplicatedErrorInIndex(long dbId) + { + duplicatedErrorInIndex.set(dbId); + + } + + /** + * @return the duplicatedErrorInIndex + */ + public IOpenBitSet getDuplicatedErrorInIndex() + { + return duplicatedErrorInIndex; + } + + /** + * @return the duplicatedUnindexedInIndex + */ + public IOpenBitSet getDuplicatedUnindexedInIndex() + { + return duplicatedUnindexedInIndex; + } + + /** + * @return the errorDocCountInIndex + */ + public long getErrorDocCountInIndex() + { + return errorDocCountInIndex; + } + + /** + * @return the unindexedDocCountInIndex + */ + public long getUnindexedDocCountInIndex() + { + return unindexedDocCountInIndex; + } + + + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/TrackerStats.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/TrackerStats.java index a13c92011..278e1353a 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/TrackerStats.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/TrackerStats.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,967 +24,967 @@ * #L% */ -package org.alfresco.solr.tracker; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; +package org.alfresco.solr.tracker; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.concurrent.NotThreadSafe; -import org.alfresco.solr.InformationServerCollectionProvider; -import org.alfresco.solr.adapters.ISimpleOrderedMap; -import org.alfresco.util.Pair; - -/** - * @author Andy - */ -public class TrackerStats -{ - public static final int TIME_SCALE = 1000000; - - ConcurrentHashMap modelTimes = new ConcurrentHashMap(); - - ConcurrentHashMap aclTimes = new ConcurrentHashMap(); - - ConcurrentHashMap changeSetAcls = new ConcurrentHashMap(); - - ConcurrentHashMap txDocs = new ConcurrentHashMap(); - - ConcurrentHashMap docTransformationTimes = new ConcurrentHashMap(); - - ConcurrentHashMap nodeTimes = new ConcurrentHashMap(); - - ConcurrentHashMap elapsedNodeTimes = new ConcurrentHashMap(); - - ConcurrentHashMap elapsedAclTimes = new ConcurrentHashMap(); - - ConcurrentHashMap elapsedContentTimes = new ConcurrentHashMap(); - - private InformationServerCollectionProvider infoSrv; - - public TrackerStats(InformationServerCollectionProvider server) - { - infoSrv = server; - } - - /** - * @return the modelTimes - */ - public SimpleStats getModelTimes() - { - return aggregateResults(modelTimes); - } - - /** - * @param all ConcurrentHashMap - * @return SimpleStats - */ - private SimpleStats aggregateResults(ConcurrentHashMap all) - { - SimpleStats answer = null; - - for (String key : all.keySet()) - { - IncrementalStats next = all.get(key); - IncrementalStats stats = next.copy(); - if (answer == null) - { - answer = new SimpleStats(stats.scale, this.infoSrv); - answer .start = stats.start; - answer.moments[0] = stats.moments[0]; - answer.moments[1] = stats.moments[1]; - answer.moments[2] = stats.moments[2]; - answer.max = stats.max; - answer.min = stats.min; - answer.copies.put(key, stats); - } - else - { - SimpleStats newAnswer = new SimpleStats(answer.scale, this.infoSrv); - - newAnswer.moments[0] = answer.moments[0] + stats.moments[0]; - - newAnswer.moments[1] = answer.moments[1] * answer.moments[0] + stats.moments[1] * stats.moments[0]; - newAnswer.moments[1] /= answer.moments[0] + stats.moments[0]; - - newAnswer.moments[2] = answer.moments[2] * answer.moments[0]; - newAnswer.moments[2] += (answer.moments[1] - newAnswer.moments[1]) * (answer.moments[1] - newAnswer.moments[1]) * answer.moments[0]; - newAnswer.moments[2] += stats.moments[2] * stats.moments[0]; - newAnswer.moments[2] += (stats.moments[1] - newAnswer.moments[1]) * (stats.moments[1] - newAnswer.moments[1]) * stats.moments[0]; - newAnswer.moments[2] /= answer.moments[0] + stats.moments[0]; - - newAnswer.min = (stats.min < answer.min) ? stats.min : answer.min; - newAnswer.max = (stats.max > answer.max) ? stats.max : answer.max; - - newAnswer.start = (stats.start.compareTo(answer.start) < 1) ? stats.start : answer.start; - - newAnswer.copies.putAll(answer.copies); - newAnswer.copies.put(key, stats); - - answer = newAnswer; - } - - } - - if (answer == null) - { - answer = new SimpleStats(1, this.infoSrv); - } - - return answer; - } - - /** - * @return the aclTxTimes - */ - public SimpleStats getAclTimes() - { - return aggregateResults(aclTimes); - } - - public SimpleStats getChangeSetAcls() - { - return aggregateResults(changeSetAcls); - } - - public SimpleStats getNodeTimes() - { - return aggregateResults(nodeTimes); - } - - /** - * @return the txDocs - */ - public SimpleStats getTxDocs() - { - return aggregateResults(txDocs); - } - - /** - * @return the docTransformationTimes - */ - public SimpleStats getDocTransformationTimes() - { - return aggregateResults(docTransformationTimes); - } - - public double getMeanModelSyncTime() - { - return aggregateResults(modelTimes).getMean(); - } - - public double getMeanNodeIndexTime() - { - return aggregateResults(nodeTimes).getMean(); - } - - public double getMeanNodeElapsedIndexTime() - { - return aggregateResults(elapsedNodeTimes).getMean(); - } - - public double getMeanAclElapsedIndexTime() - { - return aggregateResults(elapsedAclTimes).getMean(); - } - - public double getMeanContentElapsedIndexTime() - { - return aggregateResults(elapsedContentTimes).getMean(); - } - - public double getNodeIndexingThreadCount() - { - return nodeTimes.size(); - } - - public double getMeanAclIndexTime() - { - return aggregateResults(nodeTimes).getMean(); - } - - public double getMeanDocsPerTx() - { - return aggregateResults(txDocs).getMean(); - } - - public double getMeanAclsPerChangeSet() - { - return aggregateResults(changeSetAcls).getMean(); - } - - public static class SimpleStats - { - HashMap copies = new HashMap(); - private InformationServerCollectionProvider server; - int scale; - - SimpleStats(int scale, InformationServerCollectionProvider server) - { - this.scale = scale; - this.server = server; - } - - double[] moments = new double[3]; - - double min = 0D; - - double max = 0D; - - Date start = null; - - synchronized long getN() - { - return (long) moments[0]; - } - - synchronized double getMin() - { - return min; - } - - synchronized double getMax() - { - return max; - } - - synchronized double getMean() - { - return moments[1]; - } - - synchronized double getVarience() - { - if (moments[0] > 1) - { - return moments[2] * moments[0] / (moments[0] - 1); - } - else - { - return Double.NaN; - } - } - - synchronized double getStandardDeviation() - { - return Math.sqrt(getVarience()); - } - - public synchronized ISimpleOrderedMap getNamedList(boolean incdludeDetail, boolean includeHist, boolean includeValues) - { - ISimpleOrderedMap map = this.server.getSimpleOrderedMapInstance(); - map.add("Start", start); - map.add("N", getN()); - map.add("Min", getMin()); - map.add("Max", getMax()); - map.add("Mean", getMean()); - map.add("Varience", getVarience()); - map.add("StdDev", getStandardDeviation()); - if (incdludeDetail) - { +import org.alfresco.solr.InformationServerCollectionProvider; +import org.alfresco.solr.adapters.ISimpleOrderedMap; +import org.alfresco.util.Pair; + +/** + * @author Andy + */ +public class TrackerStats +{ + public static final int TIME_SCALE = 1000000; + + ConcurrentHashMap modelTimes = new ConcurrentHashMap(); + + ConcurrentHashMap aclTimes = new ConcurrentHashMap(); + + ConcurrentHashMap changeSetAcls = new ConcurrentHashMap(); + + ConcurrentHashMap txDocs = new ConcurrentHashMap(); + + ConcurrentHashMap docTransformationTimes = new ConcurrentHashMap(); + + ConcurrentHashMap nodeTimes = new ConcurrentHashMap(); + + ConcurrentHashMap elapsedNodeTimes = new ConcurrentHashMap(); + + ConcurrentHashMap elapsedAclTimes = new ConcurrentHashMap(); + + ConcurrentHashMap elapsedContentTimes = new ConcurrentHashMap(); + + private InformationServerCollectionProvider infoSrv; + + public TrackerStats(InformationServerCollectionProvider server) + { + infoSrv = server; + } + + /** + * @return the modelTimes + */ + public SimpleStats getModelTimes() + { + return aggregateResults(modelTimes); + } + + /** + * @param all ConcurrentHashMap + * @return SimpleStats + */ + private SimpleStats aggregateResults(ConcurrentHashMap all) + { + SimpleStats answer = null; + + for (String key : all.keySet()) + { + IncrementalStats next = all.get(key); + IncrementalStats stats = next.copy(); + if (answer == null) + { + answer = new SimpleStats(stats.scale, this.infoSrv); + answer .start = stats.start; + answer.moments[0] = stats.moments[0]; + answer.moments[1] = stats.moments[1]; + answer.moments[2] = stats.moments[2]; + answer.max = stats.max; + answer.min = stats.min; + answer.copies.put(key, stats); + } + else + { + SimpleStats newAnswer = new SimpleStats(answer.scale, this.infoSrv); + + newAnswer.moments[0] = answer.moments[0] + stats.moments[0]; + + newAnswer.moments[1] = answer.moments[1] * answer.moments[0] + stats.moments[1] * stats.moments[0]; + newAnswer.moments[1] /= answer.moments[0] + stats.moments[0]; + + newAnswer.moments[2] = answer.moments[2] * answer.moments[0]; + newAnswer.moments[2] += (answer.moments[1] - newAnswer.moments[1]) * (answer.moments[1] - newAnswer.moments[1]) * answer.moments[0]; + newAnswer.moments[2] += stats.moments[2] * stats.moments[0]; + newAnswer.moments[2] += (stats.moments[1] - newAnswer.moments[1]) * (stats.moments[1] - newAnswer.moments[1]) * stats.moments[0]; + newAnswer.moments[2] /= answer.moments[0] + stats.moments[0]; + + newAnswer.min = (stats.min < answer.min) ? stats.min : answer.min; + newAnswer.max = (stats.max > answer.max) ? stats.max : answer.max; + + newAnswer.start = (stats.start.compareTo(answer.start) < 1) ? stats.start : answer.start; + + newAnswer.copies.putAll(answer.copies); + newAnswer.copies.put(key, stats); + + answer = newAnswer; + } + + } + + if (answer == null) + { + answer = new SimpleStats(1, this.infoSrv); + } + + return answer; + } + + /** + * @return the aclTxTimes + */ + public SimpleStats getAclTimes() + { + return aggregateResults(aclTimes); + } + + public SimpleStats getChangeSetAcls() + { + return aggregateResults(changeSetAcls); + } + + public SimpleStats getNodeTimes() + { + return aggregateResults(nodeTimes); + } + + /** + * @return the txDocs + */ + public SimpleStats getTxDocs() + { + return aggregateResults(txDocs); + } + + /** + * @return the docTransformationTimes + */ + public SimpleStats getDocTransformationTimes() + { + return aggregateResults(docTransformationTimes); + } + + public double getMeanModelSyncTime() + { + return aggregateResults(modelTimes).getMean(); + } + + public double getMeanNodeIndexTime() + { + return aggregateResults(nodeTimes).getMean(); + } + + public double getMeanNodeElapsedIndexTime() + { + return aggregateResults(elapsedNodeTimes).getMean(); + } + + public double getMeanAclElapsedIndexTime() + { + return aggregateResults(elapsedAclTimes).getMean(); + } + + public double getMeanContentElapsedIndexTime() + { + return aggregateResults(elapsedContentTimes).getMean(); + } + + public double getNodeIndexingThreadCount() + { + return nodeTimes.size(); + } + + public double getMeanAclIndexTime() + { + return aggregateResults(nodeTimes).getMean(); + } + + public double getMeanDocsPerTx() + { + return aggregateResults(txDocs).getMean(); + } + + public double getMeanAclsPerChangeSet() + { + return aggregateResults(changeSetAcls).getMean(); + } + + public static class SimpleStats + { + HashMap copies = new HashMap(); + private InformationServerCollectionProvider server; + int scale; + + SimpleStats(int scale, InformationServerCollectionProvider server) + { + this.scale = scale; + this.server = server; + } + + double[] moments = new double[3]; + + double min = 0D; + + double max = 0D; + + Date start = null; + + synchronized long getN() + { + return (long) moments[0]; + } + + synchronized double getMin() + { + return min; + } + + synchronized double getMax() + { + return max; + } + + synchronized double getMean() + { + return moments[1]; + } + + synchronized double getVarience() + { + if (moments[0] > 1) + { + return moments[2] * moments[0] / (moments[0] - 1); + } + else + { + return Double.NaN; + } + } + + synchronized double getStandardDeviation() + { + return Math.sqrt(getVarience()); + } + + public synchronized ISimpleOrderedMap getNamedList(boolean incdludeDetail, boolean includeHist, boolean includeValues) + { + ISimpleOrderedMap map = this.server.getSimpleOrderedMapInstance(); + map.add("Start", start); + map.add("N", getN()); + map.add("Min", getMin()); + map.add("Max", getMax()); + map.add("Mean", getMean()); + map.add("Varience", getVarience()); + map.add("StdDev", getStandardDeviation()); + if (incdludeDetail) + { for (Entry copy : copies.entrySet()) { map.add(copy.getKey(), copy.getValue().getNamedList(includeHist, includeValues)); } - } - - return map; - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - return "SimpleStats [" - + "\n getN()=" + getN() + ",\n getMin()=" + getMin() + ",\n getMax()=" + getMax() + ",\n getMean()=" - + getMean() + ",\n getVarience()=" + getVarience() + ",\n getStandardDeviation()=" + getStandardDeviation() + ",\n copies=" - + copies + ",\n]"; - } - - } - - public static class Bucket - { - IncrementalStats incrementalStats; - - public double leftBoundary; - - public double rightBoundary; - - public double countLeft; - - public double countRight; - - Bucket(IncrementalStats incrementalStats, double leftBoundary, double rightBoundary) - { - this(incrementalStats, leftBoundary, rightBoundary, 0D, 0D); - } - - Bucket(IncrementalStats incrementalStats, double leftBoundary, double rightBoundary, double countLeft, double countRight) - { - this.incrementalStats = incrementalStats; - this.leftBoundary = leftBoundary; - this.rightBoundary = rightBoundary; - this.countLeft = countLeft; - this.countRight = countRight; - } - - public void add(double x) - { - if ((x - leftBoundary) < (rightBoundary - x)) - { - countLeft++; - } - else - { - countRight++; - } - } - - public double mergeError(Bucket o) - { - double f_m = (this.countLeft + this.countRight + o.countLeft + o.countRight) / 4.0d; - double t1 = this.countLeft - f_m; - double t2 = this.countRight - f_m; - double o1 = o.countLeft - f_m; - double o2 = o.countRight - f_m; - - return (t1 * t1) + (t2 * t2) + (o1 * o1) + (o2 * o2); - } - - public double error() - { - double f_m = (this.countLeft + this.countRight) / 2.0d; - double t1 = this.countLeft - f_m; - double t2 = this.countRight - f_m; - return (t1 * t1) + (t2 * t2); - - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - double mark = (leftBoundary + rightBoundary) / 2.0D; - double width = rightBoundary - leftBoundary; - return "Bucket [(" - + leftBoundary + " TO " + mark + " = " + countLeft / incrementalStats.getN() / width + ") : (" + mark + " TO " + rightBoundary + " = " + countRight - / incrementalStats.getN() / width + "]"; - } - - } - + } + + return map; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "SimpleStats [" + + "\n getN()=" + getN() + ",\n getMin()=" + getMin() + ",\n getMax()=" + getMax() + ",\n getMean()=" + + getMean() + ",\n getVarience()=" + getVarience() + ",\n getStandardDeviation()=" + getStandardDeviation() + ",\n copies=" + + copies + ",\n]"; + } + + } + + public static class Bucket + { + IncrementalStats incrementalStats; + + public double leftBoundary; + + public double rightBoundary; + + public double countLeft; + + public double countRight; + + Bucket(IncrementalStats incrementalStats, double leftBoundary, double rightBoundary) + { + this(incrementalStats, leftBoundary, rightBoundary, 0D, 0D); + } + + Bucket(IncrementalStats incrementalStats, double leftBoundary, double rightBoundary, double countLeft, double countRight) + { + this.incrementalStats = incrementalStats; + this.leftBoundary = leftBoundary; + this.rightBoundary = rightBoundary; + this.countLeft = countLeft; + this.countRight = countRight; + } + + public void add(double x) + { + if ((x - leftBoundary) < (rightBoundary - x)) + { + countLeft++; + } + else + { + countRight++; + } + } + + public double mergeError(Bucket o) + { + double f_m = (this.countLeft + this.countRight + o.countLeft + o.countRight) / 4.0d; + double t1 = this.countLeft - f_m; + double t2 = this.countRight - f_m; + double o1 = o.countLeft - f_m; + double o2 = o.countRight - f_m; + + return (t1 * t1) + (t2 * t2) + (o1 * o1) + (o2 * o2); + } + + public double error() + { + double f_m = (this.countLeft + this.countRight) / 2.0d; + double t1 = this.countLeft - f_m; + double t2 = this.countRight - f_m; + return (t1 * t1) + (t2 * t2); + + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + double mark = (leftBoundary + rightBoundary) / 2.0D; + double width = rightBoundary - leftBoundary; + return "Bucket [(" + + leftBoundary + " TO " + mark + " = " + countLeft / incrementalStats.getN() / width + ") : (" + mark + " TO " + rightBoundary + " = " + countRight + / incrementalStats.getN() / width + "]"; + } + + } + @NotThreadSafe - public static class IncrementalStats - { - Date start = new Date(); - - int scale; - - int buckets; - - double[] moments = new double[5]; - - double min = 0D; - - double max = 0D; - - List values; - - List hist; - - InformationServerCollectionProvider server; - - public IncrementalStats(int scale, int buckets, InformationServerCollectionProvider infoSrv) - { - this.scale = scale; - this.buckets = buckets; - values = new ArrayList(buckets); - hist = new ArrayList(buckets + 1); - this.server = infoSrv; - } - - /** - * @return ISimpleOrderedMap - */ - public ISimpleOrderedMap getNamedList(boolean includeHist, boolean includeValues) - { - ISimpleOrderedMap map = this.server.getSimpleOrderedMapInstance(); - map.add("Start", start); - map.add("N", getN()); - map.add("Min", getMin()); - map.add("Max", getMax()); - map.add("Mean", getMean()); - map.add("Varience", getVarience()); - map.add("StdDev", getStandardDeviation()); - map.add("Skew", getSkew()); - map.add("Kurtosis", getKurtosis()); - - if (includeHist) - { - int i = 0; - ISimpleOrderedMap buckets = this.server.getSimpleOrderedMapInstance(); - for (Bucket b : hist) - { - double mark = (b.leftBoundary + b.rightBoundary) / 2.0D; - double width = b.rightBoundary - b.leftBoundary; - - // SimpleOrderedMap bucket = new SimpleOrderedMap(); - // bucket.add("Lower", b.leftBoundary); - // bucket.add("Upper", mark); - // bucket.add("ProbabilityDensity", b.countLeft/b.incrementalStats.getN()/width); - buckets.add("" + i++, (b.leftBoundary + mark) / 2.0D + "," + b.countLeft / b.incrementalStats.getN() / width); - - // bucket = new SimpleOrderedMap(); - // bucket.add("Lower", mark); - // bucket.add("Upper", b.rightBoundary); - // bucket.add("ProbabilityDensity", b.countRight/b.incrementalStats.getN()/width); - buckets.add("" + i++, (mark + b.rightBoundary) / 2.0D + "," + b.countRight / b.incrementalStats.getN() / width); - } - map.add("Buckets", buckets); - } - - if (includeValues) - { - int i = 0; - ISimpleOrderedMap valuesMap = this.server.getSimpleOrderedMapInstance(); - for (Double value : values) - { - valuesMap.add("" + i++, value); - } - map.add("Values", valuesMap); - } - - return map; - } - - public synchronized void reset() - { - moments = new double[5]; - - min = 0D; - - max = 0D; - - values = new ArrayList(buckets); - - hist = new ArrayList(buckets + 1); - - start = new Date(); - } - - public synchronized void add(double xUnscaled) - { - double x = xUnscaled / scale; - if ((moments[0] == 0) || (x > max)) - { - max = x; - } - if ((moments[0] == 0L) || (x < min)) - { - min = x; - } - double n = moments[0]; - double nPlus1 = n + 1; - double n2 = n * n; - double d = (moments[1] - x) / nPlus1; - double d2 = d * d; - double d3 = d2 * d; - double n_nPlus1 = n / nPlus1; - - moments[4] += 4 * d * moments[3] + 6 * d2 * moments[2] + (1 + n * n2) * d2 * d2; - moments[4] *= n_nPlus1; - - moments[3] += 3 * d * moments[2] + (1 - n2) * d3; - moments[3] *= n_nPlus1; - - moments[2] += (1 + n) * d2; - moments[2] *= n_nPlus1; - - moments[1] -= d; - - moments[0] = nPlus1; - - if (buckets > 1) - { - if (moments[0] < buckets) - { - values.add(Double.valueOf(x)); - } - else if (moments[0] == buckets) - { - values.add(Double.valueOf(x)); - buildInitialBuckets(); - } - else - { - values.set((int) moments[0] % buckets, x); - if (x < hist.get(0).leftBoundary) - { - double delta = (hist.get(0).leftBoundary - x) / 3.0; - Bucket b = new Bucket(this, x - delta, hist.get(0).leftBoundary); - hist.add(0, b); - b.add(x); - Pair bestToMerge = findBestToMerge(); - if (hist.size() > buckets) - { - merge(bestToMerge.getFirst()); - } - } - else if (x >= hist.get(hist.size() - 1).rightBoundary) - { - double delta = (x - hist.get(hist.size() - 1).rightBoundary) / 3.0; - Bucket b = new Bucket(this, hist.get(hist.size() - 1).rightBoundary, x + delta); - hist.add(b); - b.add(x); - Pair bestToMerge = findBestToMerge(); - if (hist.size() > buckets) - { - merge(bestToMerge.getFirst()); - } - } - else - { - // find existing - for (Bucket b : hist) - { - if ((b.leftBoundary <= x) && (x < b.rightBoundary)) - { - b.add(x); - break; - } - - } - Pair bestToMerge = findBestToMerge(); - Pair bestToSplit = findBestToSplit(); - - if (bestToMerge.getSecond() - bestToSplit.getSecond() < 0) - { - if(hist.size() < buckets) - { - split(bestToSplit.getFirst()); - } - else - { - merge(bestToMerge.getFirst()); - split(bestToMerge.getFirst() < bestToSplit.getFirst() ? bestToSplit.getFirst() - 1 : bestToSplit.getFirst()); - } - } - } - } - } - } - - /** - * - */ - private void buildInitialBuckets() - { - // generate initial bucket list - Collections.sort(values); - FOUND: for (int i = 0; i < values.size(); i++) - { - for (Bucket b : hist) - { - if ((b.leftBoundary <= values.get(i)) && (values.get(i) < b.rightBoundary)) - { - b.add(values.get(i)); - continue FOUND; - } - - } - if (i < values.size() - 1) - { - double start = values.get(i); - double end = start + 1.0D; - - END: for (int j = i + 1; j < values.size(); j++) - { - if (values.get(j) > start) - { - end = values.get(j); - break END; - } - } - - Bucket b = new Bucket(this, start, end); - b.add(values.get(i)); - hist.add(b); - } - else - { - double first = values.get(0); - double last = values.get(values.size() - 1); - double width = 1.0D; - if (values.size() > 1) - { - width = (last - first) / (values.size() - 1); - } - Bucket b = new Bucket(this, last, last + width); - b.add(values.get(i)); - hist.add(b); - } - } - } - - void merge(int position) - { - Bucket lower = hist.get(position); - Bucket upper = hist.get(position + 1); - Bucket merged = new Bucket(this, lower.leftBoundary, upper.rightBoundary, lower.countLeft + lower.countRight, upper.countLeft + upper.countRight); - hist.remove(position); - hist.set(position, merged); - - } - - void split(int position) - { - Bucket toSplit = hist.get(position); - double mark = (toSplit.leftBoundary + toSplit.rightBoundary) / 2.0D; - Bucket lower = new Bucket(this, toSplit.leftBoundary, mark, toSplit.countLeft / 2.0D, toSplit.countLeft / 2.0D); - Bucket upper = new Bucket(this, mark, toSplit.rightBoundary, toSplit.countRight / 2.0D, toSplit.countRight / 2.0D); - hist.set(position, upper); - hist.add(position, lower); - } - - Pair findBestToMerge() - { - double minMergeError = Double.MAX_VALUE; - int bucket = 0; - for (int i = 0; i < hist.size() - 1; i++) - { - double mergeError = hist.get(i).mergeError(hist.get(i + 1)); - if (mergeError < minMergeError) - { - minMergeError = mergeError; - bucket = i; - } - } - return new Pair(bucket, minMergeError); - } - - Pair findBestToSplit() - { - double maxError = Double.MIN_VALUE; - int bucket = 0; - for (int i = 0; i < hist.size(); i++) - { - double error = hist.get(i).error(); - if (error > maxError) - { - maxError = error; - bucket = i; - } - } - return new Pair(bucket, maxError); - } - - public synchronized long getN() - { - return (long) moments[0]; - } - - public synchronized double getMin() - { - return min; - } - - public synchronized double getMax() - { - return max; - } - - public synchronized double getMean() - { - return moments[1]; - } - - public synchronized double getVarience() - { - if (moments[0] > 1) - { - return moments[2] * moments[0] / (moments[0] - 1); - } - else - { - return Double.NaN; - } - } - - public synchronized double getStandardDeviation() - { - return Math.sqrt(getVarience()); - } - - public synchronized double getSkew() - { - if (moments[0] > 2) - { - double v = getVarience(); - return moments[3] * moments[0] * moments[0] / (Math.sqrt(v) * v * (moments[0] - 1) * (moments[0] - 2)); - } - else - { - return Double.NaN; - } - } - - public synchronized double getKurtosis() - { - if (moments[0] > 3) - { - double div = (moments[0] - 2) * (moments[0] - 3); - double nMinus1 = moments[0] - 1; - double v = getVarience(); - double z = ((moments[4] * moments[0] * moments[0] * (moments[0] + 1)) / (v * v * nMinus1)); - z -= 3 * nMinus1 * nMinus1; - z /= div; - return z; - } - else - { - return Double.NaN; - } - } - - public synchronized List getHistogram() - { - if(hist.size() == 0) - { - buildInitialBuckets(); - } - return hist; - } - - - - synchronized IncrementalStats copy() - { - IncrementalStats copy = new IncrementalStats(this.scale, this.buckets, this.server); - copy.start = this.start; - copy.max = this.getMax(); - copy.min = this.min; - copy.moments[0] = this.moments[0]; - copy.moments[1] = this.moments[1]; - copy.moments[2] = this.moments[2]; - copy.moments[3] = this.moments[3]; - copy.moments[4] = this.moments[4]; - for (Double x : this.values) - { - copy.values.add(x); - } - for (Bucket b : this.hist) - { - copy.hist.add(new Bucket(copy, b.leftBoundary, b.rightBoundary, b.countLeft, b.countRight)); - } - return copy; - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - return "IncrementalStats [getN()=" - + getN() + ", getMin()=" + getMin() + ", getMax()=" + getMax() + ", getMean()=" + getMean() + ", getVarience()=" + getVarience() + ", getStandardDeviation()=" - + getStandardDeviation() + ", getSkew()=" + getSkew() + ", getKurtosis()=" + getKurtosis() + ", values=" + values + ", hist=" + hist + "]"; - } - - } - - /** - * @param time long - */ - public void addModelTime(long time) - { - IncrementalStats stats = modelTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - modelTimes.put(Thread.currentThread().getName(), stats); - } - stats.add(time); - } - - /** - * @param time long - */ - public void addAclTime(long time) - { - IncrementalStats stats = aclTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - aclTimes.put(Thread.currentThread().getName(), stats); - } - stats.add(time); - } - - /** - * @param time long - */ - public void addNodeTime(long time) - { - IncrementalStats stats = nodeTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - nodeTimes.put(Thread.currentThread().getName(), stats); - } - stats.add(time); - } - - /** - * @param docCount int - * @param time long - */ - public void addElapsedNodeTime(int docCount, long time) - { - if(docCount < 1) - { - return; - } - IncrementalStats stats = elapsedNodeTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - elapsedNodeTimes.put(Thread.currentThread().getName(), stats); - } - long meanTime = time / docCount; - for(int i = 0; i < docCount; i++) - { - stats.add(meanTime); - } - - } - - /** - * @param docCount int - * @param time long - */ - public void addElapsedAclTime(int docCount, long time) - { - if(docCount < 1) - { - return; - } - IncrementalStats stats = elapsedAclTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - elapsedAclTimes.put(Thread.currentThread().getName(), stats); - } - long meanTime = time / docCount; - for(int i = 0; i < docCount; i++) - { - stats.add(meanTime); - } - - } - - /** - * @param docCount int - * @param time long - */ - public void addElapsedContentTime(int docCount, long time) - { - if(docCount < 1) - { - return; - } - IncrementalStats stats = elapsedContentTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - elapsedContentTimes.put(Thread.currentThread().getName(), stats); - } - long meanTime = time / docCount; - for(int i = 0; i < docCount; i++) - { - stats.add(meanTime); - } - - } - - /** - * @param size int - */ - public void addTxDocs(int size) - { - IncrementalStats stats = txDocs.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(1, 50, this.infoSrv); - txDocs.put(Thread.currentThread().getName(), stats); - } - stats.add(size); - } - - /** - * @param size int - */ - public void addChangeSetAcls(int size) - { - IncrementalStats stats = changeSetAcls.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(1, 50, this.infoSrv); - changeSetAcls.put(Thread.currentThread().getName(), stats); - } - stats.add(size); - } - - /** - * @param time long - */ - public void addDocTransformationTime(long time) - { - IncrementalStats stats = docTransformationTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - docTransformationTimes.put(Thread.currentThread().getName(), stats); - } - stats.add(time); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - return "TrackerStats [modelTimes=" - + modelTimes + ", aclTimes=" + aclTimes + ", changeSetAcls=" + changeSetAcls + ", txDocs=" + txDocs + ", docTransformationTimes=" + docTransformationTimes - + ", nodeTimes=" + nodeTimes + "]"; - } - - /** - * - */ - public void reset() - { - modelTimes.clear(); - aclTimes.clear(); - changeSetAcls.clear(); - txDocs.clear(); - docTransformationTimes.clear(); - nodeTimes.clear(); - } - - -} + public static class IncrementalStats + { + Date start = new Date(); + + int scale; + + int buckets; + + double[] moments = new double[5]; + + double min = 0D; + + double max = 0D; + + List values; + + List hist; + + InformationServerCollectionProvider server; + + public IncrementalStats(int scale, int buckets, InformationServerCollectionProvider infoSrv) + { + this.scale = scale; + this.buckets = buckets; + values = new ArrayList(buckets); + hist = new ArrayList(buckets + 1); + this.server = infoSrv; + } + + /** + * @return ISimpleOrderedMap + */ + public ISimpleOrderedMap getNamedList(boolean includeHist, boolean includeValues) + { + ISimpleOrderedMap map = this.server.getSimpleOrderedMapInstance(); + map.add("Start", start); + map.add("N", getN()); + map.add("Min", getMin()); + map.add("Max", getMax()); + map.add("Mean", getMean()); + map.add("Varience", getVarience()); + map.add("StdDev", getStandardDeviation()); + map.add("Skew", getSkew()); + map.add("Kurtosis", getKurtosis()); + + if (includeHist) + { + int i = 0; + ISimpleOrderedMap buckets = this.server.getSimpleOrderedMapInstance(); + for (Bucket b : hist) + { + double mark = (b.leftBoundary + b.rightBoundary) / 2.0D; + double width = b.rightBoundary - b.leftBoundary; + + // SimpleOrderedMap bucket = new SimpleOrderedMap(); + // bucket.add("Lower", b.leftBoundary); + // bucket.add("Upper", mark); + // bucket.add("ProbabilityDensity", b.countLeft/b.incrementalStats.getN()/width); + buckets.add("" + i++, (b.leftBoundary + mark) / 2.0D + "," + b.countLeft / b.incrementalStats.getN() / width); + + // bucket = new SimpleOrderedMap(); + // bucket.add("Lower", mark); + // bucket.add("Upper", b.rightBoundary); + // bucket.add("ProbabilityDensity", b.countRight/b.incrementalStats.getN()/width); + buckets.add("" + i++, (mark + b.rightBoundary) / 2.0D + "," + b.countRight / b.incrementalStats.getN() / width); + } + map.add("Buckets", buckets); + } + + if (includeValues) + { + int i = 0; + ISimpleOrderedMap valuesMap = this.server.getSimpleOrderedMapInstance(); + for (Double value : values) + { + valuesMap.add("" + i++, value); + } + map.add("Values", valuesMap); + } + + return map; + } + + public synchronized void reset() + { + moments = new double[5]; + + min = 0D; + + max = 0D; + + values = new ArrayList(buckets); + + hist = new ArrayList(buckets + 1); + + start = new Date(); + } + + public synchronized void add(double xUnscaled) + { + double x = xUnscaled / scale; + if ((moments[0] == 0) || (x > max)) + { + max = x; + } + if ((moments[0] == 0L) || (x < min)) + { + min = x; + } + double n = moments[0]; + double nPlus1 = n + 1; + double n2 = n * n; + double d = (moments[1] - x) / nPlus1; + double d2 = d * d; + double d3 = d2 * d; + double n_nPlus1 = n / nPlus1; + + moments[4] += 4 * d * moments[3] + 6 * d2 * moments[2] + (1 + n * n2) * d2 * d2; + moments[4] *= n_nPlus1; + + moments[3] += 3 * d * moments[2] + (1 - n2) * d3; + moments[3] *= n_nPlus1; + + moments[2] += (1 + n) * d2; + moments[2] *= n_nPlus1; + + moments[1] -= d; + + moments[0] = nPlus1; + + if (buckets > 1) + { + if (moments[0] < buckets) + { + values.add(Double.valueOf(x)); + } + else if (moments[0] == buckets) + { + values.add(Double.valueOf(x)); + buildInitialBuckets(); + } + else + { + values.set((int) moments[0] % buckets, x); + if (x < hist.get(0).leftBoundary) + { + double delta = (hist.get(0).leftBoundary - x) / 3.0; + Bucket b = new Bucket(this, x - delta, hist.get(0).leftBoundary); + hist.add(0, b); + b.add(x); + Pair bestToMerge = findBestToMerge(); + if (hist.size() > buckets) + { + merge(bestToMerge.getFirst()); + } + } + else if (x >= hist.get(hist.size() - 1).rightBoundary) + { + double delta = (x - hist.get(hist.size() - 1).rightBoundary) / 3.0; + Bucket b = new Bucket(this, hist.get(hist.size() - 1).rightBoundary, x + delta); + hist.add(b); + b.add(x); + Pair bestToMerge = findBestToMerge(); + if (hist.size() > buckets) + { + merge(bestToMerge.getFirst()); + } + } + else + { + // find existing + for (Bucket b : hist) + { + if ((b.leftBoundary <= x) && (x < b.rightBoundary)) + { + b.add(x); + break; + } + + } + Pair bestToMerge = findBestToMerge(); + Pair bestToSplit = findBestToSplit(); + + if (bestToMerge.getSecond() - bestToSplit.getSecond() < 0) + { + if(hist.size() < buckets) + { + split(bestToSplit.getFirst()); + } + else + { + merge(bestToMerge.getFirst()); + split(bestToMerge.getFirst() < bestToSplit.getFirst() ? bestToSplit.getFirst() - 1 : bestToSplit.getFirst()); + } + } + } + } + } + } + + /** + * + */ + private void buildInitialBuckets() + { + // generate initial bucket list + Collections.sort(values); + FOUND: for (int i = 0; i < values.size(); i++) + { + for (Bucket b : hist) + { + if ((b.leftBoundary <= values.get(i)) && (values.get(i) < b.rightBoundary)) + { + b.add(values.get(i)); + continue FOUND; + } + + } + if (i < values.size() - 1) + { + double start = values.get(i); + double end = start + 1.0D; + + END: for (int j = i + 1; j < values.size(); j++) + { + if (values.get(j) > start) + { + end = values.get(j); + break END; + } + } + + Bucket b = new Bucket(this, start, end); + b.add(values.get(i)); + hist.add(b); + } + else + { + double first = values.get(0); + double last = values.get(values.size() - 1); + double width = 1.0D; + if (values.size() > 1) + { + width = (last - first) / (values.size() - 1); + } + Bucket b = new Bucket(this, last, last + width); + b.add(values.get(i)); + hist.add(b); + } + } + } + + void merge(int position) + { + Bucket lower = hist.get(position); + Bucket upper = hist.get(position + 1); + Bucket merged = new Bucket(this, lower.leftBoundary, upper.rightBoundary, lower.countLeft + lower.countRight, upper.countLeft + upper.countRight); + hist.remove(position); + hist.set(position, merged); + + } + + void split(int position) + { + Bucket toSplit = hist.get(position); + double mark = (toSplit.leftBoundary + toSplit.rightBoundary) / 2.0D; + Bucket lower = new Bucket(this, toSplit.leftBoundary, mark, toSplit.countLeft / 2.0D, toSplit.countLeft / 2.0D); + Bucket upper = new Bucket(this, mark, toSplit.rightBoundary, toSplit.countRight / 2.0D, toSplit.countRight / 2.0D); + hist.set(position, upper); + hist.add(position, lower); + } + + Pair findBestToMerge() + { + double minMergeError = Double.MAX_VALUE; + int bucket = 0; + for (int i = 0; i < hist.size() - 1; i++) + { + double mergeError = hist.get(i).mergeError(hist.get(i + 1)); + if (mergeError < minMergeError) + { + minMergeError = mergeError; + bucket = i; + } + } + return new Pair(bucket, minMergeError); + } + + Pair findBestToSplit() + { + double maxError = Double.MIN_VALUE; + int bucket = 0; + for (int i = 0; i < hist.size(); i++) + { + double error = hist.get(i).error(); + if (error > maxError) + { + maxError = error; + bucket = i; + } + } + return new Pair(bucket, maxError); + } + + public synchronized long getN() + { + return (long) moments[0]; + } + + public synchronized double getMin() + { + return min; + } + + public synchronized double getMax() + { + return max; + } + + public synchronized double getMean() + { + return moments[1]; + } + + public synchronized double getVarience() + { + if (moments[0] > 1) + { + return moments[2] * moments[0] / (moments[0] - 1); + } + else + { + return Double.NaN; + } + } + + public synchronized double getStandardDeviation() + { + return Math.sqrt(getVarience()); + } + + public synchronized double getSkew() + { + if (moments[0] > 2) + { + double v = getVarience(); + return moments[3] * moments[0] * moments[0] / (Math.sqrt(v) * v * (moments[0] - 1) * (moments[0] - 2)); + } + else + { + return Double.NaN; + } + } + + public synchronized double getKurtosis() + { + if (moments[0] > 3) + { + double div = (moments[0] - 2) * (moments[0] - 3); + double nMinus1 = moments[0] - 1; + double v = getVarience(); + double z = ((moments[4] * moments[0] * moments[0] * (moments[0] + 1)) / (v * v * nMinus1)); + z -= 3 * nMinus1 * nMinus1; + z /= div; + return z; + } + else + { + return Double.NaN; + } + } + + public synchronized List getHistogram() + { + if(hist.size() == 0) + { + buildInitialBuckets(); + } + return hist; + } + + + + synchronized IncrementalStats copy() + { + IncrementalStats copy = new IncrementalStats(this.scale, this.buckets, this.server); + copy.start = this.start; + copy.max = this.getMax(); + copy.min = this.min; + copy.moments[0] = this.moments[0]; + copy.moments[1] = this.moments[1]; + copy.moments[2] = this.moments[2]; + copy.moments[3] = this.moments[3]; + copy.moments[4] = this.moments[4]; + for (Double x : this.values) + { + copy.values.add(x); + } + for (Bucket b : this.hist) + { + copy.hist.add(new Bucket(copy, b.leftBoundary, b.rightBoundary, b.countLeft, b.countRight)); + } + return copy; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "IncrementalStats [getN()=" + + getN() + ", getMin()=" + getMin() + ", getMax()=" + getMax() + ", getMean()=" + getMean() + ", getVarience()=" + getVarience() + ", getStandardDeviation()=" + + getStandardDeviation() + ", getSkew()=" + getSkew() + ", getKurtosis()=" + getKurtosis() + ", values=" + values + ", hist=" + hist + "]"; + } + + } + + /** + * @param time long + */ + public void addModelTime(long time) + { + IncrementalStats stats = modelTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + modelTimes.put(Thread.currentThread().getName(), stats); + } + stats.add(time); + } + + /** + * @param time long + */ + public void addAclTime(long time) + { + IncrementalStats stats = aclTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + aclTimes.put(Thread.currentThread().getName(), stats); + } + stats.add(time); + } + + /** + * @param time long + */ + public void addNodeTime(long time) + { + IncrementalStats stats = nodeTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + nodeTimes.put(Thread.currentThread().getName(), stats); + } + stats.add(time); + } + + /** + * @param docCount int + * @param time long + */ + public void addElapsedNodeTime(int docCount, long time) + { + if(docCount < 1) + { + return; + } + IncrementalStats stats = elapsedNodeTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + elapsedNodeTimes.put(Thread.currentThread().getName(), stats); + } + long meanTime = time / docCount; + for(int i = 0; i < docCount; i++) + { + stats.add(meanTime); + } + + } + + /** + * @param docCount int + * @param time long + */ + public void addElapsedAclTime(int docCount, long time) + { + if(docCount < 1) + { + return; + } + IncrementalStats stats = elapsedAclTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + elapsedAclTimes.put(Thread.currentThread().getName(), stats); + } + long meanTime = time / docCount; + for(int i = 0; i < docCount; i++) + { + stats.add(meanTime); + } + + } + + /** + * @param docCount int + * @param time long + */ + public void addElapsedContentTime(int docCount, long time) + { + if(docCount < 1) + { + return; + } + IncrementalStats stats = elapsedContentTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + elapsedContentTimes.put(Thread.currentThread().getName(), stats); + } + long meanTime = time / docCount; + for(int i = 0; i < docCount; i++) + { + stats.add(meanTime); + } + + } + + /** + * @param size int + */ + public void addTxDocs(int size) + { + IncrementalStats stats = txDocs.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(1, 50, this.infoSrv); + txDocs.put(Thread.currentThread().getName(), stats); + } + stats.add(size); + } + + /** + * @param size int + */ + public void addChangeSetAcls(int size) + { + IncrementalStats stats = changeSetAcls.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(1, 50, this.infoSrv); + changeSetAcls.put(Thread.currentThread().getName(), stats); + } + stats.add(size); + } + + /** + * @param time long + */ + public void addDocTransformationTime(long time) + { + IncrementalStats stats = docTransformationTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + docTransformationTimes.put(Thread.currentThread().getName(), stats); + } + stats.add(time); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "TrackerStats [modelTimes=" + + modelTimes + ", aclTimes=" + aclTimes + ", changeSetAcls=" + changeSetAcls + ", txDocs=" + txDocs + ", docTransformationTimes=" + docTransformationTimes + + ", nodeTimes=" + nodeTimes + "]"; + } + + /** + * + */ + public void reset() + { + modelTimes.clear(); + aclTimes.clear(); + changeSetAcls.clear(); + txDocs.clear(); + docTransformationTimes.clear(); + nodeTimes.clear(); + } + + +} diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/BoundedDequeTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/BoundedDequeTest.java index 95e300c1a..e7ca86c38 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/BoundedDequeTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/BoundedDequeTest.java @@ -1,57 +1,57 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr; - -import static java.util.Arrays.asList; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.concurrent.LinkedBlockingDeque; - -import org.junit.Test; - -/** Unit tests for the {@link BoundedDeque}. */ -public class BoundedDequeTest -{ - /** Check that earlier entries are removed from the BoundedDeque. */ - @Test - public void testBoundedness() - { - // Create a BoundedDeque with size two. - BoundedDeque boundedDeque = new BoundedDeque<>(2); - - // Add three things. - boundedDeque.add("A"); - boundedDeque.add("B"); - boundedDeque.add("C"); - - // Check that the latest two are still there. - LinkedBlockingDeque actual = boundedDeque.getDeque(); - assertEquals("Unexpected entries in BoundedDeque.", asList("C", "B"), new ArrayList(actual)); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr; + +import static java.util.Arrays.asList; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.concurrent.LinkedBlockingDeque; + +import org.junit.Test; + +/** Unit tests for the {@link BoundedDeque}. */ +public class BoundedDequeTest +{ + /** Check that earlier entries are removed from the BoundedDeque. */ + @Test + public void testBoundedness() + { + // Create a BoundedDeque with size two. + BoundedDeque boundedDeque = new BoundedDeque<>(2); + + // Add three things. + boundedDeque.add("A"); + boundedDeque.add("B"); + boundedDeque.add("C"); + + // Check that the latest two are still there. + LinkedBlockingDeque actual = boundedDeque.getDeque(); + assertEquals("Unexpected entries in BoundedDeque.", asList("C", "B"), new ArrayList(actual)); + } +} diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/AclReadersTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/AclReadersTest.java index b14c2ce59..dcc8f015e 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/AclReadersTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/AclReadersTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,63 +24,63 @@ * #L% */ -package org.alfresco.solr.client; - -import static java.util.Arrays.asList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -/** - * Some simple sanity tests for the {@link AclReaders} class. - * - * @author Matt Ward - */ -public class AclReadersTest -{ - @Test - public void testHashCode() - { - // We only care about ID for equals() and hashCode() - - // The same ID - assertEquals(new AclReaders(123, null, null, 0, null).hashCode(), - new AclReaders(123, null, null, 0, null).hashCode()); - - // Different ID - assertNotEquals(new AclReaders(123, null, null, 0, null).hashCode(), - new AclReaders(124, null, null, 0, null).hashCode()); - } - - @Test - public void testEqualsObject() - { - // The very same - final AclReaders aclReaders = new AclReaders(0, null, null, 0, null); - assertTrue(aclReaders.equals(aclReaders)); - - // The same ID - assertEquals(new AclReaders(123, null, null, 0, null), - new AclReaders(123, null, null, 0, null)); - - // Different ID - assertNotEquals(new AclReaders(123, null, null, 0, null), - new AclReaders(124, null, null, 0, null)); - } - - @Test - public void testGetReaders() - { - AclReaders aclReaders = new AclReaders(0, null, asList("d1", "d2"), 0, null); - assertEquals(asList("d1", "d2"), aclReaders.getDenied()); - } - - @Test - public void testGetDenied() - { - AclReaders aclReaders = new AclReaders(0, asList("r1", "r2", "r3"), null, 0, null); - assertEquals(asList("r1", "r2", "r3"), aclReaders.getReaders()); - } -} +package org.alfresco.solr.client; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Some simple sanity tests for the {@link AclReaders} class. + * + * @author Matt Ward + */ +public class AclReadersTest +{ + @Test + public void testHashCode() + { + // We only care about ID for equals() and hashCode() + + // The same ID + assertEquals(new AclReaders(123, null, null, 0, null).hashCode(), + new AclReaders(123, null, null, 0, null).hashCode()); + + // Different ID + assertNotEquals(new AclReaders(123, null, null, 0, null).hashCode(), + new AclReaders(124, null, null, 0, null).hashCode()); + } + + @Test + public void testEqualsObject() + { + // The very same + final AclReaders aclReaders = new AclReaders(0, null, null, 0, null); + assertTrue(aclReaders.equals(aclReaders)); + + // The same ID + assertEquals(new AclReaders(123, null, null, 0, null), + new AclReaders(123, null, null, 0, null)); + + // Different ID + assertNotEquals(new AclReaders(123, null, null, 0, null), + new AclReaders(124, null, null, 0, null)); + } + + @Test + public void testGetReaders() + { + AclReaders aclReaders = new AclReaders(0, null, asList("d1", "d2"), 0, null); + assertEquals(asList("d1", "d2"), aclReaders.getDenied()); + } + + @Test + public void testGetDenied() + { + AclReaders aclReaders = new AclReaders(0, asList("r1", "r2", "r3"), null, 0, null); + assertEquals(asList("r1", "r2", "r3"), aclReaders.getReaders()); + } +} diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/LookAheadBufferedReaderTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/LookAheadBufferedReaderTest.java index 93e6bb65e..4e72e82f9 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/LookAheadBufferedReaderTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/LookAheadBufferedReaderTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -208,4 +208,4 @@ public class LookAheadBufferedReaderTest throw new RuntimeException(exception); } } -} \ No newline at end of file +} diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientFactoryTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientFactoryTest.java index fccc36ed6..e6d0b3b23 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientFactoryTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientFactoryTest.java @@ -1,101 +1,101 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -import static org.mockito.Mockito.*; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; - -import java.util.Properties; - -import org.alfresco.encryption.KeyResourceLoader; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.solr.client.SOLRAPIClient; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class SOLRAPIClientFactoryTest -{ - @Mock - private Properties props; - @Mock - private KeyResourceLoader keyResourceLoader; - @Mock - private DictionaryService dictionaryService; - @Mock - private NamespaceDAO namespaceDAO; - private SOLRAPIClientFactory factory; - - @Before - public void setUp() throws Exception - { - when(props.getProperty(eq("alfresco.host"), anyString())).thenReturn("localhost"); - when(props.getProperty(eq("alfresco.port"), anyString())).thenReturn("8080"); - when(props.getProperty(eq("alfresco.port.ssl"), anyString())).thenReturn("8443"); - when(props.getProperty(eq("alfresco.maxTotalConnections"), anyString())).thenReturn("40"); - when(props.getProperty(eq("alfresco.maxHostConnections"), anyString())).thenReturn("40"); - when(props.getProperty(eq("alfresco.socketTimeout"), anyString())).thenReturn("0"); - when(props.getProperty(eq("alfresco.secureComms"), anyString())).thenReturn("none"); - - this.factory = new SOLRAPIClientFactory(); - } - - @Test - public void getsSameSOLRAPIClientForSameAlfresco() - { - SOLRAPIClient solrapiClient = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, - namespaceDAO); - assertNotNull(solrapiClient); - - SOLRAPIClient solrapiClient2 = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, - namespaceDAO); - assertNotNull(solrapiClient2); - - assertSame(solrapiClient, solrapiClient2); - } - - @Test - public void getsDifferentSOLRAPIClientForDifferentAlfresco() - { - SOLRAPIClient solrapiClient = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, - namespaceDAO); - assertNotNull(solrapiClient); - - when(props.getProperty("alfresco.port.ssl", "8443")).thenReturn("8444"); - SOLRAPIClient solrapiClient2 = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, - namespaceDAO); - assertNotNull(solrapiClient2); - - assertNotSame(solrapiClient, solrapiClient2); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +import static org.mockito.Mockito.*; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +import java.util.Properties; + +import org.alfresco.encryption.KeyResourceLoader; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.solr.client.SOLRAPIClient; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SOLRAPIClientFactoryTest +{ + @Mock + private Properties props; + @Mock + private KeyResourceLoader keyResourceLoader; + @Mock + private DictionaryService dictionaryService; + @Mock + private NamespaceDAO namespaceDAO; + private SOLRAPIClientFactory factory; + + @Before + public void setUp() throws Exception + { + when(props.getProperty(eq("alfresco.host"), anyString())).thenReturn("localhost"); + when(props.getProperty(eq("alfresco.port"), anyString())).thenReturn("8080"); + when(props.getProperty(eq("alfresco.port.ssl"), anyString())).thenReturn("8443"); + when(props.getProperty(eq("alfresco.maxTotalConnections"), anyString())).thenReturn("40"); + when(props.getProperty(eq("alfresco.maxHostConnections"), anyString())).thenReturn("40"); + when(props.getProperty(eq("alfresco.socketTimeout"), anyString())).thenReturn("0"); + when(props.getProperty(eq("alfresco.secureComms"), anyString())).thenReturn("none"); + + this.factory = new SOLRAPIClientFactory(); + } + + @Test + public void getsSameSOLRAPIClientForSameAlfresco() + { + SOLRAPIClient solrapiClient = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, + namespaceDAO); + assertNotNull(solrapiClient); + + SOLRAPIClient solrapiClient2 = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, + namespaceDAO); + assertNotNull(solrapiClient2); + + assertSame(solrapiClient, solrapiClient2); + } + + @Test + public void getsDifferentSOLRAPIClientForDifferentAlfresco() + { + SOLRAPIClient solrapiClient = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, + namespaceDAO); + assertNotNull(solrapiClient); + + when(props.getProperty("alfresco.port.ssl", "8443")).thenReturn("8444"); + SOLRAPIClient solrapiClient2 = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, + namespaceDAO); + assertNotNull(solrapiClient2); + + assertNotSame(solrapiClient, solrapiClient2); + } +} diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientTest.java index 0f4aa1e40..4e885c3d5 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientTest.java @@ -1,684 +1,684 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2020 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.solr.client; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.security.AlgorithmParameters; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.alfresco.encryption.DefaultEncryptionUtils; -import org.alfresco.encryption.KeyProvider; -import org.alfresco.encryption.KeyResourceLoader; -import org.alfresco.encryption.KeyStoreParameters; -import org.alfresco.encryption.MACUtils.MACInput; -import org.alfresco.encryption.ssl.SSLEncryptionParameters; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.httpclient.AlfrescoHttpClient; -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.httpclient.HttpClientFactory; -import org.alfresco.httpclient.HttpClientFactory.SecureCommsType; -import org.alfresco.opencmis.dictionary.CMISDictionaryRegistry; -import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; -import org.alfresco.opencmis.mapping.CMISMapping; -import org.alfresco.opencmis.mapping.RuntimePropertyLuceneBuilderMapping; -import org.alfresco.repo.cache.MemoryCache; -import org.alfresco.repo.dictionary.CompiledModelsCache; -import org.alfresco.repo.dictionary.DictionaryComponent; -import org.alfresco.repo.dictionary.DictionaryDAOImpl; -import org.alfresco.repo.dictionary.DictionaryNamespaceComponent; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.M2Namespace; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.repo.i18n.StaticMessageLookup; -import org.alfresco.repo.tenant.SingleTServiceImpl; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.DynamicallySizedThreadPoolExecutor; -import org.alfresco.util.Pair; -import org.alfresco.util.TraceableThreadFactory; -import org.alfresco.util.cache.DefaultAsynchronouslyRefreshedCacheRegistry; -import org.apache.chemistry.opencmis.commons.enums.CmisVersion; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.json.JSONException; - -import junit.framework.TestCase; - -/** - * Tests {@link SOLRAPIClient} Note: need to make sure that source/solr/instance is on the run classpath. Note: doesn't - * currently work, need to change to use SSL. - * - * @since 4.0 - */ -public class SOLRAPIClientTest extends TestCase -{ - private static Log logger = LogFactory.getLog(SOLRAPIClientTest.class); - - // private static final String TEST_MODEL = "org/alfresco/repo/dictionary/dictionarydaotest_model.xml"; - - private SOLRAPIClient client; - - private SOLRAPIClient invalidKeyClient; - - private SOLRAPIClient tamperWithClient; - - private DictionaryDAOImpl dictionaryDAO; - - private CMISStrictDictionaryService cmisDictionaryService; - - private static final String CORENAME = "collection1"; - - // private M2Model testModel; - - private void loadModel(Map modelMap, HashSet loadedModels, M2Model model) - { - String modelName = model.getName(); - if (loadedModels.contains(modelName) == false) - { - for (M2Namespace importNamespace : model.getImports()) - { - M2Model importedModel = modelMap.get(importNamespace.getUri()); - if (importedModel != null) - { - - // Ensure that the imported model is loaded first - loadModel(modelMap, loadedModels, importedModel); - } - } - - dictionaryDAO.putModelIgnoringConstraints(model); - loadedModels.add(modelName); - } - } - - @Override - public void setUp() throws Exception - { - if(client == null) - { - TenantService tenantService = new SingleTServiceImpl(); - - dictionaryDAO = new DictionaryDAOImpl(); - NamespaceDAO namespaceDAO = dictionaryDAO; - dictionaryDAO.setTenantService(tenantService); - - CompiledModelsCache compiledModelsCache = new CompiledModelsCache(); - compiledModelsCache.setDictionaryDAO(dictionaryDAO); - compiledModelsCache.setTenantService(tenantService); - compiledModelsCache.setRegistry(new DefaultAsynchronouslyRefreshedCacheRegistry()); - TraceableThreadFactory threadFactory = new TraceableThreadFactory(); - threadFactory.setThreadDaemon(true); - threadFactory.setThreadPriority(Thread.NORM_PRIORITY); - - ThreadPoolExecutor threadPoolExecutor = new DynamicallySizedThreadPoolExecutor(20, 20, 90, TimeUnit.SECONDS, new LinkedBlockingQueue(), threadFactory, - new ThreadPoolExecutor.CallerRunsPolicy()); - compiledModelsCache.setThreadPoolExecutor(threadPoolExecutor); - dictionaryDAO.setDictionaryRegistryCache(compiledModelsCache); - dictionaryDAO.setResourceClassLoader(getResourceClassLoader()); - dictionaryDAO.init(); - - DictionaryComponent dictionaryComponent = new DictionaryComponent(); - dictionaryComponent.setDictionaryDAO(dictionaryDAO); - dictionaryComponent.setMessageLookup(new StaticMessageLookup()); - - // cmis dictionary - CMISMapping cmisMapping = new CMISMapping(); - cmisMapping.setCmisVersion(CmisVersion.CMIS_1_0); - DictionaryNamespaceComponent namespaceService = new DictionaryNamespaceComponent(); - namespaceService.setNamespaceDAO(namespaceDAO); - cmisMapping.setNamespaceService(namespaceService); - cmisMapping.setDictionaryService(dictionaryComponent); - cmisMapping.afterPropertiesSet(); - - cmisDictionaryService = new CMISStrictDictionaryService(); - cmisDictionaryService.setCmisMapping(cmisMapping); - cmisDictionaryService.setDictionaryService(dictionaryComponent); - cmisDictionaryService.setDictionaryDAO(dictionaryDAO); - cmisDictionaryService.setSingletonCache(new MemoryCache()); - cmisDictionaryService.setTenantService(tenantService); - cmisDictionaryService.init(); - - RuntimePropertyLuceneBuilderMapping luceneBuilderMapping = new RuntimePropertyLuceneBuilderMapping(); - luceneBuilderMapping.setDictionaryService(dictionaryComponent); - luceneBuilderMapping.setCmisDictionaryService(cmisDictionaryService); - cmisDictionaryService.setPropertyLuceneBuilderMapping(luceneBuilderMapping); - - luceneBuilderMapping.afterPropertiesSet(); - - // Load the key store from the classpath - ClasspathKeyResourceLoader keyResourceLoader = new ClasspathKeyResourceLoader(); - client = new SOLRAPIClient(getRepoClient(keyResourceLoader), dictionaryComponent, dictionaryDAO); - trackModels(); - } - } - - private void trackModels() throws AuthenticationException, IOException, JSONException - { - - List modelDiffs = client.getModelsDiff(CORENAME,Collections. emptyList()); - HashMap modelMap = new HashMap(); - - for (AlfrescoModelDiff modelDiff : modelDiffs) - { - switch (modelDiff.getType()) - { - case CHANGED: - AlfrescoModel changedModel = client.getModel(CORENAME, modelDiff.getModelName()); - for (M2Namespace namespace : changedModel.getModel().getNamespaces()) - { - modelMap.put(namespace.getUri(), changedModel.getModel()); - } - break; - case NEW: - AlfrescoModel newModel = client.getModel(CORENAME, modelDiff.getModelName()); - for (M2Namespace namespace : newModel.getModel().getNamespaces()) - { - modelMap.put(namespace.getUri(), newModel.getModel()); - } - break; - case REMOVED: - // At the moment we do not unload models - I can see no side effects .... - // However search is used to check for references to indexed properties or types - // This will be partially broken anyway due to eventual consistency - // A model should only be unloaded if there are no data dependencies - // Should have been on the de-lucene list. - break; - } - } - - HashSet loadedModels = new HashSet(); - for (M2Model model : modelMap.values()) - { - loadModel(modelMap, loadedModels, model); - } - if (modelDiffs.size() > 0) - { - afterInitModels(); - } - - } - - public void afterInitModels() - { - cmisDictionaryService.afterDictionaryInit(); - } - - - protected AlfrescoHttpClient getRepoClient(ClasspathKeyResourceLoader keyResourceLoader) - { - // TODO i18n - KeyStoreParameters keyStoreParameters = new KeyStoreParameters("ssl-keystore", "SSL Key Store", "JCEKS", null, "ssl-keystore-passwords.properties", "ssl.repo.client.keystore"); - KeyStoreParameters trustStoreParameters = new KeyStoreParameters("ssl-truststore", "SSL Trust Store", "JCEKS", null, "ssl-truststore-passwords.properties", "ssl.repo.client.truststore"); - - SSLEncryptionParameters sslEncryptionParameters = new SSLEncryptionParameters(keyStoreParameters, trustStoreParameters); - - //MD5HttpClientFactory httpClientFactory = new MD5HttpClientFactory(); -// HttpClientFactory httpClientFactory = new HttpClientFactory(SecureComm); -// -// invalidKeyEncryptionService = new EncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); -// AlfrescoHttpClient repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, invalidKeyEncryptionService); -// //SecureHttpClient repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, invalidKeyEncryptionService); -// invalidKeyClient = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); -// -// keyStoreParameters.setLocation("org/alfresco/solr/client/.keystore"); -// tamperWithEncryptionService = new TamperWithEncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); -// repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, tamperWithEncryptionService); -//// repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, tamperWithEncryptionService); -// tamperWithClient = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); -// -// encryptionParameters.setMessageTimeout(30*1000); -// keyStoreParameters.setLocation("workspace-SpacesStore/conf/.keystore"); -// EncryptionService encryptionService = new EncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); -// repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, encryptionService); - //repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, encryptionService); -// client = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); - - HttpClientFactory httpClientFactory = new HttpClientFactory(SecureCommsType.getType("https"), sslEncryptionParameters, keyResourceLoader, null, null, "localhost", 8080, - 8443, 40, 40, 0); - // TODO need to make port configurable depending on secure comms, or just make redirects - // work - return httpClientFactory.getRepoClient("localhost", 8443); - } - - public ClassLoader getResourceClassLoader() - { - - File f = new File("woof", "alfrescoResources"); - if (f.canRead() && f.isDirectory()) - { - - URL[] urls = new URL[1]; - - try - { - URL url = f.toURI().normalize().toURL(); - urls[0] = url; - } - catch (MalformedURLException e) - { - throw new AlfrescoRuntimeException("Failed to add resources to classpath ", e); - } - - return URLClassLoader.newInstance(urls, this.getClass().getClassLoader()); - } - else - { - return this.getClass().getClassLoader(); - } - } - - private class ClasspathKeyResourceLoader implements KeyResourceLoader - { - @Override - public InputStream getKeyStore(String location) throws FileNotFoundException - { - return getClass().getClassLoader().getResourceAsStream(location); - } - - @Override - public Properties loadKeyMetaData(String location) throws IOException - { - Properties p = new Properties(); - p.load(getClass().getClassLoader().getResourceAsStream(location)); - return p; - } - } - - /** - * Full testing of ChangeSets, ACLs and Readers - */ - public void testGetAcls() throws Exception - { - AclChangeSets aclChangeSets = null; - - aclChangeSets = client.getAclChangeSets(null, null, null, null, 50); - assertTrue("Too many results", aclChangeSets.getAclChangeSets().size() <= 50); - if (aclChangeSets.getAclChangeSets().size() < 2) - { - return; // Not enough data - } - AclChangeSet aclChangeSetCheck = null; - AclChangeSet aclChangeSet0 = aclChangeSets.getAclChangeSets().get(0); - AclChangeSet aclChangeSet1 = aclChangeSets.getAclChangeSets().get(1); - long id0 = aclChangeSet0.getId(); - long commitTimeMs0 = aclChangeSet0.getCommitTimeMs(); - // Now query for the next ID - Long nextId = id0 + 1; - aclChangeSets = client.getAclChangeSets(commitTimeMs0, nextId, null, null, 1); - assertEquals(1, aclChangeSets.getAclChangeSets().size()); - aclChangeSetCheck = aclChangeSets.getAclChangeSets().get(0); - assertEquals(aclChangeSet1, aclChangeSetCheck); - - Map aclChangeSetsById = new HashMap(); - for (AclChangeSet aclChangeSet : aclChangeSets.getAclChangeSets()) - { - aclChangeSetsById.put(aclChangeSet.getId(), aclChangeSet); - } - - Set aclIdUniqueCheck = new HashSet(1000); - // Now do a large walk-through of the ACLs - Long minAclChangeSetId = null; - Long fromCommitTimeMs = null; - for (int i = 0; i < 100; i++) - { - aclChangeSets = client.getAclChangeSets(fromCommitTimeMs, minAclChangeSetId, null, null, 10); - if (aclChangeSets.getAclChangeSets().size() == 0) - { - break; - } - // Now repeat for the ACLs, keeping track of the last ChangeSet - Long nextAclId = null; - while (true) - { - List acls = client.getAcls(aclChangeSets.getAclChangeSets(), nextAclId, 512); - if (acls.size() == 0) - { - break; // Run out of ACLs - } - Set aclIds = new HashSet(1000); - for (Acl acl : acls) - { - long aclId = acl.getId(); - aclIds.add(aclId); - if (!aclIdUniqueCheck.add(aclId)) - { - // ignore duplicates for lazy/eager shared acl creation - //fail("ACL already processed: " + aclId); - } - // Check that we are ascending - if (nextAclId != null) - { - assertTrue("ACL IDs must be ascending: " + aclId, nextAclId.longValue() <= aclId); - } - nextAclId = aclId + 1; - } - // Now get the readers for these ACLs - List aclsReaders = client.getAclReaders(acls); - // Check that the ACL ids are all covered - for (AclReaders aclReaders : aclsReaders) - { - Long aclId = aclReaders.getId(); - aclIds.remove(aclId); - } - assertTrue("Some ACL IDs were not covered: " + aclIds, aclIds.size() == 0); - } - // March in time - AclChangeSet lastAclChangeSet = aclChangeSets.getAclChangeSets().get(aclChangeSets.getAclChangeSets().size() - 1); - fromCommitTimeMs = lastAclChangeSet.getCommitTimeMs(); - minAclChangeSetId = lastAclChangeSet.getId() + 1; - } - } - - public void testGetTransactions() throws Exception - { - // get transactions starting from txn id 1298288417234l - Transactions transactions = client.getTransactions(1298288417234l, null, null, null, 5); - - // get transactions starting from transaction 426 - transactions = client.getTransactions(null, Long.valueOf(1), null, null, 5); - List transactionIds = new ArrayList(transactions.getTransactions().size()); - for (Transaction info : transactions.getTransactions()) - { - logger.debug(info); - transactionIds.add(Long.valueOf(info.getId())); - } - - // get the first 3 nodes in those transactions - GetNodesParameters params = new GetNodesParameters(); - params.setTransactionIds(transactionIds); - List nodes = client.getNodes(params, 5); - for (Node info : nodes) - { - logger.debug(info); - } - - // get the next 3 nodes in those transactions i.e. starting from the last node id (inclusive) - params = new GetNodesParameters(); - params.setTransactionIds(transactionIds); - params.setFromNodeId(nodes.get(nodes.size() - 1).getId()); - nodes = client.getNodes(params, 3); - List nodeIds = new ArrayList(nodes.size()); - for (Node info : nodes) - { - logger.debug(info); - nodeIds.add(info.getId()); - } - - NodeMetaDataParameters metaParams = new NodeMetaDataParameters(); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - List metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - } - - public void testMetaData() throws AuthenticationException, IOException, JSONException - { - NodeMetaDataParameters metaParams = new NodeMetaDataParameters(); - List nodeIds = new ArrayList(1); - nodeIds.add(1L); - metaParams.setMaxResults(3); - metaParams.setNodeIds(nodeIds); - List metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(9L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(19L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - // individual tag/category - // TODO check why the category path has a null QName - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(49437L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - // content with tags - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(49431L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - MultiPropertyValue multi = (MultiPropertyValue) info.getProperties().get(QName.createQName("{http://www.alfresco.org/model/content/1.0}taggable")); - if(multi != null) - { - for (PropertyValue propValue : multi.getValues()) - { - logger.debug("multi property values = " + propValue); - } - } - } - - // content with null property values for author - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(117630L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - // content with accented characters in title properties - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(117678L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - } - - public void testGetModel() throws AuthenticationException, IOException, JSONException - { - AlfrescoModel alfModel = client.getModel(CORENAME,QName.createQName("http://www.alfresco.org/model/content/1.0", "contentmodel")); - M2Model model = alfModel.getModel(); - assertNotNull(model); - assertEquals("Returned model has incorrect name", "cm:contentmodel", model.getName()); - assertNotNull(alfModel.getChecksum()); - } - - public void testGetModelDiffs() throws AuthenticationException, IOException, JSONException - { - List diffs = client.getModelsDiff(CORENAME,Collections. emptyList()); - assertTrue(diffs.size() > 0); - } - -// public void testMAC() throws IOException, JSONException -// { -// // dodyClient has a secret key that is not the same as the repository's. This -// // should fail with a 401 -// try -// { -// Transactions transactions = invalidKeyClient.getTransactions(1298288417234l, null, null, null, 5); -// } -// catch (AuthenticationException e) -// { -// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); -// } -// -// try -// { -// tamperWithEncryptionService.setOverrideTimestamp(true); -// Transactions transactions = tamperWithClient.getTransactions(1298288417234l, null, null, null, 5); -// } -// catch (AuthenticationException e) -// { -// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); -// } -// finally -// { -// tamperWithEncryptionService.setOverrideTimestamp(false); -// } -// -// try -// { -// tamperWithEncryptionService.setOverrideMAC(true); -// Transactions transactions = tamperWithClient.getTransactions(1298288417234l, null, null, null, 5); -// } -// catch (AuthenticationException e) -// { -// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); -// } -// finally -// { -// tamperWithEncryptionService.setOverrideMAC(false); -// } -// } - - private void outputTextContent(SOLRAPIClient.GetTextContentResponse response) throws IOException - { - InputStream in = response.getContent(); - if (in != null) - { - logger.debug("Text content:"); - - BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); - String line = null; - while ((line = reader.readLine()) != null) - { - logger.debug(line); - } - } - } - - /** - * Overrides request encryption to create dodgy MAC and timestamp on requests - */ - private static class TestEncryptionUtils extends DefaultEncryptionUtils - { - private boolean overrideMAC = false; - - private boolean overrideTimestamp = false; - - public void setOverrideMAC(boolean overrideMAC) - { - this.overrideMAC = overrideMAC; - } - - public void setOverrideTimestamp(boolean overrideTimestamp) - { - this.overrideTimestamp = overrideTimestamp; - } - - @Override - public void setRequestAuthentication(HttpMethod method, byte[] message) throws IOException - { - if (method instanceof PostMethod) - { - // encrypt body - Pair encrypted = encryptor.encrypt(KeyProvider.ALIAS_SOLR, null, message); - setRequestAlgorithmParameters(method, encrypted.getSecond()); - - ((PostMethod) method).setRequestEntity(new ByteArrayRequestEntity(encrypted.getFirst(), "application/octet-stream")); - } - - long requestTimestamp = System.currentTimeMillis(); - - // add MAC header - byte[] mac = macUtils.generateMAC(KeyProvider.ALIAS_SOLR, new MACInput(message, requestTimestamp, getLocalIPAddress())); - - if (logger.isDebugEnabled()) - { - logger.debug("Setting MAC " + mac + " on HTTP request " + method.getPath()); - logger.debug("Setting timestamp " + requestTimestamp + " on HTTP request " + method.getPath()); - } - - if (overrideMAC) - { - mac[0] += (byte) 1; - } - setRequestMac(method, mac); - - if (overrideTimestamp) - { - requestTimestamp += 60000; - } - // prevent replays - setRequestTimestamp(method, requestTimestamp); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2021 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.solr.client; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.AlgorithmParameters; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.alfresco.encryption.DefaultEncryptionUtils; +import org.alfresco.encryption.KeyProvider; +import org.alfresco.encryption.KeyResourceLoader; +import org.alfresco.encryption.KeyStoreParameters; +import org.alfresco.encryption.MACUtils.MACInput; +import org.alfresco.encryption.ssl.SSLEncryptionParameters; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.AlfrescoHttpClient; +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.httpclient.HttpClientFactory; +import org.alfresco.httpclient.HttpClientFactory.SecureCommsType; +import org.alfresco.opencmis.dictionary.CMISDictionaryRegistry; +import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.opencmis.mapping.RuntimePropertyLuceneBuilderMapping; +import org.alfresco.repo.cache.MemoryCache; +import org.alfresco.repo.dictionary.CompiledModelsCache; +import org.alfresco.repo.dictionary.DictionaryComponent; +import org.alfresco.repo.dictionary.DictionaryDAOImpl; +import org.alfresco.repo.dictionary.DictionaryNamespaceComponent; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.M2Namespace; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.repo.tenant.SingleTServiceImpl; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.DynamicallySizedThreadPoolExecutor; +import org.alfresco.util.Pair; +import org.alfresco.util.TraceableThreadFactory; +import org.alfresco.util.cache.DefaultAsynchronouslyRefreshedCacheRegistry; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; + +import junit.framework.TestCase; + +/** + * Tests {@link SOLRAPIClient} Note: need to make sure that source/solr/instance is on the run classpath. Note: doesn't + * currently work, need to change to use SSL. + * + * @since 4.0 + */ +public class SOLRAPIClientTest extends TestCase +{ + private static Log logger = LogFactory.getLog(SOLRAPIClientTest.class); + + // private static final String TEST_MODEL = "org/alfresco/repo/dictionary/dictionarydaotest_model.xml"; + + private SOLRAPIClient client; + + private SOLRAPIClient invalidKeyClient; + + private SOLRAPIClient tamperWithClient; + + private DictionaryDAOImpl dictionaryDAO; + + private CMISStrictDictionaryService cmisDictionaryService; + + private static final String CORENAME = "collection1"; + + // private M2Model testModel; + + private void loadModel(Map modelMap, HashSet loadedModels, M2Model model) + { + String modelName = model.getName(); + if (loadedModels.contains(modelName) == false) + { + for (M2Namespace importNamespace : model.getImports()) + { + M2Model importedModel = modelMap.get(importNamespace.getUri()); + if (importedModel != null) + { + + // Ensure that the imported model is loaded first + loadModel(modelMap, loadedModels, importedModel); + } + } + + dictionaryDAO.putModelIgnoringConstraints(model); + loadedModels.add(modelName); + } + } + + @Override + public void setUp() throws Exception + { + if(client == null) + { + TenantService tenantService = new SingleTServiceImpl(); + + dictionaryDAO = new DictionaryDAOImpl(); + NamespaceDAO namespaceDAO = dictionaryDAO; + dictionaryDAO.setTenantService(tenantService); + + CompiledModelsCache compiledModelsCache = new CompiledModelsCache(); + compiledModelsCache.setDictionaryDAO(dictionaryDAO); + compiledModelsCache.setTenantService(tenantService); + compiledModelsCache.setRegistry(new DefaultAsynchronouslyRefreshedCacheRegistry()); + TraceableThreadFactory threadFactory = new TraceableThreadFactory(); + threadFactory.setThreadDaemon(true); + threadFactory.setThreadPriority(Thread.NORM_PRIORITY); + + ThreadPoolExecutor threadPoolExecutor = new DynamicallySizedThreadPoolExecutor(20, 20, 90, TimeUnit.SECONDS, new LinkedBlockingQueue(), threadFactory, + new ThreadPoolExecutor.CallerRunsPolicy()); + compiledModelsCache.setThreadPoolExecutor(threadPoolExecutor); + dictionaryDAO.setDictionaryRegistryCache(compiledModelsCache); + dictionaryDAO.setResourceClassLoader(getResourceClassLoader()); + dictionaryDAO.init(); + + DictionaryComponent dictionaryComponent = new DictionaryComponent(); + dictionaryComponent.setDictionaryDAO(dictionaryDAO); + dictionaryComponent.setMessageLookup(new StaticMessageLookup()); + + // cmis dictionary + CMISMapping cmisMapping = new CMISMapping(); + cmisMapping.setCmisVersion(CmisVersion.CMIS_1_0); + DictionaryNamespaceComponent namespaceService = new DictionaryNamespaceComponent(); + namespaceService.setNamespaceDAO(namespaceDAO); + cmisMapping.setNamespaceService(namespaceService); + cmisMapping.setDictionaryService(dictionaryComponent); + cmisMapping.afterPropertiesSet(); + + cmisDictionaryService = new CMISStrictDictionaryService(); + cmisDictionaryService.setCmisMapping(cmisMapping); + cmisDictionaryService.setDictionaryService(dictionaryComponent); + cmisDictionaryService.setDictionaryDAO(dictionaryDAO); + cmisDictionaryService.setSingletonCache(new MemoryCache()); + cmisDictionaryService.setTenantService(tenantService); + cmisDictionaryService.init(); + + RuntimePropertyLuceneBuilderMapping luceneBuilderMapping = new RuntimePropertyLuceneBuilderMapping(); + luceneBuilderMapping.setDictionaryService(dictionaryComponent); + luceneBuilderMapping.setCmisDictionaryService(cmisDictionaryService); + cmisDictionaryService.setPropertyLuceneBuilderMapping(luceneBuilderMapping); + + luceneBuilderMapping.afterPropertiesSet(); + + // Load the key store from the classpath + ClasspathKeyResourceLoader keyResourceLoader = new ClasspathKeyResourceLoader(); + client = new SOLRAPIClient(getRepoClient(keyResourceLoader), dictionaryComponent, dictionaryDAO); + trackModels(); + } + } + + private void trackModels() throws AuthenticationException, IOException, JSONException + { + + List modelDiffs = client.getModelsDiff(CORENAME,Collections. emptyList()); + HashMap modelMap = new HashMap(); + + for (AlfrescoModelDiff modelDiff : modelDiffs) + { + switch (modelDiff.getType()) + { + case CHANGED: + AlfrescoModel changedModel = client.getModel(CORENAME, modelDiff.getModelName()); + for (M2Namespace namespace : changedModel.getModel().getNamespaces()) + { + modelMap.put(namespace.getUri(), changedModel.getModel()); + } + break; + case NEW: + AlfrescoModel newModel = client.getModel(CORENAME, modelDiff.getModelName()); + for (M2Namespace namespace : newModel.getModel().getNamespaces()) + { + modelMap.put(namespace.getUri(), newModel.getModel()); + } + break; + case REMOVED: + // At the moment we do not unload models - I can see no side effects .... + // However search is used to check for references to indexed properties or types + // This will be partially broken anyway due to eventual consistency + // A model should only be unloaded if there are no data dependencies + // Should have been on the de-lucene list. + break; + } + } + + HashSet loadedModels = new HashSet(); + for (M2Model model : modelMap.values()) + { + loadModel(modelMap, loadedModels, model); + } + if (modelDiffs.size() > 0) + { + afterInitModels(); + } + + } + + public void afterInitModels() + { + cmisDictionaryService.afterDictionaryInit(); + } + + + protected AlfrescoHttpClient getRepoClient(ClasspathKeyResourceLoader keyResourceLoader) + { + // TODO i18n + KeyStoreParameters keyStoreParameters = new KeyStoreParameters("ssl-keystore", "SSL Key Store", "JCEKS", null, "ssl-keystore-passwords.properties", "ssl.repo.client.keystore"); + KeyStoreParameters trustStoreParameters = new KeyStoreParameters("ssl-truststore", "SSL Trust Store", "JCEKS", null, "ssl-truststore-passwords.properties", "ssl.repo.client.truststore"); + + SSLEncryptionParameters sslEncryptionParameters = new SSLEncryptionParameters(keyStoreParameters, trustStoreParameters); + + //MD5HttpClientFactory httpClientFactory = new MD5HttpClientFactory(); +// HttpClientFactory httpClientFactory = new HttpClientFactory(SecureComm); +// +// invalidKeyEncryptionService = new EncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); +// AlfrescoHttpClient repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, invalidKeyEncryptionService); +// //SecureHttpClient repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, invalidKeyEncryptionService); +// invalidKeyClient = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); +// +// keyStoreParameters.setLocation("org/alfresco/solr/client/.keystore"); +// tamperWithEncryptionService = new TamperWithEncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); +// repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, tamperWithEncryptionService); +//// repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, tamperWithEncryptionService); +// tamperWithClient = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); +// +// encryptionParameters.setMessageTimeout(30*1000); +// keyStoreParameters.setLocation("workspace-SpacesStore/conf/.keystore"); +// EncryptionService encryptionService = new EncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); +// repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, encryptionService); + //repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, encryptionService); +// client = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); + + HttpClientFactory httpClientFactory = new HttpClientFactory(SecureCommsType.getType("https"), sslEncryptionParameters, keyResourceLoader, null, null, "localhost", 8080, + 8443, 40, 40, 0); + // TODO need to make port configurable depending on secure comms, or just make redirects + // work + return httpClientFactory.getRepoClient("localhost", 8443); + } + + public ClassLoader getResourceClassLoader() + { + + File f = new File("woof", "alfrescoResources"); + if (f.canRead() && f.isDirectory()) + { + + URL[] urls = new URL[1]; + + try + { + URL url = f.toURI().normalize().toURL(); + urls[0] = url; + } + catch (MalformedURLException e) + { + throw new AlfrescoRuntimeException("Failed to add resources to classpath ", e); + } + + return URLClassLoader.newInstance(urls, this.getClass().getClassLoader()); + } + else + { + return this.getClass().getClassLoader(); + } + } + + private class ClasspathKeyResourceLoader implements KeyResourceLoader + { + @Override + public InputStream getKeyStore(String location) throws FileNotFoundException + { + return getClass().getClassLoader().getResourceAsStream(location); + } + + @Override + public Properties loadKeyMetaData(String location) throws IOException + { + Properties p = new Properties(); + p.load(getClass().getClassLoader().getResourceAsStream(location)); + return p; + } + } + + /** + * Full testing of ChangeSets, ACLs and Readers + */ + public void testGetAcls() throws Exception + { + AclChangeSets aclChangeSets = null; + + aclChangeSets = client.getAclChangeSets(null, null, null, null, 50); + assertTrue("Too many results", aclChangeSets.getAclChangeSets().size() <= 50); + if (aclChangeSets.getAclChangeSets().size() < 2) + { + return; // Not enough data + } + AclChangeSet aclChangeSetCheck = null; + AclChangeSet aclChangeSet0 = aclChangeSets.getAclChangeSets().get(0); + AclChangeSet aclChangeSet1 = aclChangeSets.getAclChangeSets().get(1); + long id0 = aclChangeSet0.getId(); + long commitTimeMs0 = aclChangeSet0.getCommitTimeMs(); + // Now query for the next ID + Long nextId = id0 + 1; + aclChangeSets = client.getAclChangeSets(commitTimeMs0, nextId, null, null, 1); + assertEquals(1, aclChangeSets.getAclChangeSets().size()); + aclChangeSetCheck = aclChangeSets.getAclChangeSets().get(0); + assertEquals(aclChangeSet1, aclChangeSetCheck); + + Map aclChangeSetsById = new HashMap(); + for (AclChangeSet aclChangeSet : aclChangeSets.getAclChangeSets()) + { + aclChangeSetsById.put(aclChangeSet.getId(), aclChangeSet); + } + + Set aclIdUniqueCheck = new HashSet(1000); + // Now do a large walk-through of the ACLs + Long minAclChangeSetId = null; + Long fromCommitTimeMs = null; + for (int i = 0; i < 100; i++) + { + aclChangeSets = client.getAclChangeSets(fromCommitTimeMs, minAclChangeSetId, null, null, 10); + if (aclChangeSets.getAclChangeSets().size() == 0) + { + break; + } + // Now repeat for the ACLs, keeping track of the last ChangeSet + Long nextAclId = null; + while (true) + { + List acls = client.getAcls(aclChangeSets.getAclChangeSets(), nextAclId, 512); + if (acls.size() == 0) + { + break; // Run out of ACLs + } + Set aclIds = new HashSet(1000); + for (Acl acl : acls) + { + long aclId = acl.getId(); + aclIds.add(aclId); + if (!aclIdUniqueCheck.add(aclId)) + { + // ignore duplicates for lazy/eager shared acl creation + //fail("ACL already processed: " + aclId); + } + // Check that we are ascending + if (nextAclId != null) + { + assertTrue("ACL IDs must be ascending: " + aclId, nextAclId.longValue() <= aclId); + } + nextAclId = aclId + 1; + } + // Now get the readers for these ACLs + List aclsReaders = client.getAclReaders(acls); + // Check that the ACL ids are all covered + for (AclReaders aclReaders : aclsReaders) + { + Long aclId = aclReaders.getId(); + aclIds.remove(aclId); + } + assertTrue("Some ACL IDs were not covered: " + aclIds, aclIds.size() == 0); + } + // March in time + AclChangeSet lastAclChangeSet = aclChangeSets.getAclChangeSets().get(aclChangeSets.getAclChangeSets().size() - 1); + fromCommitTimeMs = lastAclChangeSet.getCommitTimeMs(); + minAclChangeSetId = lastAclChangeSet.getId() + 1; + } + } + + public void testGetTransactions() throws Exception + { + // get transactions starting from txn id 1298288417234l + Transactions transactions = client.getTransactions(1298288417234l, null, null, null, 5); + + // get transactions starting from transaction 426 + transactions = client.getTransactions(null, Long.valueOf(1), null, null, 5); + List transactionIds = new ArrayList(transactions.getTransactions().size()); + for (Transaction info : transactions.getTransactions()) + { + logger.debug(info); + transactionIds.add(Long.valueOf(info.getId())); + } + + // get the first 3 nodes in those transactions + GetNodesParameters params = new GetNodesParameters(); + params.setTransactionIds(transactionIds); + List nodes = client.getNodes(params, 5); + for (Node info : nodes) + { + logger.debug(info); + } + + // get the next 3 nodes in those transactions i.e. starting from the last node id (inclusive) + params = new GetNodesParameters(); + params.setTransactionIds(transactionIds); + params.setFromNodeId(nodes.get(nodes.size() - 1).getId()); + nodes = client.getNodes(params, 3); + List nodeIds = new ArrayList(nodes.size()); + for (Node info : nodes) + { + logger.debug(info); + nodeIds.add(info.getId()); + } + + NodeMetaDataParameters metaParams = new NodeMetaDataParameters(); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + List metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + } + + public void testMetaData() throws AuthenticationException, IOException, JSONException + { + NodeMetaDataParameters metaParams = new NodeMetaDataParameters(); + List nodeIds = new ArrayList(1); + nodeIds.add(1L); + metaParams.setMaxResults(3); + metaParams.setNodeIds(nodeIds); + List metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(9L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(19L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + // individual tag/category + // TODO check why the category path has a null QName + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(49437L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + // content with tags + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(49431L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + MultiPropertyValue multi = (MultiPropertyValue) info.getProperties().get(QName.createQName("{http://www.alfresco.org/model/content/1.0}taggable")); + if(multi != null) + { + for (PropertyValue propValue : multi.getValues()) + { + logger.debug("multi property values = " + propValue); + } + } + } + + // content with null property values for author + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(117630L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + // content with accented characters in title properties + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(117678L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + } + + public void testGetModel() throws AuthenticationException, IOException, JSONException + { + AlfrescoModel alfModel = client.getModel(CORENAME,QName.createQName("http://www.alfresco.org/model/content/1.0", "contentmodel")); + M2Model model = alfModel.getModel(); + assertNotNull(model); + assertEquals("Returned model has incorrect name", "cm:contentmodel", model.getName()); + assertNotNull(alfModel.getChecksum()); + } + + public void testGetModelDiffs() throws AuthenticationException, IOException, JSONException + { + List diffs = client.getModelsDiff(CORENAME,Collections. emptyList()); + assertTrue(diffs.size() > 0); + } + +// public void testMAC() throws IOException, JSONException +// { +// // dodyClient has a secret key that is not the same as the repository's. This +// // should fail with a 401 +// try +// { +// Transactions transactions = invalidKeyClient.getTransactions(1298288417234l, null, null, null, 5); +// } +// catch (AuthenticationException e) +// { +// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); +// } +// +// try +// { +// tamperWithEncryptionService.setOverrideTimestamp(true); +// Transactions transactions = tamperWithClient.getTransactions(1298288417234l, null, null, null, 5); +// } +// catch (AuthenticationException e) +// { +// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); +// } +// finally +// { +// tamperWithEncryptionService.setOverrideTimestamp(false); +// } +// +// try +// { +// tamperWithEncryptionService.setOverrideMAC(true); +// Transactions transactions = tamperWithClient.getTransactions(1298288417234l, null, null, null, 5); +// } +// catch (AuthenticationException e) +// { +// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); +// } +// finally +// { +// tamperWithEncryptionService.setOverrideMAC(false); +// } +// } + + private void outputTextContent(SOLRAPIClient.GetTextContentResponse response) throws IOException + { + InputStream in = response.getContent(); + if (in != null) + { + logger.debug("Text content:"); + + BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); + String line = null; + while ((line = reader.readLine()) != null) + { + logger.debug(line); + } + } + } + + /** + * Overrides request encryption to create dodgy MAC and timestamp on requests + */ + private static class TestEncryptionUtils extends DefaultEncryptionUtils + { + private boolean overrideMAC = false; + + private boolean overrideTimestamp = false; + + public void setOverrideMAC(boolean overrideMAC) + { + this.overrideMAC = overrideMAC; + } + + public void setOverrideTimestamp(boolean overrideTimestamp) + { + this.overrideTimestamp = overrideTimestamp; + } + + @Override + public void setRequestAuthentication(HttpMethod method, byte[] message) throws IOException + { + if (method instanceof PostMethod) + { + // encrypt body + Pair encrypted = encryptor.encrypt(KeyProvider.ALIAS_SOLR, null, message); + setRequestAlgorithmParameters(method, encrypted.getSecond()); + + ((PostMethod) method).setRequestEntity(new ByteArrayRequestEntity(encrypted.getFirst(), "application/octet-stream")); + } + + long requestTimestamp = System.currentTimeMillis(); + + // add MAC header + byte[] mac = macUtils.generateMAC(KeyProvider.ALIAS_SOLR, new MACInput(message, requestTimestamp, getLocalIPAddress())); + + if (logger.isDebugEnabled()) + { + logger.debug("Setting MAC " + mac + " on HTTP request " + method.getPath()); + logger.debug("Setting timestamp " + requestTimestamp + " on HTTP request " + method.getPath()); + } + + if (overrideMAC) + { + mac[0] += (byte) 1; + } + setRequestMac(method, mac); + + if (overrideTimestamp) + { + requestTimestamp += 60000; + } + // prevent replays + setRequestTimestamp(method, requestTimestamp); + } + } +} diff --git a/search-services/packaging/src/test/java/org/alfresco/ThirdPartyLicensesIT.java b/search-services/packaging/src/test/java/org/alfresco/ThirdPartyLicensesIT.java index 17bb08092..47b1a06d3 100644 --- a/search-services/packaging/src/test/java/org/alfresco/ThirdPartyLicensesIT.java +++ b/search-services/packaging/src/test/java/org/alfresco/ThirdPartyLicensesIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of From b071000ab125516bccc7460d70f3d6ab7107ab8c Mon Sep 17 00:00:00 2001 From: Angel Borroy Date: Tue, 18 May 2021 17:09:41 +0200 Subject: [PATCH 7/8] Revert "SEARCH-2548: Disable documentCache for SOLR Core, as properties are not re-used from SOLR side ever." This reverts commit e6e00b2c98c79df0d9f004fafb4106111478ac21. --- .../solr/instance/templates/rerank/conf/solrcore.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/solrcore.properties b/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/solrcore.properties index 9d1901b4a..f2c1b9a2d 100644 --- a/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/solrcore.properties +++ b/search-services/alfresco-search/src/main/resources/solr/instance/templates/rerank/conf/solrcore.properties @@ -103,8 +103,8 @@ solr.filterCache.size=256 solr.filterCache.initialSize=128 solr.queryResultCache.size=1024 solr.queryResultCache.initialSize=1024 -solr.documentCache.size=0 -solr.documentCache.initialSize=0 +solr.documentCache.size=1024 +solr.documentCache.initialSize=1024 solr.queryResultMaxDocsCached=2048 solr.authorityCache.size=128 @@ -155,7 +155,7 @@ solr.deniedCache.autowarmCount=0 solr.readerCache.autowarmCount=0 solr.ownerCache.autowarmCount=0 solr.queryResultCache.autowarmCount=4 -solr.documentCache.autowarmCount=0 +solr.documentCache.autowarmCount=512 solr.queryResultWindowSize=512 From 5bdb8727b0d4b6111de960e9e0b246e625ccfac0 Mon Sep 17 00:00:00 2001 From: Angel Borroy Date: Tue, 18 May 2021 17:09:58 +0200 Subject: [PATCH 8/8] Revert "SEARCH-2548: Upgrade license headers" This reverts commit c3298a0af67fd24f69004135c349df8f8e132d3c. --- .../java/org/alfresco/search/TestGroup.java | 114 +- .../functional/AbstractE2EFunctionalTest.java | 2 +- .../cmis/AbstractCmisE2ETest.java | 2 +- .../cmis/SolrSearchByAspectTests.java | 2 +- .../cmis/SolrSearchByIdTests.java | 2 +- .../cmis/SolrSearchByPathTests.java | 2 +- .../cmis/SolrSearchByPropertyTests.java | 2 +- .../cmis/SolrSearchInFolderTests.java | 2 +- .../cmis/SolrSearchInTreeTests.java | 2 +- .../cmis/SolrSearchScoreQueryTests.java | 2 +- .../cmis/SorlSearchSimpleQueryTests.java | 2 +- .../searchServices/sanity/SetupTest.java | 4 +- .../search/AbstractSearchServicesE2ETest.java | 2 +- .../search/ExplicitRoutingTest.java | 2 +- .../search/FacetFieldsSearchTest.java | 2 +- .../search/FacetIntervalSearchTest.java | 2 +- .../search/FacetRangeSearchTest.java | 4 +- .../search/FacetedSearchTest.java | 4 +- .../search/FieldDefinitionTest.java | 2 +- .../search/FingerPrintTest.java | 2 +- .../search/PivotFacetedSearchTest.java | 2 +- .../search/SearchAFTSInFieldTest.java | 2 +- .../search/SearchAPATHTest.java | 4 +- .../search/SearchAspectTest.java | 2 +- .../search/SearchHighLightTest.java | 2 +- .../search/SearchNonIndexedFields.java | 2 +- .../search/SearchParentField.java | 2 +- .../search/SearchPermissionsTest.java | 2 +- .../search/SearchQueryPaginationTest.java | 2 +- .../SearchSecondaryAssociationTest.java | 2 +- .../search/SearchSpellCheckTest.java | 4 +- .../searchServices/search/SearchTest.java | 2 +- .../search/SearchWithCustomModelTest.java | 2 +- .../searchServices/search/ShardInfoTest.java | 2 +- .../search/SpecialCharacterSearchTest.java | 2 +- .../search/StatsSearchTest.java | 4 +- .../tracker/CascadingIntegrationTest.java | 2 +- .../solr/SearchSolrAPITest.java | 4 +- .../solr/admin/SolrE2eActionTest.java | 2 +- .../solr/admin/SolrE2eAdminTest.java | 2 +- .../backup/AbstractBackupE2ETest.java | 2 +- .../backup/SearchServicesOnBackupTests.java | 2 +- .../backup/SearchServicesPostBackupTests.java | 2 +- .../backup/SearchServicesPreBackupTests.java | 2 +- .../upgrade/AbstractUpgradeE2ETest.java | 4 +- .../upgrade/SearchServiceUpgradeTests.java | 2 +- .../lucene/analysis/AnalysisException.java | 126 +- .../impl/lucene/analysis/MLAnalayser.java | 782 +- .../lucene/analysis/MLTokenDuplicator.java | 568 +- .../impl/lucene/analysis/PathAnalyzer.java | 94 +- .../impl/lucene/analysis/PathTokenFilter.java | 792 +- .../solr/AlfrescoAnalyzerWrapper.java | 218 +- .../AlfrescoCollatableMLTextFieldType.java | 546 +- .../solr/AlfrescoCollatableTextFieldType.java | 490 +- .../solr/AlfrescoCoreAdminHandler.java | 4 +- .../org/alfresco/solr/AlfrescoFieldType.java | 542 +- .../alfresco/solr/AlfrescoLockException.java | 2 +- ...lfrescoSolr4FunctionEvaluationContext.java | 152 +- .../alfresco/solr/AlfrescoSolrDataModel.java | 3784 +++--- .../main/java/org/alfresco/solr/Cloud.java | 336 +- .../org/alfresco/solr/ContextAwareQuery.java | 240 +- .../org/alfresco/solr/DualPivotQuickSort.java | 2 +- .../org/alfresco/solr/HandlerOfResources.java | 2 +- .../alfresco/solr/HandlerReportHelper.java | 4 +- .../org/alfresco/solr/InformationServer.java | 2 +- .../alfresco/solr/SolrInformationServer.java | 4 +- .../alfresco/solr/SolrKeyResourceLoader.java | 152 +- .../alfresco/solr/StripLocaleStrField.java | 4 +- .../alfresco/solr/TrackerStateException.java | 158 +- .../solr/adapters/SolrOpenBitSetAdapter.java | 154 +- .../solr/adapters/SolrSimpleOrderedMap.java | 78 +- .../solr/cache/AuthorityCacheRegenerator.java | 116 +- .../alfresco/solr/cache/CacheConstants.java | 96 +- .../solr/cache/PathCacheRegenerator.java | 118 +- .../solr/component/ClearLocaleComponent.java | 156 +- .../solr/component/ConsistencyComponent.java | 210 +- .../solr/component/FingerPrintComponent.java | 4 +- .../solr/component/QueryLoggingComponent.java | 2 +- .../RewriteFacetCountsComponent.java | 558 +- .../RewriteFacetParametersComponent.java | 1136 +- .../component/RewriteFieldListComponent.java | 2 +- .../solr/component/SetLocaleComponent.java | 178 +- .../SetProcessedDeniesComponent.java | 174 +- .../solr/component/TempFileWarningLogger.java | 218 +- ...AlfrescoSpellCheckBackCompatComponent.java | 2 +- .../AlfrescoSpellCheckCollation.java | 110 +- .../AlfrescoSpellCheckCollator.java | 738 +- .../AlfrescoSpellCheckComponent.java | 232 +- .../org/alfresco/solr/config/ConfigUtil.java | 2 +- .../org/alfresco/solr/data/GlobalReaders.java | 108 +- .../solr/lifecycle/SolrCoreLoadListener.java | 4 +- .../java/org/alfresco/solr/logging/Log.java | 2 +- .../AbsoluteStructuredFieldPosition.java | 284 +- .../solr/query/AbstractAuthorityQuery.java | 152 +- .../query/AbstractAuthorityQueryWeight.java | 170 +- .../solr/query/AbstractAuthoritySetQuery.java | 384 +- .../alfresco/solr/query/AbstractQParser.java | 1414 +- .../solr/query/AbstractSolrCachingScorer.java | 396 +- .../AbstractStructuredFieldPosition.java | 338 +- .../solr/query/AlfrescoDefaultTextFields.java | 2 +- .../solr/query/AlfrescoFTSQParserPlugin.java | 306 +- .../query/AlfrescoLuceneQParserPlugin.java | 298 +- .../query/AlfrescoReRankQParserPlugin.java | 1044 +- .../solr/query/AllAccessCollector.java | 74 +- .../query/AnyStructuredFieldPosition.java | 206 +- .../org/alfresco/solr/query/BitsFilter.java | 210 +- .../solr/query/CachingTermPositions.java | 420 +- .../solr/query/CmisQParserPlugin.java | 414 +- .../ContentSizeGroupingAnalyticsQuery.java | 126 +- .../query/ContentSizeGroupingCollector.java | 262 +- .../ContentSizeGroupingQParserPlugin.java | 220 +- ...cendantAndSelfStructuredFieldPosition.java | 106 +- .../alfresco/solr/query/DocValuesCache.java | 286 +- .../solr/query/EmptyHybridBitSet.java | 80 +- .../org/alfresco/solr/query/HybridBitSet.java | 160 +- .../Lucene4QueryBuilderContextSolrImpl.java | 212 +- .../solr/query/Lucene4QueryParserAdaptor.java | 854 +- .../query/MimetypeGroupingAnalyticsQuery.java | 130 +- .../solr/query/MimetypeGroupingCollector.java | 264 +- .../query/MimetypeGroupingQParserPlugin.java | 326 +- .../alfresco/solr/query/PostFilterQuery.java | 514 +- .../RelativeStructuredFieldPosition.java | 284 +- .../SelfAxisStructuredFieldPosition.java | 124 +- .../alfresco/solr/query/Solr4QueryParser.java | 11126 ++++++++-------- .../solr/query/SolrAuthorityQuery.java | 182 +- .../solr/query/SolrAuthorityScorer.java | 218 +- .../solr/query/SolrAuthoritySetQuery.java | 744 +- .../solr/query/SolrAuthoritySetScorer.java | 272 +- .../solr/query/SolrCachingPathQuery.java | 216 +- .../solr/query/SolrCachingPathScorer.java | 144 +- .../solr/query/SolrCachingPathWeight.java | 168 +- .../solr/query/SolrContainerScorer.java | 1162 +- .../alfresco/solr/query/SolrDeniedQuery.java | 180 +- .../alfresco/solr/query/SolrDeniedScorer.java | 178 +- .../alfresco/solr/query/SolrDenySetQuery.java | 312 +- .../solr/query/SolrDenySetScorer.java | 2 +- .../solr/query/SolrDenySetScorer2.java | 250 +- .../alfresco/solr/query/SolrOwnerQuery.java | 254 +- .../alfresco/solr/query/SolrOwnerScorer.java | 150 +- .../solr/query/SolrOwnerSetQuery.java | 184 +- .../solr/query/SolrOwnerSetScorer.java | 184 +- .../alfresco/solr/query/SolrPathQuery.java | 674 +- .../alfresco/solr/query/SolrPathScorer.java | 256 +- .../alfresco/solr/query/SolrReaderQuery.java | 182 +- .../alfresco/solr/query/SolrReaderScorer.java | 178 +- .../solr/query/SolrReaderSetQuery.java | 184 +- .../solr/query/SolrReaderSetScorer.java | 208 +- .../solr/query/SolrReaderSetScorer2.java | 242 +- .../alfresco/solr/query/SolrXPathHandler.java | 1066 +- .../solr/query/StructuredFieldPosition.java | 248 +- .../solr/query/StructuredFieldTerm.java | 130 +- .../schema/highlight/CompositeReader.java | 4 +- .../LanguagePrefixedTextAnalyzer.java | 4 +- .../highlight/LanguagePrefixedTextField.java | 4 +- .../LanguagePrefixedTokenStream.java | 4 +- .../solr/schema/highlight/package-info.java | 4 +- .../solr/servlet/Solr4X509ServletFilter.java | 578 +- .../alfresco/solr/tracker/ACLIDModRouter.java | 4 +- .../solr/tracker/ACLIDMurmurRouter.java | 4 +- .../solr/tracker/AbstractTracker.java | 944 +- .../alfresco/solr/tracker/AbstractWorker.java | 2 +- .../org/alfresco/solr/tracker/AclTracker.java | 1878 +-- .../solr/tracker/ActivatableTracker.java | 8 +- .../alfresco/solr/tracker/CascadeTracker.java | 2 +- .../alfresco/solr/tracker/CommitTracker.java | 2 +- .../solr/tracker/ComposableDocRouter.java | 2 +- .../alfresco/solr/tracker/ContentTracker.java | 2 +- .../solr/tracker/DBIDRangeRouter.java | 2 +- .../org/alfresco/solr/tracker/DBIDRouter.java | 4 +- .../solr/tracker/DateMonthRouter.java | 2 +- .../solr/tracker/DateQuarterRouter.java | 4 +- .../org/alfresco/solr/tracker/DocRouter.java | 2 +- .../solr/tracker/DocRouterFactory.java | 2 +- .../solr/tracker/DocRouterWithFallback.java | 2 +- ...licitShardIdWithDynamicPropertyRouter.java | 2 +- ...plicitShardIdWithStaticPropertyRouter.java | 4 +- .../solr/tracker/MetadataTracker.java | 2 +- .../alfresco/solr/tracker/ModelTracker.java | 1160 +- .../alfresco/solr/tracker/PropertyRouter.java | 2 +- .../solr/tracker/ShardStatePublisher.java | 2 +- .../solr/tracker/SolrTrackerScheduler.java | 2 +- .../org/alfresco/solr/tracker/Tracker.java | 2 +- .../org/alfresco/solr/tracker/TrackerJob.java | 148 +- .../solr/tracker/TrackerRegistry.java | 212 +- .../pool/DefaultTrackerPoolFactory.java | 358 +- .../solr/tracker/pool/TrackerPoolFactory.java | 78 +- .../AlfrescoFieldMapperTransformer.java | 4 +- ...AlfrescoFieldMapperTransformerFactory.java | 2 +- .../transformer/DocValueDocTransformer.java | 378 +- .../DocValueDocTransformerFactory.java | 100 +- ...escoDistributedUpdateProcessorFactory.java | 8 +- .../AlfrescoExplicitDocumentMerger.java | 8 +- .../solr/utils/AlfrescoFileUtils.java | 2 +- .../alfresco/solr/utils/ThrowingConsumer.java | 2 +- .../alfresco/solr/utils/ThrowingFunction.java | 2 +- .../java/org/alfresco/solr/utils/Utils.java | 4 +- .../minhash/ContextAccumulatingFilter.java | 2 +- .../ContextAccumulatingFilterFactory.java | 2 +- .../solr/core/CoreDescriptorDecorator.java | 216 +- .../component/AlfrescoHttpShardHandler.java | 102 +- .../AlfrescoHttpShardHandlerFactory.java | 100 +- .../component/AlfrescoLukeRequestHandler.java | 1704 +-- .../component/AlfrescoQueryRequest.java | 154 +- .../component/AlfrescoSearchHandler.java | 1430 +- .../component/AlfrescoSolrHighlighter.java | 924 +- .../impl/lucene/analysis/MLAnalayserTest.java | 364 +- .../lucene/analysis/PathTokenFilterTest.java | 440 +- .../solr/AbstractAlfrescoDistributedIT.java | 4 +- .../alfresco/solr/AbstractAlfrescoSolrIT.java | 2 +- .../solr/AdminHandlerDistributedIT.java | 4 +- .../org/alfresco/solr/AdminHandlerIT.java | 2 +- ...AlfrescoCollatableMLTextFieldTypeTest.java | 416 +- .../AlfrescoCollatableTextFieldTypeTest.java | 352 +- .../solr/AlfrescoCoreAdminHandlerIT.java | 4 +- .../solr/AlfrescoCoreAdminHandlerTest.java | 4 +- ...dexingStatePersistenceAcrossReloadsIT.java | 4 +- .../solr/AlfrescoJsonQueryRequest.java | 2 +- .../alfresco/solr/AlfrescoSolrConstants.java | 4 +- .../alfresco/solr/AlfrescoSolrReloadIT.java | 4 +- .../org/alfresco/solr/AlfrescoSolrUtils.java | 4 +- .../solr/AlfrescoTrackerRegistrationIT.java | 2 +- .../alfresco/solr/CMISDataCreatorTest.java | 900 +- .../solr/CoresCreateUpdateDistributedIT.java | 4 +- .../solr/CoresCreateViaPropertyIT.java | 2 +- .../alfresco/solr/DynamicCopyFieldsIT.java | 2 +- .../alfresco/solr/HandlerOfResourcesTest.java | 4 +- .../org/alfresco/solr/SolrDataModelTest.java | 112 +- .../org/alfresco/solr/SolrITInitializer.java | 4 +- .../solr/SolrInformationServerTest.java | 842 +- .../java/org/alfresco/solr/SolrTestFiles.java | 2 +- .../alfresco/solr/TemplatesDistributedIT.java | 2 +- .../alfresco/solr/basics/RandomSupplier.java | 2 +- .../solr/basics/SolrResponsesComparator.java | 2 +- .../RewriteFacetParametersComponentTest.java | 660 +- .../component/TempFileWarningLoggerTest.java | 358 +- .../alfresco/solr/config/ConfigUtilTest.java | 4 +- .../solr/dataload/TestDataProvider.java | 4 +- .../AlfrescoHighligherDistributedIT.java | 2 +- .../solr/highlight/AlfrescoHighlighterIT.java | 4 +- .../lifecycle/SolrCoreLoadListenerTest.java | 4 +- .../solr/query/AlfrescoLuceneQParserTest.java | 124 +- .../query/AlfrescoReRankQParserPluginIT.java | 1410 +- .../solr/query/AlfrescoSolrFingerprintIT.java | 2 +- .../solr/query/AlfrescoSolrSortIT.java | 4 +- .../query/AlfrescoSolrSpellcheckerIT.java | 2 +- .../org/alfresco/solr/query/AuthDataLoad.java | 2 +- .../org/alfresco/solr/query/AuthQueryIT.java | 2 +- .../DistributedAlfrescoSolrFacetingIT.java | 2 +- .../DistributedAlfrescoSolrFingerPrintIT.java | 4 +- ...DistributedAlfrescoSolrSpellcheckerIT.java | 2 +- .../solr/query/Solr4QueryParserTest.java | 2 +- .../org/alfresco/solr/query/SolrAuthIT.java | 366 +- .../solr/query/UntokenisedFieldIT.java | 2 +- .../afts/qparser/AbstractQParserPluginIT.java | 2 +- .../afts/qparser/FieldNameEscapingIT.java | 4 +- .../query/afts/qparser/QParserPluginIT.java | 4 +- .../AFTSDefaultTextQueryIT.java | 2 +- .../requestHandler/AFTSDisjunctionIT.java | 4 +- .../AFTSIdentifierFieldsIT.java | 4 +- .../afts/requestHandler/AFTSRangeQueryIT.java | 4 +- .../requestHandler/AFTSRequestHandlerIT.java | 4 +- .../AbstractRequestHandlerIT.java | 2 +- .../solr/query/afts/requestHandler/MNTIT.java | 4 +- .../cmis/AlfrescoCMISQParserPluginIT.java | 4 +- .../org/alfresco/solr/query/cmis/CmisIT.java | 2 +- .../solr/query/cmis/LoadCMISData.java | 2 +- .../solr/query/cmis/SortCMISTest.java | 2 +- .../solr/query/cmis/TrieFieldsCMISTest.java | 2 +- .../schema/highlight/CompositeReaderTest.java | 2 +- .../LanguagePrefixedTokenStreamTest.java | 2 +- .../servlet/Solr4X509ServletFilterTest.java | 2 +- .../solr/tracker/ACLIDMurmurRouterTest.java | 4 +- .../solr/tracker/AclModCountRouterIT.java | 4 +- .../solr/tracker/ActivatableTrackerTest.java | 8 +- .../AlfrescoSolrTrackerExceptionIT.java | 2 +- .../solr/tracker/AlfrescoSolrTrackerIT.java | 2 +- .../AlfrescoSolrTrackerRollbackIT.java | 2 +- .../tracker/AlfrescoSolrTrackerStateIT.java | 4 +- .../alfresco/solr/tracker/CascadingIT.java | 4 +- .../ContentPropertyValueTrackerIT.java | 4 +- .../solr/tracker/ContentTrackerIT.java | 2 +- .../solr/tracker/DBIDRangeRouterTest.java | 4 +- .../alfresco/solr/tracker/DBIDRouterTest.java | 4 +- .../solr/tracker/DateMonthRouterIT.java | 4 +- ...DistributedAclIdAlfrescoSolrTrackerIT.java | 2 +- .../DistributedAlfrescoSolrJsonIT.java | 2 +- .../DistributedAlfrescoSolrTrackerIT.java | 4 +- ...ibutedAlfrescoSolrTrackerLargeTxnIdIT.java | 4 +- .../DistributedAlfrescoSolrTrackerRaceIT.java | 4 +- ...DistributedAlfrescoSolrTrackerStateIT.java | 4 +- ...rescoTrackerWithDelayedTransactionsIT.java | 8 +- .../tracker/DistributedCascadeTrackerIT.java | 4 +- .../DistributedDateAbstractSolrTrackerIT.java | 2 +- ...ributedDateMonthAlfrescoSolrTrackerIT.java | 2 +- ...butedDateQuarterAlfrescoSolrTrackerIT.java | 2 +- ...tedDateSplitYearAlfrescoSolrTrackerIT.java | 2 +- ...ributedDbidRangeAlfrescoSolrTrackerIT.java | 2 +- ...dExpandDbidRangeAlfrescoSolrTrackerIT.java | 2 +- ...icitShardIdWithStaticPropertyRouterIT.java | 4 +- ...tributedExplicitShardRoutingTrackerIT.java | 2 +- ...tedPropertyBasedAlfrescoSolrTrackerIT.java | 2 +- ...RemovePropertiesAlfrescoSolrTrackerIT.java | 10 +- .../solr/tracker/DocRouterFactoryTest.java | 136 +- .../solr/tracker/ExplicitIDRouterIT.java | 4 +- .../tracker/ExplicitIDWithLRISRouterIT.java | 4 +- ...tShardIdWithDynamicPropertyRouterTest.java | 4 +- ...itShardIdWithStaticPropertyRouterTest.java | 4 +- .../solr/tracker/MetadataTrackerTest.java | 2 +- .../alfresco/solr/tracker/ModelTrackerIT.java | 400 +- .../solr/tracker/PropertyRouterIT.java | 4 +- .../tracker/SolrTrackerSchedulerTest.java | 432 +- .../alfresco/solr/tracker/TrackerJobTest.java | 158 +- .../solr/tracker/TrackerRegistryTest.java | 2 +- .../pool/DefaultTrackerPoolFactoryTest.java | 322 +- .../AlfrescoFieldMapperTransformerIT.java | 2 +- .../AlfrescoLukeRequestHandlerIT.java | 2 +- .../AlfrescoSearchHandlerFacetQueryIT.java | 2 +- .../java/org/alfresco/solr/AclReport.java | 2 +- ...lfrescoClientDataModelServicesFactory.java | 2 +- .../java/org/alfresco/solr/BoundedDeque.java | 2 +- .../solr/IndexTrackingShutdownException.java | 2 +- .../InformationServerCollectionProvider.java | 2 +- .../java/org/alfresco/solr/NodeReport.java | 2 +- .../java/org/alfresco/solr/TrackerState.java | 2 +- .../alfresco/solr/adapters/IOpenBitSet.java | 36 +- .../solr/adapters/ISimpleOrderedMap.java | 30 +- .../java/org/alfresco/solr/client/Acl.java | 114 +- .../alfresco/solr/client/AclChangeSet.java | 132 +- .../alfresco/solr/client/AclChangeSets.java | 2 +- .../org/alfresco/solr/client/AclReaders.java | 250 +- .../alfresco/solr/client/AlfrescoModel.java | 166 +- .../solr/client/AlfrescoModelDiff.java | 212 +- .../solr/client/ContentPropertyValue.java | 120 +- .../solr/client/GetNodesParameters.java | 346 +- .../solr/client/LookAheadBufferedReader.java | 4 +- .../solr/client/MLTextPropertyValue.java | 110 +- .../solr/client/MultiPropertyValue.java | 132 +- .../java/org/alfresco/solr/client/Node.java | 288 +- .../alfresco/solr/client/NodeMetaData.java | 364 +- .../solr/client/NodeMetaDataParameters.java | 484 +- .../alfresco/solr/client/PropertyValue.java | 24 +- .../alfresco/solr/client/SOLRAPIClient.java | 3274 ++--- .../solr/client/SOLRAPIClientFactory.java | 2 +- .../solr/client/SOLRAPIQueueClient.java | 2 +- .../java/org/alfresco/solr/client/Status.java | 382 +- .../solr/client/StringPropertyValue.java | 58 +- .../org/alfresco/solr/client/Transaction.java | 174 +- .../alfresco/solr/client/Transactions.java | 78 +- .../solr/tracker/IndexHealthReport.java | 820 +- .../alfresco/solr/tracker/TrackerStats.java | 1908 +-- .../org/alfresco/solr/BoundedDequeTest.java | 114 +- .../alfresco/solr/client/AclReadersTest.java | 122 +- .../client/LookAheadBufferedReaderTest.java | 4 +- .../solr/client/SOLRAPIClientFactoryTest.java | 202 +- .../solr/client/SOLRAPIClientTest.java | 1368 +- .../org/alfresco/ThirdPartyLicensesIT.java | 2 +- 356 files changed, 36492 insertions(+), 36492 deletions(-) diff --git a/e2e-test/src/main/java/org/alfresco/search/TestGroup.java b/e2e-test/src/main/java/org/alfresco/search/TestGroup.java index 1f319397e..3e981287f 100644 --- a/e2e-test/src/main/java/org/alfresco/search/TestGroup.java +++ b/e2e-test/src/main/java/org/alfresco/search/TestGroup.java @@ -1,57 +1,57 @@ -/* - * #%L - * Alfresco Search Services E2E Test - * %% - * Copyright (C) 2005 - 2021 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.search; - -public class TestGroup -{ - // Used for TestRail test annotation - public static final String SEARCH = "search"; - public static final String REST_API = "rest-api"; - - public static final String PREUPGRADE = "pre-upgrade"; - public static final String POSTUPGRADE = "post-upgrade"; - - public static final String CONFIG_MASTER_SLAVE = "CONFIG_Master_Slave"; // Alfresco Search Services using master slave configurations - public static final String CONFIG_MASTER ="CONFIG_Master"; // Alfresco search services using master/stand alone mode - - public static final String CONFIG_SHARDING ="CONFIG_Sharding"; // Alfresco search services using sharded environment - public static final String CONFIG_SHARDING_EXPLICIT ="CONFIG_Sharding_EXPLICIT"; // Alfresco search services using sharded environment and explicit routing - public static final String CONFIG_SHARDING_DB_ID_RANGE = "CONFIG_Sharding_DB_ID_RANGE"; // Alfresco Search Services using Sharding with DB_ID_RANGE - - public static final String CONFIG_ENABLED_CASCADE_TRACKER ="Config_Enabled_Cascade_Tracker"; // Alfresco search services does not index fields related to cascaded updates - - public static final String NOT_INSIGHT_ENGINE = "Not_InsightEngine"; // When Alfresco Insight Engine 1.0 isn't running - - public static final String ACS_52n = "ACS_52n"; // Alfresco Content Services 5.2.n - public static final String ACS_60n = "ACS_60n"; // Alfresco Content Services 6.0 or above - public static final String ACS_61n = "ACS_61n"; // Alfresco Content Services 6.1 or above - public static final String ACS_611n = "ACS_611n"; // Alfresco Content Services 6.1.1 or above - public static final String ACS_62n = "ACS_62n"; // Alfresco Content Services 6.2 or above - public static final String ACS_63n = "ACS_63n"; // Alfresco Content Services 6.3 or above - - public static final String AGS_302 = "AGS_302"; // Alfresco governance Services 3.0.2 or above -} +/* + * #%L + * Alfresco Search Services E2E Test + * %% + * Copyright (C) 2005 - 2020 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.search; + +public class TestGroup +{ + // Used for TestRail test annotation + public static final String SEARCH = "search"; + public static final String REST_API = "rest-api"; + + public static final String PREUPGRADE = "pre-upgrade"; + public static final String POSTUPGRADE = "post-upgrade"; + + public static final String CONFIG_MASTER_SLAVE = "CONFIG_Master_Slave"; // Alfresco Search Services using master slave configurations + public static final String CONFIG_MASTER ="CONFIG_Master"; // Alfresco search services using master/stand alone mode + + public static final String CONFIG_SHARDING ="CONFIG_Sharding"; // Alfresco search services using sharded environment + public static final String CONFIG_SHARDING_EXPLICIT ="CONFIG_Sharding_EXPLICIT"; // Alfresco search services using sharded environment and explicit routing + public static final String CONFIG_SHARDING_DB_ID_RANGE = "CONFIG_Sharding_DB_ID_RANGE"; // Alfresco Search Services using Sharding with DB_ID_RANGE + + public static final String CONFIG_ENABLED_CASCADE_TRACKER ="Config_Enabled_Cascade_Tracker"; // Alfresco search services does not index fields related to cascaded updates + + public static final String NOT_INSIGHT_ENGINE = "Not_InsightEngine"; // When Alfresco Insight Engine 1.0 isn't running + + public static final String ACS_52n = "ACS_52n"; // Alfresco Content Services 5.2.n + public static final String ACS_60n = "ACS_60n"; // Alfresco Content Services 6.0 or above + public static final String ACS_61n = "ACS_61n"; // Alfresco Content Services 6.1 or above + public static final String ACS_611n = "ACS_611n"; // Alfresco Content Services 6.1.1 or above + public static final String ACS_62n = "ACS_62n"; // Alfresco Content Services 6.2 or above + public static final String ACS_63n = "ACS_63n"; // Alfresco Content Services 6.3 or above + + public static final String AGS_302 = "AGS_302"; // Alfresco governance Services 3.0.2 or above +} diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/AbstractE2EFunctionalTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/AbstractE2EFunctionalTest.java index 957a2ba0d..6f377f23a 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/AbstractE2EFunctionalTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/AbstractE2EFunctionalTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/AbstractCmisE2ETest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/AbstractCmisE2ETest.java index 9acae472c..2d4eb2367 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/AbstractCmisE2ETest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/AbstractCmisE2ETest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByAspectTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByAspectTests.java index f4e7b1721..9cfca22f7 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByAspectTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByAspectTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByIdTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByIdTests.java index 11ed7cd80..dd9b27723 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByIdTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByIdTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPathTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPathTests.java index 25fd3ef8e..d135a14dc 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPathTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPathTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPropertyTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPropertyTests.java index 9f395802b..8f5c6d3c3 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPropertyTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchByPropertyTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInFolderTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInFolderTests.java index 1d3ab3c2b..3a585d81c 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInFolderTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInFolderTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInTreeTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInTreeTests.java index 48b1d7db5..ca455cc44 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInTreeTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchInTreeTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchScoreQueryTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchScoreQueryTests.java index 25847c053..7b78557e3 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchScoreQueryTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SolrSearchScoreQueryTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SorlSearchSimpleQueryTests.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SorlSearchSimpleQueryTests.java index 4709879ee..9d14a0b79 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SorlSearchSimpleQueryTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/cmis/SorlSearchSimpleQueryTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/sanity/SetupTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/sanity/SetupTest.java index 79fac07a2..953637033 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/sanity/SetupTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/sanity/SetupTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -116,4 +116,4 @@ public class SetupTest extends AbstractE2EFunctionalTest Assert.assertTrue(waitForIndexing("cm:name:" + customFile.getName(),true),"New content could not be found"); } -} +} \ No newline at end of file diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/AbstractSearchServicesE2ETest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/AbstractSearchServicesE2ETest.java index 894af7a6d..4ebb66e9f 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/AbstractSearchServicesE2ETest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/AbstractSearchServicesE2ETest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ExplicitRoutingTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ExplicitRoutingTest.java index 0423d69e5..51430f28f 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ExplicitRoutingTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ExplicitRoutingTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetFieldsSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetFieldsSearchTest.java index 5e1d743f3..7d0d275e4 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetFieldsSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetFieldsSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetIntervalSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetIntervalSearchTest.java index 5a8cc9fe3..49f235361 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetIntervalSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetIntervalSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetRangeSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetRangeSearchTest.java index 22c800c59..9688d564e 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetRangeSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetRangeSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -336,4 +336,4 @@ public class FacetRangeSearchTest extends AbstractSearchServicesE2ETest facetRangeModel.setGap(gap); return facetRangeModel; } -} +} \ No newline at end of file diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetedSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetedSearchTest.java index 9a321cc65..76e069d24 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetedSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FacetedSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -339,4 +339,4 @@ public class FacetedSearchTest extends AbstractSearchServicesE2ETest bucket1.assertThat().field("filterQuery").is("modifier:\"" + testUser.getUsername() + "\""); bucket1.assertThat().field("metrics").is("[{entry=null, type=count, value={count=1}}]"); } -} +} \ No newline at end of file diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FieldDefinitionTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FieldDefinitionTest.java index 2c1801dc4..bb38e667d 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FieldDefinitionTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FieldDefinitionTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FingerPrintTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FingerPrintTest.java index 055e9ad9e..688563809 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FingerPrintTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/FingerPrintTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/PivotFacetedSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/PivotFacetedSearchTest.java index 2b52927d5..d543961ac 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/PivotFacetedSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/PivotFacetedSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAFTSInFieldTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAFTSInFieldTest.java index 5dba5179e..92130b0d3 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAFTSInFieldTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAFTSInFieldTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAPATHTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAPATHTest.java index 6925de7f7..413fd3fe5 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAPATHTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAPATHTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -208,4 +208,4 @@ public class SearchAPATHTest extends AbstractSearchServicesE2ETest return buckets; } -} +} \ No newline at end of file diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAspectTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAspectTest.java index 51c3ba4e8..d1cb39268 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAspectTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchAspectTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchHighLightTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchHighLightTest.java index a93aea1dd..678db8aa8 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchHighLightTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchHighLightTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchNonIndexedFields.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchNonIndexedFields.java index 556e9edeb..af75cbd02 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchNonIndexedFields.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchNonIndexedFields.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchParentField.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchParentField.java index c6f8e97fb..e3e3fda88 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchParentField.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchParentField.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchPermissionsTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchPermissionsTest.java index 433000274..3f9754831 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchPermissionsTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchPermissionsTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchQueryPaginationTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchQueryPaginationTest.java index ff3de9edb..2abdb2842 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchQueryPaginationTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchQueryPaginationTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSecondaryAssociationTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSecondaryAssociationTest.java index e52d70933..5ccbd3ea0 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSecondaryAssociationTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSecondaryAssociationTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSpellCheckTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSpellCheckTest.java index c3a3bde2e..86e17ec6a 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSpellCheckTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchSpellCheckTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -509,4 +509,4 @@ public class SearchSpellCheckTest extends AbstractSearchServicesE2ETest Assert.assertTrue(isContentInSearchResponse(response, file2.getName()), "Expected file not returned in the search results: " + file2.getName()); testSearchSpellcheckResponse(response, null, null); } -} +} \ No newline at end of file diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchTest.java index 933b0f787..3ecf2d9e2 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchWithCustomModelTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchWithCustomModelTest.java index 7439a3aac..cce905976 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchWithCustomModelTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SearchWithCustomModelTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ShardInfoTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ShardInfoTest.java index c1a706aac..669dc15da 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ShardInfoTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/ShardInfoTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SpecialCharacterSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SpecialCharacterSearchTest.java index 2c8263800..4c6a5e6e2 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SpecialCharacterSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/SpecialCharacterSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/StatsSearchTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/StatsSearchTest.java index 657bb1252..074282697 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/StatsSearchTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/StatsSearchTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -338,4 +338,4 @@ public class StatsSearchTest extends AbstractSearchServicesE2ETest assertEquals(metrics.size(),metricsCount); return metrics.stream().map(RestGenericMetricModel::getType).collect(Collectors.toSet()); } -} +} \ No newline at end of file diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/tracker/CascadingIntegrationTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/tracker/CascadingIntegrationTest.java index 0f38b9372..cec58be0f 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/tracker/CascadingIntegrationTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/search/tracker/CascadingIntegrationTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/SearchSolrAPITest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/SearchSolrAPITest.java index 29cf556a9..85b0d19c0 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/SearchSolrAPITest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/SearchSolrAPITest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -127,4 +127,4 @@ public class SearchSolrAPITest extends AbstractE2EFunctionalTest String errorMsg = "No QueryObjectBuilder defined for node a in {q={!xmlparser"; Assert.assertTrue(restClient.onResponse().getResponse().body().xmlPath().getString("response").contains(errorMsg)); } -} +} \ No newline at end of file diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eActionTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eActionTest.java index c90e46f55..31bea224b 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eActionTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eActionTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java index 5ea6c071f..3c3718c8b 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/AbstractBackupE2ETest.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/AbstractBackupE2ETest.java index 875492d91..8ef3fef2d 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/AbstractBackupE2ETest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/AbstractBackupE2ETest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesOnBackupTests.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesOnBackupTests.java index b02f2579c..663370096 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesOnBackupTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesOnBackupTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPostBackupTests.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPostBackupTests.java index 74127a843..774b96b8d 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPostBackupTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPostBackupTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPreBackupTests.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPreBackupTests.java index 46c3436f0..3371c07f0 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPreBackupTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/backup/SearchServicesPreBackupTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/AbstractUpgradeE2ETest.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/AbstractUpgradeE2ETest.java index 22e407f17..9e24de781 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/AbstractUpgradeE2ETest.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/AbstractUpgradeE2ETest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -67,4 +67,4 @@ public abstract class AbstractUpgradeE2ETest extends AbstractTestNGSpringContext { serverHealth.assertServerIsOnline(); } -} +} \ No newline at end of file diff --git a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/SearchServiceUpgradeTests.java b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/SearchServiceUpgradeTests.java index b7797f318..b672fc320 100644 --- a/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/SearchServiceUpgradeTests.java +++ b/e2e-test/src/test/java/org/alfresco/test/search/nonFunctional/upgrade/SearchServiceUpgradeTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services E2E Test * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/AnalysisException.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/AnalysisException.java index a930830d7..7aa414099 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/AnalysisException.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/AnalysisException.java @@ -1,63 +1,63 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; - -import org.alfresco.error.AlfrescoRuntimeException; - -public class AnalysisException extends AlfrescoRuntimeException -{ - - /** - * - */ - private static final long serialVersionUID = -7722380192490118459L; - - public AnalysisException(String msgId) - { - super(msgId); - // TODO Auto-generated constructor stub - } - - public AnalysisException(String msgId, Object[] msgParams) - { - super(msgId, msgParams); - // TODO Auto-generated constructor stub - } - - public AnalysisException(String msgId, Throwable cause) - { - super(msgId, cause); - // TODO Auto-generated constructor stub - } - - public AnalysisException(String msgId, Object[] msgParams, Throwable cause) - { - super(msgId, msgParams, cause); - // TODO Auto-generated constructor stub - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; + +import org.alfresco.error.AlfrescoRuntimeException; + +public class AnalysisException extends AlfrescoRuntimeException +{ + + /** + * + */ + private static final long serialVersionUID = -7722380192490118459L; + + public AnalysisException(String msgId) + { + super(msgId); + // TODO Auto-generated constructor stub + } + + public AnalysisException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + // TODO Auto-generated constructor stub + } + + public AnalysisException(String msgId, Throwable cause) + { + super(msgId, cause); + // TODO Auto-generated constructor stub + } + + public AnalysisException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + // TODO Auto-generated constructor stub + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayser.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayser.java index 5870598cf..334d0c231 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayser.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayser.java @@ -1,391 +1,391 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; -import java.util.Locale; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.solr.AlfrescoAnalyzerWrapper.Mode; -import org.alfresco.util.Pair; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.Tokenizer; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; -import org.apache.solr.schema.FieldType; -import org.apache.solr.schema.IndexSchema; -import org.springframework.extensions.surf.util.I18NUtil; - -public class MLAnalayser extends Analyzer -{ - private static Log s_logger = LogFactory.getLog(MLAnalayser.class); - - private MLAnalysisMode mlAnalaysisMode; - - private IndexSchema schema; - - private Mode mode; - - public MLAnalayser(MLAnalysisMode mlAnalaysisMode, IndexSchema schema, Mode mode) - { - super(Analyzer.PER_FIELD_REUSE_STRATEGY); - this.mlAnalaysisMode = mlAnalaysisMode; - this.schema = schema; - this.mode = mode; - } - - - public MLAnalayser(MLAnalysisMode mlAnalaysisMode) - { - super(Analyzer.PER_FIELD_REUSE_STRATEGY); - this.mlAnalaysisMode = mlAnalaysisMode; - } - - - - @Override - protected TokenStreamComponents createComponents(String fieldName) - { - MLTokenizer mltokenizer = new MLTokenizer(fieldName, schema, mlAnalaysisMode, mode); -// try -// { -// mltokenizer.setLocaleAndPositionReaderAfterLocaleEncoding(reader); -// } -// catch (IOException e) -// { -// try{mltokenizer.close();} catch(IOException ioe) {}; -// throw new AnalysisException("Failed to init MLTokenizer", e); -// } - return new LocaleAwareTokenStreamComponents(mltokenizer); - } - - private static class LocaleAwareTokenStreamComponents extends Analyzer.TokenStreamComponents - { - MLTokenizer mltokenizer; - - /** - * @param source - */ - public LocaleAwareTokenStreamComponents(final MLTokenizer source) - { - super(source); - this.mltokenizer = source; - } - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Analyzer.TokenStreamComponents#setReader(java.io.Reader) - */ - @Override - protected void setReader(Reader reader) - { - super.setReader(mltokenizer.setLocaleAndPositionReaderAfterLocaleEncoding(reader)); - } - } - - private static class MLTokenizer extends Tokenizer - { - TokenStream ts; - - String fieldName; - - private IndexSchema schema; - - MLAnalysisMode mlAnalaysisMode; - - @SuppressWarnings("unused") - private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); - - @SuppressWarnings("unused") - private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); - - @SuppressWarnings("unused") - private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); - - @SuppressWarnings("unused") - private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); - - private Mode mode; - - MLTokenizer(String fieldName, IndexSchema schema, MLAnalysisMode mlAnalaysisMode, Mode mode) - { - this.fieldName = fieldName; - this.schema = schema; - this.mlAnalaysisMode = mlAnalaysisMode; - this.mode = mode; - } - - - - /** - * @param reader - * @throws IOException - */ - public Reader setLocaleAndPositionReaderAfterLocaleEncoding(Reader reader) - { - Pair pair = getLocaleAndPositioReaderAfterLocaleEncoding(fieldName, reader); - - if(s_logger.isDebugEnabled()) - { - s_logger.debug("Created ML analyser token stream for "+fieldName+ " with locale "+pair.getFirst()); - } - TokenStream source = getAnalyser(fieldName, pair.getFirst()).tokenStream(fieldName, pair.getSecond()); - ts = new MLTokenDuplicator(source, pair.getFirst(), pair.getSecond(), mlAnalaysisMode); - return pair.getSecond(); - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#close() - */ - @Override - public void close() throws IOException - { - ts.close(); - super.close(); - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#reset() - */ - @Override - public void reset() throws IOException - { - ts.reset(); - super.reset(); - } - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#reset() - */ - @Override - public void end() throws IOException - { - ts.end(); - super.end(); - } - - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.TokenStream#incrementToken() - */ - @Override - public boolean incrementToken() throws IOException - { - clearAttributes(); - if(ts.incrementToken()) - { - ts.copyTo(this); - return true; - - } - else - { - ts.end(); - ts.close(); - return false; - } - - - - } - - - public Pair getLocaleAndPositioReaderAfterLocaleEncoding(String fieldName, Reader reader) - { - // We use read ahead to get the language info - if this does not exist we need to restart - // an use the default - there for we need mark and restore. - BufferedReader breader; - if (reader instanceof BufferedReader) - { - breader = (BufferedReader)reader; - } - else - { - breader = new BufferedReader(reader); - } - - try - { - if (!breader.markSupported()) - { - throw new AnalysisException( - "Multilingual tokenisation requires a reader that supports marks and reset"); - } - breader.mark(100); - StringBuilder builder = new StringBuilder(); - if (breader.read() == '\u0000') - { - String language = ""; - String country = ""; - String varient = ""; - char c; - int count = 0; - while ((c = (char) breader.read()) != '\u0000') - { - if (count++ > 99) - { - breader.reset(); - return new Pair(I18NUtil.getLocale(), breader); - } - if (c == '_') - { - if (language.length() == 0) - { - language = builder.toString(); - } - else if (country.length() == 0) - { - country = builder.toString(); - } - else if (varient.length() == 0) - { - varient = builder.toString(); - } - else - { - breader.reset(); - return new Pair(I18NUtil.getLocale(), breader); - } - builder = new StringBuilder(); - } - else - { - builder.append(c); - } - } - if (builder.length() > 0) - { - if (language.length() == 0) - { - language = builder.toString(); - } - else if (country.length() == 0) - { - country = builder.toString(); - } - else if (varient.length() == 0) - { - varient = builder.toString(); - } - else - { - breader.reset(); - return new Pair(I18NUtil.getLocale(), breader); - } - } - Locale locale = new Locale(language, country, varient); - // leave the reader where it is .... - return new Pair(locale, breader); - - } - else - { - breader.reset(); - return new Pair(I18NUtil.getLocale(), breader); - } - } - catch (IOException io) - { - try - { - breader.reset(); - } - catch (IOException e) - { - throw new AnalysisException("Failed to reset buffered reader - token stream will be invalid", e); - } - return null; - } - } - - - private Analyzer getAnalyser(String fieldName, Locale locale) - { - if(schema != null) - { - if(fieldName.contains("l_@{")) - { - FieldType fieldType = schema.getFieldTypeByName("identifier"); - return selectAnalyzer(fieldType); - } - else if(fieldName.contains("lt@{")) - { - StringBuilder builder = new StringBuilder(); - builder.append("text_"); - builder.append(locale.getLanguage()); - FieldType fieldType = schema.getFieldTypeByName(builder.toString()); - if(fieldType == null) - { - fieldType = schema.getFieldTypeByName("text_en"); - } - return selectAnalyzer(fieldType); - } - else - { - return null; - } - - } - else - { - return null; - } - } - - - - private Analyzer selectAnalyzer(FieldType fieldType) { - if(mode == Mode.INDEX) - { - return fieldType.getIndexAnalyzer(); - } - else if(mode == Mode.QUERY) - { - return fieldType.getQueryAnalyzer(); - } - else - { - return null; - } - } - - - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Locale; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.solr.AlfrescoAnalyzerWrapper.Mode; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.Tokenizer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.springframework.extensions.surf.util.I18NUtil; + +public class MLAnalayser extends Analyzer +{ + private static Log s_logger = LogFactory.getLog(MLAnalayser.class); + + private MLAnalysisMode mlAnalaysisMode; + + private IndexSchema schema; + + private Mode mode; + + public MLAnalayser(MLAnalysisMode mlAnalaysisMode, IndexSchema schema, Mode mode) + { + super(Analyzer.PER_FIELD_REUSE_STRATEGY); + this.mlAnalaysisMode = mlAnalaysisMode; + this.schema = schema; + this.mode = mode; + } + + + public MLAnalayser(MLAnalysisMode mlAnalaysisMode) + { + super(Analyzer.PER_FIELD_REUSE_STRATEGY); + this.mlAnalaysisMode = mlAnalaysisMode; + } + + + + @Override + protected TokenStreamComponents createComponents(String fieldName) + { + MLTokenizer mltokenizer = new MLTokenizer(fieldName, schema, mlAnalaysisMode, mode); +// try +// { +// mltokenizer.setLocaleAndPositionReaderAfterLocaleEncoding(reader); +// } +// catch (IOException e) +// { +// try{mltokenizer.close();} catch(IOException ioe) {}; +// throw new AnalysisException("Failed to init MLTokenizer", e); +// } + return new LocaleAwareTokenStreamComponents(mltokenizer); + } + + private static class LocaleAwareTokenStreamComponents extends Analyzer.TokenStreamComponents + { + MLTokenizer mltokenizer; + + /** + * @param source + */ + public LocaleAwareTokenStreamComponents(final MLTokenizer source) + { + super(source); + this.mltokenizer = source; + } + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Analyzer.TokenStreamComponents#setReader(java.io.Reader) + */ + @Override + protected void setReader(Reader reader) + { + super.setReader(mltokenizer.setLocaleAndPositionReaderAfterLocaleEncoding(reader)); + } + } + + private static class MLTokenizer extends Tokenizer + { + TokenStream ts; + + String fieldName; + + private IndexSchema schema; + + MLAnalysisMode mlAnalaysisMode; + + @SuppressWarnings("unused") + private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); + + @SuppressWarnings("unused") + private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); + + @SuppressWarnings("unused") + private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); + + @SuppressWarnings("unused") + private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); + + private Mode mode; + + MLTokenizer(String fieldName, IndexSchema schema, MLAnalysisMode mlAnalaysisMode, Mode mode) + { + this.fieldName = fieldName; + this.schema = schema; + this.mlAnalaysisMode = mlAnalaysisMode; + this.mode = mode; + } + + + + /** + * @param reader + * @throws IOException + */ + public Reader setLocaleAndPositionReaderAfterLocaleEncoding(Reader reader) + { + Pair pair = getLocaleAndPositioReaderAfterLocaleEncoding(fieldName, reader); + + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Created ML analyser token stream for "+fieldName+ " with locale "+pair.getFirst()); + } + TokenStream source = getAnalyser(fieldName, pair.getFirst()).tokenStream(fieldName, pair.getSecond()); + ts = new MLTokenDuplicator(source, pair.getFirst(), pair.getSecond(), mlAnalaysisMode); + return pair.getSecond(); + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#close() + */ + @Override + public void close() throws IOException + { + ts.close(); + super.close(); + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#reset() + */ + @Override + public void reset() throws IOException + { + ts.reset(); + super.reset(); + } + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#reset() + */ + @Override + public void end() throws IOException + { + ts.end(); + super.end(); + } + + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.TokenStream#incrementToken() + */ + @Override + public boolean incrementToken() throws IOException + { + clearAttributes(); + if(ts.incrementToken()) + { + ts.copyTo(this); + return true; + + } + else + { + ts.end(); + ts.close(); + return false; + } + + + + } + + + public Pair getLocaleAndPositioReaderAfterLocaleEncoding(String fieldName, Reader reader) + { + // We use read ahead to get the language info - if this does not exist we need to restart + // an use the default - there for we need mark and restore. + BufferedReader breader; + if (reader instanceof BufferedReader) + { + breader = (BufferedReader)reader; + } + else + { + breader = new BufferedReader(reader); + } + + try + { + if (!breader.markSupported()) + { + throw new AnalysisException( + "Multilingual tokenisation requires a reader that supports marks and reset"); + } + breader.mark(100); + StringBuilder builder = new StringBuilder(); + if (breader.read() == '\u0000') + { + String language = ""; + String country = ""; + String varient = ""; + char c; + int count = 0; + while ((c = (char) breader.read()) != '\u0000') + { + if (count++ > 99) + { + breader.reset(); + return new Pair(I18NUtil.getLocale(), breader); + } + if (c == '_') + { + if (language.length() == 0) + { + language = builder.toString(); + } + else if (country.length() == 0) + { + country = builder.toString(); + } + else if (varient.length() == 0) + { + varient = builder.toString(); + } + else + { + breader.reset(); + return new Pair(I18NUtil.getLocale(), breader); + } + builder = new StringBuilder(); + } + else + { + builder.append(c); + } + } + if (builder.length() > 0) + { + if (language.length() == 0) + { + language = builder.toString(); + } + else if (country.length() == 0) + { + country = builder.toString(); + } + else if (varient.length() == 0) + { + varient = builder.toString(); + } + else + { + breader.reset(); + return new Pair(I18NUtil.getLocale(), breader); + } + } + Locale locale = new Locale(language, country, varient); + // leave the reader where it is .... + return new Pair(locale, breader); + + } + else + { + breader.reset(); + return new Pair(I18NUtil.getLocale(), breader); + } + } + catch (IOException io) + { + try + { + breader.reset(); + } + catch (IOException e) + { + throw new AnalysisException("Failed to reset buffered reader - token stream will be invalid", e); + } + return null; + } + } + + + private Analyzer getAnalyser(String fieldName, Locale locale) + { + if(schema != null) + { + if(fieldName.contains("l_@{")) + { + FieldType fieldType = schema.getFieldTypeByName("identifier"); + return selectAnalyzer(fieldType); + } + else if(fieldName.contains("lt@{")) + { + StringBuilder builder = new StringBuilder(); + builder.append("text_"); + builder.append(locale.getLanguage()); + FieldType fieldType = schema.getFieldTypeByName(builder.toString()); + if(fieldType == null) + { + fieldType = schema.getFieldTypeByName("text_en"); + } + return selectAnalyzer(fieldType); + } + else + { + return null; + } + + } + else + { + return null; + } + } + + + + private Analyzer selectAnalyzer(FieldType fieldType) { + if(mode == Mode.INDEX) + { + return fieldType.getIndexAnalyzer(); + } + else if(mode == Mode.QUERY) + { + return fieldType.getQueryAnalyzer(); + } + else + { + return null; + } + } + + + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLTokenDuplicator.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLTokenDuplicator.java index 65c19d538..ce476a1f4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLTokenDuplicator.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/MLTokenDuplicator.java @@ -1,284 +1,284 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; - -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Locale; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; - -/** - * Create duplicate tokens for multilingual varients The forms are Tokens: Token - all languages {fr}Token - if a - * language is specified {fr_CA}Token - if a language and country is specified {fr_CA_Varient}Token - for all three - * {fr__Varient}Token - for a language varient with no country - * - * @author andyh - */ -public class MLTokenDuplicator extends TokenStream -{ - private static Log s_logger = LogFactory.getLog(MLTokenDuplicator.class); - - TokenStream source; - - Locale locale; - - Iterator it; - - HashSet prefixes; - - private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); - - private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); - - private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); - - private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); - - private boolean done = false; - - public MLTokenDuplicator(TokenStream source, Locale locale, Reader reader, MLAnalysisMode mlAnalysisMode) - { - this.source = source; - this.locale = locale; - - Collection locales = MLAnalysisMode.getLocales(mlAnalysisMode, locale, false); - prefixes = new HashSet(locales.size()); - for(Locale toAdd : locales) - { - String localeString = toAdd.toString(); - if(localeString.length() == 0) - { - prefixes.add(""); - } - else - { - StringBuilder builder = new StringBuilder(16); - builder.append("{").append(localeString).append("}"); - prefixes.add(builder.toString()); - } - } - if(s_logger.isDebugEnabled()) - { - s_logger.debug("Locale "+ locale +" using "+mlAnalysisMode+" is "+prefixes); - } - - } - - /** - * - * @param locale - * @param mlAnalysisMode - */ - public MLTokenDuplicator(Locale locale, MLAnalysisMode mlAnalysisMode) - { - this.locale = locale; - - Collection locales = MLAnalysisMode.getLocales(mlAnalysisMode, locale, false); - prefixes = new HashSet(locales.size()); - for(Locale toAdd : locales) - { - String localeString = toAdd.toString(); - if(localeString.length() == 0) - { - prefixes.add(""); - } - else - { - StringBuilder builder = new StringBuilder(16); - builder.append("{").append(localeString).append("}"); - prefixes.add(builder.toString()); - } - } - if(s_logger.isDebugEnabled()) - { - s_logger.debug("Locale "+ locale +" using "+mlAnalysisMode+" is "+prefixes); - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#close() - */ - @Override - public void close() throws IOException - { - source.close(); - super.close(); - } - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Tokenizer#reset() - */ - @Override - public void reset() throws IOException - { - source.reset(); - super.reset(); - } - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.TokenStream#end() - */ - @Override - public void end() throws IOException - { - source.end(); - super.end(); - } - - private PackedTokenAttributeImpl next() throws IOException - { - PackedTokenAttributeImpl t = null; - if (it == null) - { - it = buildIterator(); - } - if (it == null) - { - return null; - } - if (it.hasNext()) - { - t = it.next(); - - return t; - } - else - { - it = null; - t = this.next(); - return t; - } - } - - private Iterator buildIterator() throws IOException - { - // TODO: use incrementToken() somehow? - if(!done && source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if(source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if(source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - PackedTokenAttributeImpl token = new PackedTokenAttributeImpl(); - token.setEmpty().append(new String(cta.buffer()), 0, cta.length()); - token.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if(typeAtt != null) - { - token.setType(typeAtt.type()); - } - if(posIncAtt != null) - { - token.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - return buildIterator(token); - } - else - { - done = true; - return buildIterator(null); - } - - - } - - - public Iterator buildIterator(PackedTokenAttributeImpl token) - { - if (token == null) - { - return null; - } - - ArrayList tokens = new ArrayList(prefixes.size()); - for (String prefix : prefixes) - { - - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(prefix + termText(token)); - newToken.setOffset(token.startOffset(), token.endOffset()); - newToken.setType(token.type()); - if (tokens.size() == 0) - { - newToken.setPositionIncrement(token.getPositionIncrement()); - } - else - { - newToken.setPositionIncrement(0); - } - tokens.add(newToken); - } - return tokens.iterator(); - - } - - @Override - public final boolean incrementToken() throws IOException - { - clearAttributes(); - - PackedTokenAttributeImpl next = next(); - if (next == null) - { - return false; - } - - termAtt.copyBuffer(next.buffer(), 0, next.length()); - offsetAtt.setOffset(next.startOffset(), next.endOffset()); - typeAtt.setType(next.type()); - posIncAtt.setPositionIncrement(next.getPositionIncrement()); - return true; - - } - - // TODO: temporary replacement for Token.termText() - private String termText(PackedTokenAttributeImpl token) - { - return new String(token.buffer(), 0, token.length()); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; + +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; + +/** + * Create duplicate tokens for multilingual varients The forms are Tokens: Token - all languages {fr}Token - if a + * language is specified {fr_CA}Token - if a language and country is specified {fr_CA_Varient}Token - for all three + * {fr__Varient}Token - for a language varient with no country + * + * @author andyh + */ +public class MLTokenDuplicator extends TokenStream +{ + private static Log s_logger = LogFactory.getLog(MLTokenDuplicator.class); + + TokenStream source; + + Locale locale; + + Iterator it; + + HashSet prefixes; + + private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); + + private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); + + private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); + + private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); + + private boolean done = false; + + public MLTokenDuplicator(TokenStream source, Locale locale, Reader reader, MLAnalysisMode mlAnalysisMode) + { + this.source = source; + this.locale = locale; + + Collection locales = MLAnalysisMode.getLocales(mlAnalysisMode, locale, false); + prefixes = new HashSet(locales.size()); + for(Locale toAdd : locales) + { + String localeString = toAdd.toString(); + if(localeString.length() == 0) + { + prefixes.add(""); + } + else + { + StringBuilder builder = new StringBuilder(16); + builder.append("{").append(localeString).append("}"); + prefixes.add(builder.toString()); + } + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Locale "+ locale +" using "+mlAnalysisMode+" is "+prefixes); + } + + } + + /** + * + * @param locale + * @param mlAnalysisMode + */ + public MLTokenDuplicator(Locale locale, MLAnalysisMode mlAnalysisMode) + { + this.locale = locale; + + Collection locales = MLAnalysisMode.getLocales(mlAnalysisMode, locale, false); + prefixes = new HashSet(locales.size()); + for(Locale toAdd : locales) + { + String localeString = toAdd.toString(); + if(localeString.length() == 0) + { + prefixes.add(""); + } + else + { + StringBuilder builder = new StringBuilder(16); + builder.append("{").append(localeString).append("}"); + prefixes.add(builder.toString()); + } + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Locale "+ locale +" using "+mlAnalysisMode+" is "+prefixes); + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#close() + */ + @Override + public void close() throws IOException + { + source.close(); + super.close(); + } + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Tokenizer#reset() + */ + @Override + public void reset() throws IOException + { + source.reset(); + super.reset(); + } + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.TokenStream#end() + */ + @Override + public void end() throws IOException + { + source.end(); + super.end(); + } + + private PackedTokenAttributeImpl next() throws IOException + { + PackedTokenAttributeImpl t = null; + if (it == null) + { + it = buildIterator(); + } + if (it == null) + { + return null; + } + if (it.hasNext()) + { + t = it.next(); + + return t; + } + else + { + it = null; + t = this.next(); + return t; + } + } + + private Iterator buildIterator() throws IOException + { + // TODO: use incrementToken() somehow? + if(!done && source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if(source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if(source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + PackedTokenAttributeImpl token = new PackedTokenAttributeImpl(); + token.setEmpty().append(new String(cta.buffer()), 0, cta.length()); + token.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if(typeAtt != null) + { + token.setType(typeAtt.type()); + } + if(posIncAtt != null) + { + token.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + return buildIterator(token); + } + else + { + done = true; + return buildIterator(null); + } + + + } + + + public Iterator buildIterator(PackedTokenAttributeImpl token) + { + if (token == null) + { + return null; + } + + ArrayList tokens = new ArrayList(prefixes.size()); + for (String prefix : prefixes) + { + + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(prefix + termText(token)); + newToken.setOffset(token.startOffset(), token.endOffset()); + newToken.setType(token.type()); + if (tokens.size() == 0) + { + newToken.setPositionIncrement(token.getPositionIncrement()); + } + else + { + newToken.setPositionIncrement(0); + } + tokens.add(newToken); + } + return tokens.iterator(); + + } + + @Override + public final boolean incrementToken() throws IOException + { + clearAttributes(); + + PackedTokenAttributeImpl next = next(); + if (next == null) + { + return false; + } + + termAtt.copyBuffer(next.buffer(), 0, next.length()); + offsetAtt.setOffset(next.startOffset(), next.endOffset()); + typeAtt.setType(next.type()); + posIncAtt.setPositionIncrement(next.getPositionIncrement()); + return true; + + } + + // TODO: temporary replacement for Token.termText() + private String termText(PackedTokenAttributeImpl token) + { + return new String(token.buffer(), 0, token.length()); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathAnalyzer.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathAnalyzer.java index fee36b88d..b8d8dc4f3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathAnalyzer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathAnalyzer.java @@ -1,47 +1,47 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; - -import org.apache.lucene.analysis.Analyzer; - -/** - * @author Andy - * - */ -public class PathAnalyzer extends Analyzer -{ - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.Analyzer#createComponents(java.lang.String, java.io.Reader) - */ - @Override - protected TokenStreamComponents createComponents(String fieldName) - { - return new TokenStreamComponents(new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true)); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; + +import org.apache.lucene.analysis.Analyzer; + +/** + * @author Andy + * + */ +public class PathAnalyzer extends Analyzer +{ + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.Analyzer#createComponents(java.lang.String, java.io.Reader) + */ + @Override + protected TokenStreamComponents createComponents(String fieldName) + { + return new TokenStreamComponents(new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true)); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilter.java b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilter.java index 89bd14f88..79d8c1adb 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilter.java @@ -1,396 +1,396 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; - -import java.io.IOException; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Iterator; -import java.util.LinkedList; - -import org.apache.lucene.analysis.Tokenizer; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; - -/** - * @author andyh TODO To change the template for this generated type comment go to Window - Preferences - Java - Code - * Style - Code Templates - */ -public class PathTokenFilter extends Tokenizer -{ - public final static String INTEGER_FORMAT = "0000000000"; - - public final static char PATH_SEPARATOR = ';'; - - public final static char NAMESPACE_START_DELIMITER = '{'; - - public final static char NAMESPACE_END_DELIMITER = '}'; - - public final static String SEPARATOR_TOKEN_TEXT = ";"; - - public final static String NO_NS_TOKEN_TEXT = ""; - - public final static String TOKEN_TYPE_PATH_SEP = "PATH_SEPARATOR"; - - public final static String TOKEN_TYPE_PATH_LENGTH = "PATH_LENGTH"; - - public final static String TOKEN_TYPE_PATH_ELEMENT_NAME = "PATH_ELEMENT_NAME"; - - public final static String TOKEN_TYPE_PATH_ELEMENT_NAMESPACE = "PATH_ELEMENT_NAMESPACE"; - - public final static String TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX = "PATH_ELEMENT_NAMESPACE_PREFIX"; - - private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); - - private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); - - private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); - - private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); - - char pathSeparator; - - String separatorTokenText; - - String noNsTokenText; - - char nsStartDelimiter; - - int nsStartDelimiterLength; - - char nsEndDelimiter; - - int nsEndDelimiterLength; - - char nsPrefixDelimiter = ':'; - - LinkedList tokens = new LinkedList(); - - Iterator it = null; - - private boolean includeNamespace; - - private boolean endOfStream = false; - - public PathTokenFilter(char pathSeparator, String separatorTokenText, String noNsTokenText, - char nsStartDelimiter, char nsEndDelimiter, boolean includeNameSpace) - { - this.pathSeparator = pathSeparator; - this.separatorTokenText = separatorTokenText; - this.noNsTokenText = noNsTokenText; - this.nsStartDelimiter = nsStartDelimiter; - this.nsEndDelimiter = nsEndDelimiter; - this.includeNamespace = includeNameSpace; - - this.nsStartDelimiterLength = 1; - this.nsEndDelimiterLength = 1; - - } - - /* - * (non-Javadoc) - * - * @see org.apache.lucene.analysis.TokenStream#next() - */ - - public PackedTokenAttributeImpl next() throws IOException - { - PackedTokenAttributeImpl nextToken; - if (it == null) - { - buildTokenListAndIterator(); - } - if (it.hasNext()) - { - nextToken = it.next(); - } - else - { - nextToken = null; - } - return nextToken; - } - - // TODO: temporary replacement for Token.termText() - private String termText(PackedTokenAttributeImpl token) - { - return new String(token.buffer(), 0, token.length()); - } - - private void buildTokenListAndIterator() throws IOException - { - NumberFormat nf = new DecimalFormat(INTEGER_FORMAT); - - // Could optimise to read each path ata time - not just all paths - int insertCountAt = 0; - int lengthCounter = 0; - PackedTokenAttributeImpl t; - PackedTokenAttributeImpl pathSplitToken = null; - PackedTokenAttributeImpl nameToken = null; - PackedTokenAttributeImpl countToken = null; - PackedTokenAttributeImpl namespaceToken = null; - while ((t = nextToken()) != null) - { - String text = termText(t); - - if (text.length() == 0) - { - continue; // Skip if we find // or /; or ;; etc - } - - if (text.charAt(text.length() - 1) == pathSeparator) - { - text = text.substring(0, text.length() - 1); - pathSplitToken = new PackedTokenAttributeImpl(); - pathSplitToken.setEmpty().append(separatorTokenText); - pathSplitToken.setOffset(t.startOffset(), t.endOffset()); - pathSplitToken.setType( TOKEN_TYPE_PATH_SEP); - pathSplitToken.setPositionIncrement(1); - - } - - int split = -1; - boolean isPrefix = false; - - if ((text.length() > 0) && (text.charAt(0) == nsStartDelimiter)) - { - split = text.indexOf(nsEndDelimiter); - } - - if (split == -1) - { - split = text.indexOf(nsPrefixDelimiter); - isPrefix = true; - } - - if (split == -1) - { - namespaceToken = new PackedTokenAttributeImpl(); - namespaceToken.setEmpty().append(noNsTokenText); - namespaceToken.setOffset(t.startOffset(), t.startOffset()); - namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); - nameToken = new PackedTokenAttributeImpl(); - nameToken.setEmpty().append(text); - nameToken.setOffset(t.startOffset(), t.endOffset()); - - nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); - } - else - { - if (isPrefix) - { - namespaceToken = new PackedTokenAttributeImpl(); - namespaceToken.setEmpty().append(text.substring(0, split)); - namespaceToken.setOffset(t.startOffset(), t.startOffset() + split); - namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX); - nameToken = new PackedTokenAttributeImpl(); - nameToken.setEmpty().append(text.substring(split + 1)); - nameToken.setOffset(t.startOffset() + split + 1, t.endOffset()); - nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); - } - else - { - namespaceToken = new PackedTokenAttributeImpl(); - namespaceToken.setEmpty().append(text.substring(nsStartDelimiterLength, (split + nsEndDelimiterLength - 1))); - namespaceToken.setOffset(t.startOffset(), t.startOffset() + split); - namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); - nameToken = new PackedTokenAttributeImpl(); - nameToken.setEmpty().append(text.substring(split + nsEndDelimiterLength)); - nameToken.setOffset(t.startOffset() + split + nsEndDelimiterLength, t.endOffset()); - nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); - } - } - - namespaceToken.setPositionIncrement(1); - nameToken.setPositionIncrement(1); - - if (includeNamespace) - { - if (termText(namespaceToken).equals("")) - { - namespaceToken = new PackedTokenAttributeImpl(); - namespaceToken.setEmpty().append(noNsTokenText); - namespaceToken.setOffset(t.startOffset(), t.startOffset()); - namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); - namespaceToken.setPositionIncrement(1); - } - - tokens.add(namespaceToken); - - } - tokens.add(nameToken); - - lengthCounter++; - - if (pathSplitToken != null) - { - - String countString = nf.format(lengthCounter); - countToken = new PackedTokenAttributeImpl(); - countToken.setEmpty().append(countString); - countToken.setOffset(t.startOffset(), t.endOffset()); - countToken.setType(TOKEN_TYPE_PATH_SEP); - countToken.setPositionIncrement(1); - - tokens.add(insertCountAt, countToken); - tokens.add(pathSplitToken); - - lengthCounter = 0; - insertCountAt = tokens.size(); - - pathSplitToken = null; - } - } - - String countString = nf.format(lengthCounter); - countToken = new PackedTokenAttributeImpl(); - countToken.setEmpty().append(countString); - countToken.setOffset(0, 0); - countToken.setType(TOKEN_TYPE_PATH_SEP); - countToken.setPositionIncrement(1); - - tokens.add(insertCountAt, countToken); - - if ((tokens.size() == 0) || !(termText(tokens.get(tokens.size() - 1)).equals(TOKEN_TYPE_PATH_SEP))) - { - pathSplitToken = new PackedTokenAttributeImpl(); - pathSplitToken.setEmpty().append(separatorTokenText); - pathSplitToken.setOffset(0, 0); - pathSplitToken.setType(TOKEN_TYPE_PATH_SEP); - pathSplitToken.setPositionIncrement(1); - tokens.add(pathSplitToken); - } - - it = tokens.iterator(); - } - - int readerPosition = 0; - - private PackedTokenAttributeImpl nextToken() throws IOException - { - if (endOfStream) - { - return null; - } - StringBuilder buffer = new StringBuilder(64); - boolean inNameSpace = false; - int start = readerPosition; - int current; - char c; - while ((current = input.read()) != -1) - { - c = (char) current; - readerPosition++; - if (c == nsStartDelimiter) - { - inNameSpace = true; - } - else if (c == nsEndDelimiter) - { - inNameSpace = false; - } - else if (!inNameSpace && (c == '/')) - { - PackedTokenAttributeImpl qNameToken = new PackedTokenAttributeImpl(); - qNameToken.setEmpty().append(buffer.toString()); - qNameToken.setOffset(start, readerPosition - 1); - qNameToken.setType("QNAME"); - return qNameToken; - } - else if (!inNameSpace && (c == ';')) - { - buffer.append(c); - PackedTokenAttributeImpl lastQNameToken = new PackedTokenAttributeImpl(); - lastQNameToken.setEmpty().append(buffer.toString()); - lastQNameToken.setOffset(start, readerPosition); - lastQNameToken.setType("LASTQNAME"); - return lastQNameToken; - } - - buffer.append(c); - } - int end = readerPosition - 1; - // Stop the final token being returned with an end before the start. - if (start > end) - { - end = start; - } - endOfStream = true; - if (!inNameSpace) - { - PackedTokenAttributeImpl qNameToken = new PackedTokenAttributeImpl(); - qNameToken.setEmpty().append(buffer.toString()); - qNameToken.setOffset(start, end); - qNameToken.setType("QNAME"); - return qNameToken; - } - else - { - throw new IllegalStateException("QName terminated incorrectly: " + buffer.toString()); - } - - } - - @Override - public final boolean incrementToken() throws IOException - { - clearAttributes(); - - PackedTokenAttributeImpl next = next(); - if (next == null) - { - return false; - } - - termAtt.copyBuffer(next.buffer(), 0, next.length()); - offsetAtt.setOffset(correctOffset(next.startOffset()), correctOffset(next.endOffset())); - typeAtt.setType(next.type()); - posIncAtt.setPositionIncrement(next.getPositionIncrement()); - return true; - } - - @Override - public void reset() throws IOException - { - super.reset(); - tokens.clear(); - it = null; - readerPosition = 0; - endOfStream = false; - } - - @Override - public void end() throws IOException - { - super.end(); - int finalOffset = correctOffset(readerPosition); - offsetAtt.setOffset(finalOffset, finalOffset); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; + +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Iterator; +import java.util.LinkedList; + +import org.apache.lucene.analysis.Tokenizer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; + +/** + * @author andyh TODO To change the template for this generated type comment go to Window - Preferences - Java - Code + * Style - Code Templates + */ +public class PathTokenFilter extends Tokenizer +{ + public final static String INTEGER_FORMAT = "0000000000"; + + public final static char PATH_SEPARATOR = ';'; + + public final static char NAMESPACE_START_DELIMITER = '{'; + + public final static char NAMESPACE_END_DELIMITER = '}'; + + public final static String SEPARATOR_TOKEN_TEXT = ";"; + + public final static String NO_NS_TOKEN_TEXT = ""; + + public final static String TOKEN_TYPE_PATH_SEP = "PATH_SEPARATOR"; + + public final static String TOKEN_TYPE_PATH_LENGTH = "PATH_LENGTH"; + + public final static String TOKEN_TYPE_PATH_ELEMENT_NAME = "PATH_ELEMENT_NAME"; + + public final static String TOKEN_TYPE_PATH_ELEMENT_NAMESPACE = "PATH_ELEMENT_NAMESPACE"; + + public final static String TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX = "PATH_ELEMENT_NAMESPACE_PREFIX"; + + private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); + + private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); + + private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); + + private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); + + char pathSeparator; + + String separatorTokenText; + + String noNsTokenText; + + char nsStartDelimiter; + + int nsStartDelimiterLength; + + char nsEndDelimiter; + + int nsEndDelimiterLength; + + char nsPrefixDelimiter = ':'; + + LinkedList tokens = new LinkedList(); + + Iterator it = null; + + private boolean includeNamespace; + + private boolean endOfStream = false; + + public PathTokenFilter(char pathSeparator, String separatorTokenText, String noNsTokenText, + char nsStartDelimiter, char nsEndDelimiter, boolean includeNameSpace) + { + this.pathSeparator = pathSeparator; + this.separatorTokenText = separatorTokenText; + this.noNsTokenText = noNsTokenText; + this.nsStartDelimiter = nsStartDelimiter; + this.nsEndDelimiter = nsEndDelimiter; + this.includeNamespace = includeNameSpace; + + this.nsStartDelimiterLength = 1; + this.nsEndDelimiterLength = 1; + + } + + /* + * (non-Javadoc) + * + * @see org.apache.lucene.analysis.TokenStream#next() + */ + + public PackedTokenAttributeImpl next() throws IOException + { + PackedTokenAttributeImpl nextToken; + if (it == null) + { + buildTokenListAndIterator(); + } + if (it.hasNext()) + { + nextToken = it.next(); + } + else + { + nextToken = null; + } + return nextToken; + } + + // TODO: temporary replacement for Token.termText() + private String termText(PackedTokenAttributeImpl token) + { + return new String(token.buffer(), 0, token.length()); + } + + private void buildTokenListAndIterator() throws IOException + { + NumberFormat nf = new DecimalFormat(INTEGER_FORMAT); + + // Could optimise to read each path ata time - not just all paths + int insertCountAt = 0; + int lengthCounter = 0; + PackedTokenAttributeImpl t; + PackedTokenAttributeImpl pathSplitToken = null; + PackedTokenAttributeImpl nameToken = null; + PackedTokenAttributeImpl countToken = null; + PackedTokenAttributeImpl namespaceToken = null; + while ((t = nextToken()) != null) + { + String text = termText(t); + + if (text.length() == 0) + { + continue; // Skip if we find // or /; or ;; etc + } + + if (text.charAt(text.length() - 1) == pathSeparator) + { + text = text.substring(0, text.length() - 1); + pathSplitToken = new PackedTokenAttributeImpl(); + pathSplitToken.setEmpty().append(separatorTokenText); + pathSplitToken.setOffset(t.startOffset(), t.endOffset()); + pathSplitToken.setType( TOKEN_TYPE_PATH_SEP); + pathSplitToken.setPositionIncrement(1); + + } + + int split = -1; + boolean isPrefix = false; + + if ((text.length() > 0) && (text.charAt(0) == nsStartDelimiter)) + { + split = text.indexOf(nsEndDelimiter); + } + + if (split == -1) + { + split = text.indexOf(nsPrefixDelimiter); + isPrefix = true; + } + + if (split == -1) + { + namespaceToken = new PackedTokenAttributeImpl(); + namespaceToken.setEmpty().append(noNsTokenText); + namespaceToken.setOffset(t.startOffset(), t.startOffset()); + namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); + nameToken = new PackedTokenAttributeImpl(); + nameToken.setEmpty().append(text); + nameToken.setOffset(t.startOffset(), t.endOffset()); + + nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); + } + else + { + if (isPrefix) + { + namespaceToken = new PackedTokenAttributeImpl(); + namespaceToken.setEmpty().append(text.substring(0, split)); + namespaceToken.setOffset(t.startOffset(), t.startOffset() + split); + namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX); + nameToken = new PackedTokenAttributeImpl(); + nameToken.setEmpty().append(text.substring(split + 1)); + nameToken.setOffset(t.startOffset() + split + 1, t.endOffset()); + nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); + } + else + { + namespaceToken = new PackedTokenAttributeImpl(); + namespaceToken.setEmpty().append(text.substring(nsStartDelimiterLength, (split + nsEndDelimiterLength - 1))); + namespaceToken.setOffset(t.startOffset(), t.startOffset() + split); + namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); + nameToken = new PackedTokenAttributeImpl(); + nameToken.setEmpty().append(text.substring(split + nsEndDelimiterLength)); + nameToken.setOffset(t.startOffset() + split + nsEndDelimiterLength, t.endOffset()); + nameToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAME); + } + } + + namespaceToken.setPositionIncrement(1); + nameToken.setPositionIncrement(1); + + if (includeNamespace) + { + if (termText(namespaceToken).equals("")) + { + namespaceToken = new PackedTokenAttributeImpl(); + namespaceToken.setEmpty().append(noNsTokenText); + namespaceToken.setOffset(t.startOffset(), t.startOffset()); + namespaceToken.setType(TOKEN_TYPE_PATH_ELEMENT_NAMESPACE); + namespaceToken.setPositionIncrement(1); + } + + tokens.add(namespaceToken); + + } + tokens.add(nameToken); + + lengthCounter++; + + if (pathSplitToken != null) + { + + String countString = nf.format(lengthCounter); + countToken = new PackedTokenAttributeImpl(); + countToken.setEmpty().append(countString); + countToken.setOffset(t.startOffset(), t.endOffset()); + countToken.setType(TOKEN_TYPE_PATH_SEP); + countToken.setPositionIncrement(1); + + tokens.add(insertCountAt, countToken); + tokens.add(pathSplitToken); + + lengthCounter = 0; + insertCountAt = tokens.size(); + + pathSplitToken = null; + } + } + + String countString = nf.format(lengthCounter); + countToken = new PackedTokenAttributeImpl(); + countToken.setEmpty().append(countString); + countToken.setOffset(0, 0); + countToken.setType(TOKEN_TYPE_PATH_SEP); + countToken.setPositionIncrement(1); + + tokens.add(insertCountAt, countToken); + + if ((tokens.size() == 0) || !(termText(tokens.get(tokens.size() - 1)).equals(TOKEN_TYPE_PATH_SEP))) + { + pathSplitToken = new PackedTokenAttributeImpl(); + pathSplitToken.setEmpty().append(separatorTokenText); + pathSplitToken.setOffset(0, 0); + pathSplitToken.setType(TOKEN_TYPE_PATH_SEP); + pathSplitToken.setPositionIncrement(1); + tokens.add(pathSplitToken); + } + + it = tokens.iterator(); + } + + int readerPosition = 0; + + private PackedTokenAttributeImpl nextToken() throws IOException + { + if (endOfStream) + { + return null; + } + StringBuilder buffer = new StringBuilder(64); + boolean inNameSpace = false; + int start = readerPosition; + int current; + char c; + while ((current = input.read()) != -1) + { + c = (char) current; + readerPosition++; + if (c == nsStartDelimiter) + { + inNameSpace = true; + } + else if (c == nsEndDelimiter) + { + inNameSpace = false; + } + else if (!inNameSpace && (c == '/')) + { + PackedTokenAttributeImpl qNameToken = new PackedTokenAttributeImpl(); + qNameToken.setEmpty().append(buffer.toString()); + qNameToken.setOffset(start, readerPosition - 1); + qNameToken.setType("QNAME"); + return qNameToken; + } + else if (!inNameSpace && (c == ';')) + { + buffer.append(c); + PackedTokenAttributeImpl lastQNameToken = new PackedTokenAttributeImpl(); + lastQNameToken.setEmpty().append(buffer.toString()); + lastQNameToken.setOffset(start, readerPosition); + lastQNameToken.setType("LASTQNAME"); + return lastQNameToken; + } + + buffer.append(c); + } + int end = readerPosition - 1; + // Stop the final token being returned with an end before the start. + if (start > end) + { + end = start; + } + endOfStream = true; + if (!inNameSpace) + { + PackedTokenAttributeImpl qNameToken = new PackedTokenAttributeImpl(); + qNameToken.setEmpty().append(buffer.toString()); + qNameToken.setOffset(start, end); + qNameToken.setType("QNAME"); + return qNameToken; + } + else + { + throw new IllegalStateException("QName terminated incorrectly: " + buffer.toString()); + } + + } + + @Override + public final boolean incrementToken() throws IOException + { + clearAttributes(); + + PackedTokenAttributeImpl next = next(); + if (next == null) + { + return false; + } + + termAtt.copyBuffer(next.buffer(), 0, next.length()); + offsetAtt.setOffset(correctOffset(next.startOffset()), correctOffset(next.endOffset())); + typeAtt.setType(next.type()); + posIncAtt.setPositionIncrement(next.getPositionIncrement()); + return true; + } + + @Override + public void reset() throws IOException + { + super.reset(); + tokens.clear(); + it = null; + readerPosition = 0; + endOfStream = false; + } + + @Override + public void end() throws IOException + { + super.end(); + int finalOffset = correctOffset(readerPosition); + offsetAtt.setOffset(finalOffset, finalOffset); + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoAnalyzerWrapper.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoAnalyzerWrapper.java index 0e017486a..0da879e34 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoAnalyzerWrapper.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoAnalyzerWrapper.java @@ -1,109 +1,109 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.impl.lucene.analysis.MLAnalayser; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.AnalyzerWrapper; -import org.apache.solr.schema.IndexSchema; - -/** - * Wraps SOLR access to for localising tokens - * As analysers are cached, and anylysers themselves cache token streams we have to be able to switch locales - * inside the MLAnalyser. - * - * @author Andy - * - */ -public class AlfrescoAnalyzerWrapper extends AnalyzerWrapper -{ - public static enum Mode - { - INDEX, QUERY; - } - - IndexSchema schema; - - Mode mode; - - /** - * @param schema - * @param index - */ - public AlfrescoAnalyzerWrapper(IndexSchema schema, Mode mode) - { - super(Analyzer.PER_FIELD_REUSE_STRATEGY); - this.schema = schema; - this.mode = mode; - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.AnalyzerWrapper#getPositionIncrementGap(java.lang.String) - */ - @Override - public int getPositionIncrementGap(String fieldName) - { - return 100; - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.analysis.AnalyzerWrapper#getWrappedAnalyzer(java.lang.String) - */ - @Override - protected Analyzer getWrappedAnalyzer(String fieldName) - { - if(fieldName.contains("l_@{")) - { - return new MLAnalayser(MLAnalysisMode.EXACT_LANGUAGE, schema, mode); - } - else if(fieldName.contains("lt@{")) - { - return new MLAnalayser(MLAnalysisMode.EXACT_LANGUAGE, schema, mode); - } - else - { - if(mode == Mode.INDEX) - { - return schema.getFieldTypeByName("text___").getIndexAnalyzer(); - } - else if(mode == Mode.QUERY) - { - return schema.getFieldTypeByName("text___").getQueryAnalyzer(); - } - else - { - throw new IllegalStateException(); - } - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.impl.lucene.analysis.MLAnalayser; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.AnalyzerWrapper; +import org.apache.solr.schema.IndexSchema; + +/** + * Wraps SOLR access to for localising tokens + * As analysers are cached, and anylysers themselves cache token streams we have to be able to switch locales + * inside the MLAnalyser. + * + * @author Andy + * + */ +public class AlfrescoAnalyzerWrapper extends AnalyzerWrapper +{ + public static enum Mode + { + INDEX, QUERY; + } + + IndexSchema schema; + + Mode mode; + + /** + * @param schema + * @param index + */ + public AlfrescoAnalyzerWrapper(IndexSchema schema, Mode mode) + { + super(Analyzer.PER_FIELD_REUSE_STRATEGY); + this.schema = schema; + this.mode = mode; + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.AnalyzerWrapper#getPositionIncrementGap(java.lang.String) + */ + @Override + public int getPositionIncrementGap(String fieldName) + { + return 100; + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.analysis.AnalyzerWrapper#getWrappedAnalyzer(java.lang.String) + */ + @Override + protected Analyzer getWrappedAnalyzer(String fieldName) + { + if(fieldName.contains("l_@{")) + { + return new MLAnalayser(MLAnalysisMode.EXACT_LANGUAGE, schema, mode); + } + else if(fieldName.contains("lt@{")) + { + return new MLAnalayser(MLAnalysisMode.EXACT_LANGUAGE, schema, mode); + } + else + { + if(mode == Mode.INDEX) + { + return schema.getFieldTypeByName("text___").getIndexAnalyzer(); + } + else if(mode == Mode.QUERY) + { + return schema.getFieldTypeByName("text___").getQueryAnalyzer(); + } + else + { + throw new IllegalStateException(); + } + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldType.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldType.java index 6210511c2..8028cfd6a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldType.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldType.java @@ -1,273 +1,273 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import java.io.IOException; -import java.text.Collator; -import java.util.Locale; - -import org.alfresco.service.cmr.repository.MLText; -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.FieldComparator; -import org.apache.lucene.search.FieldComparatorSource; -import org.apache.lucene.search.LeafFieldComparator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.SortField; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.schema.StrField; -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * @author Andy - */ -public class AlfrescoCollatableMLTextFieldType extends StrField -{ - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#getSortField(org.apache.solr.schema.SchemaField, boolean) - */ - @Override - public SortField getSortField(SchemaField field, boolean reverse) - { - return new SortField(field.getName(), new MLTextSortFieldComparatorSource(), reverse); - } - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#marshalSortValue(java.lang.Object) - */ - @Override - public Object marshalSortValue(Object value) - { - return value; - } - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#unmarshalSortValue(java.lang.Object) - */ - @Override - public Object unmarshalSortValue(Object value) - { - return value; - } - - - public static class MLTextSortFieldComparatorSource extends FieldComparatorSource - { - - /* - * (non-Javadoc) - * @see org.apache.lucene.search.FieldComparatorSource#newComparator(java.lang.String, int, int, boolean) - */ - @Override - public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) - { - return new MLTextSortFieldComparator(numHits, fieldname, I18NUtil.getLocale()); - } - - } - - - /* - * Adapted from org.apache.lucene.search.FieldComparator.TermValComparator - */ - public static final class MLTextSortFieldComparator extends FieldComparator implements LeafFieldComparator - { - - private final String[] values; - - private BinaryDocValues docTerms; - - /** - * An array of flags - one for each document in the segment. Each bit is set to true if the document has the - * field or false otherwise. If this is set to null then all docs in the segment have the field. - */ - Bits docsWithField; - - private final String field; - - Collator collator; - - String bottom; - - String top; - - Locale collatorLocale; - - MLTextSortFieldComparator(int numHits, String field, Locale collatorLocale) - { - values = new String[numHits]; - this.field = field; - this.collatorLocale = collatorLocale; - collator = Collator.getInstance(collatorLocale); - } - - public int compare(int slot1, int slot2) - { - final String val1 = values[slot1]; - final String val2 = values[slot2]; - return compareValues(val1, val2); - } - - public void setBottom(final int bottom) - { - this.bottom = values[bottom]; - } - - public int compareBottom(int doc) - { - final String comparableString = findBestValue(doc, docTerms.get(doc)); - return compareValues(bottom, comparableString); - - } - - public void copy(int slot, int doc) - { - values[slot] = findBestValue(doc, docTerms.get(doc)); - } - - public String value(int slot) - { - return values[slot]; - } - - private String findBestValue(int doc, BytesRef term) - { - if (term.length == 0 && docsWithField != null && docsWithField.get(doc) == false) - { - return null; - } - - String withLocale = term.utf8ToString(); - - // split string into MLText object - if (withLocale == null) - { - return withLocale; - } - else if (withLocale.startsWith("\u0000")) - { - MLText mlText = new MLText(); - String[] parts = withLocale.split("\u0000"); - for (int i = 0; (i + 2) <= parts.length; i += 3) - { - Locale locale = null; - String[] localeParts = parts[i + 1].split("_"); - if (localeParts.length == 1) - { - locale = new Locale(localeParts[0]); - } - else if (localeParts.length == 2) - { - locale = new Locale(localeParts[0], localeParts[1]); - } - else if (localeParts.length == 3) - { - locale = new Locale(localeParts[0], localeParts[1], localeParts[2]); - } - if (locale != null) - { - if (i + 2 == parts.length) - { - mlText.addValue(locale, ""); - } - else - { - mlText.addValue(locale, parts[i + 2]); - } - } - } - return mlText.getClosestValue(collatorLocale); - } - else - { - return withLocale; - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#setTopValue(java.lang.Object) - */ - @Override - public void setTopValue(String value) - { - this.top = value; - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#compareTop(int) - */ - @Override - public int compareTop(int doc) throws IOException - { - final String comparableString = findBestValue(doc, docTerms.get(doc)); - return compareValues(top, comparableString); - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#setNextReader(org.apache.lucene.index.AtomicReaderContext) - */ - @Override - public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException - { - docTerms = DocValues.getBinary(context.reader(), field); - docsWithField = DocValues.getDocsWithField(context.reader(), field); - if (docsWithField instanceof Bits.MatchAllBits) - { - docsWithField = null; - } - return this; - } - - @Override - public int compareValues(String val1, String val2) - { - if (val1 == null) - { - if (val2 == null) - { - return 0; - } - return -1; - } - else if (val2 == null) - { - return 1; - } - return collator.compare(val1, val2); - } - - @Override - public void setScorer(Scorer scorer) - { - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import java.io.IOException; +import java.text.Collator; +import java.util.Locale; + +import org.alfresco.service.cmr.repository.MLText; +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.FieldComparator; +import org.apache.lucene.search.FieldComparatorSource; +import org.apache.lucene.search.LeafFieldComparator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.SortField; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.schema.StrField; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + */ +public class AlfrescoCollatableMLTextFieldType extends StrField +{ + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#getSortField(org.apache.solr.schema.SchemaField, boolean) + */ + @Override + public SortField getSortField(SchemaField field, boolean reverse) + { + return new SortField(field.getName(), new MLTextSortFieldComparatorSource(), reverse); + } + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#marshalSortValue(java.lang.Object) + */ + @Override + public Object marshalSortValue(Object value) + { + return value; + } + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#unmarshalSortValue(java.lang.Object) + */ + @Override + public Object unmarshalSortValue(Object value) + { + return value; + } + + + public static class MLTextSortFieldComparatorSource extends FieldComparatorSource + { + + /* + * (non-Javadoc) + * @see org.apache.lucene.search.FieldComparatorSource#newComparator(java.lang.String, int, int, boolean) + */ + @Override + public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) + { + return new MLTextSortFieldComparator(numHits, fieldname, I18NUtil.getLocale()); + } + + } + + + /* + * Adapted from org.apache.lucene.search.FieldComparator.TermValComparator + */ + public static final class MLTextSortFieldComparator extends FieldComparator implements LeafFieldComparator + { + + private final String[] values; + + private BinaryDocValues docTerms; + + /** + * An array of flags - one for each document in the segment. Each bit is set to true if the document has the + * field or false otherwise. If this is set to null then all docs in the segment have the field. + */ + Bits docsWithField; + + private final String field; + + Collator collator; + + String bottom; + + String top; + + Locale collatorLocale; + + MLTextSortFieldComparator(int numHits, String field, Locale collatorLocale) + { + values = new String[numHits]; + this.field = field; + this.collatorLocale = collatorLocale; + collator = Collator.getInstance(collatorLocale); + } + + public int compare(int slot1, int slot2) + { + final String val1 = values[slot1]; + final String val2 = values[slot2]; + return compareValues(val1, val2); + } + + public void setBottom(final int bottom) + { + this.bottom = values[bottom]; + } + + public int compareBottom(int doc) + { + final String comparableString = findBestValue(doc, docTerms.get(doc)); + return compareValues(bottom, comparableString); + + } + + public void copy(int slot, int doc) + { + values[slot] = findBestValue(doc, docTerms.get(doc)); + } + + public String value(int slot) + { + return values[slot]; + } + + private String findBestValue(int doc, BytesRef term) + { + if (term.length == 0 && docsWithField != null && docsWithField.get(doc) == false) + { + return null; + } + + String withLocale = term.utf8ToString(); + + // split string into MLText object + if (withLocale == null) + { + return withLocale; + } + else if (withLocale.startsWith("\u0000")) + { + MLText mlText = new MLText(); + String[] parts = withLocale.split("\u0000"); + for (int i = 0; (i + 2) <= parts.length; i += 3) + { + Locale locale = null; + String[] localeParts = parts[i + 1].split("_"); + if (localeParts.length == 1) + { + locale = new Locale(localeParts[0]); + } + else if (localeParts.length == 2) + { + locale = new Locale(localeParts[0], localeParts[1]); + } + else if (localeParts.length == 3) + { + locale = new Locale(localeParts[0], localeParts[1], localeParts[2]); + } + if (locale != null) + { + if (i + 2 == parts.length) + { + mlText.addValue(locale, ""); + } + else + { + mlText.addValue(locale, parts[i + 2]); + } + } + } + return mlText.getClosestValue(collatorLocale); + } + else + { + return withLocale; + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#setTopValue(java.lang.Object) + */ + @Override + public void setTopValue(String value) + { + this.top = value; + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#compareTop(int) + */ + @Override + public int compareTop(int doc) throws IOException + { + final String comparableString = findBestValue(doc, docTerms.get(doc)); + return compareValues(top, comparableString); + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#setNextReader(org.apache.lucene.index.AtomicReaderContext) + */ + @Override + public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException + { + docTerms = DocValues.getBinary(context.reader(), field); + docsWithField = DocValues.getDocsWithField(context.reader(), field); + if (docsWithField instanceof Bits.MatchAllBits) + { + docsWithField = null; + } + return this; + } + + @Override + public int compareValues(String val1, String val2) + { + if (val1 == null) + { + if (val2 == null) + { + return 0; + } + return -1; + } + else if (val2 == null) + { + return 1; + } + return collator.compare(val1, val2); + } + + @Override + public void setScorer(Scorer scorer) + { + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableTextFieldType.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableTextFieldType.java index 5ab5783bc..0bd6f84ab 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableTextFieldType.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCollatableTextFieldType.java @@ -1,245 +1,245 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import java.io.IOException; -import java.text.Collator; -import java.util.Locale; - -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.FieldComparator; -import org.apache.lucene.search.FieldComparatorSource; -import org.apache.lucene.search.LeafFieldComparator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.SortField; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.schema.StrField; -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * @author Andy - * - */ -public class AlfrescoCollatableTextFieldType extends StrField -{ - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#getSortField(org.apache.solr.schema.SchemaField, boolean) - */ - @Override - public SortField getSortField(SchemaField field, boolean reverse) - { - return new SortField(field.getName(), new TextSortFieldComparatorSource(), reverse); - } - - - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#marshalSortValue(java.lang.Object) - */ - @Override - public Object marshalSortValue(Object value) - { - return value; - } - - - /* (non-Javadoc) - * @see org.apache.solr.schema.StrField#unmarshalSortValue(java.lang.Object) - */ - @Override - public Object unmarshalSortValue(Object value) - { - return value; - } - - - - - public static class TextSortFieldComparatorSource extends FieldComparatorSource - { - /* - * (non-Javadoc) - * @see org.apache.lucene.search.FieldComparatorSource#newComparator(java.lang.String, int, int, boolean) - */ - @Override - public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) - { - return new TextSortFieldComparator(numHits, fieldname, I18NUtil.getLocale()); - } - } - - - /* - * Adapted from org.apache.lucene.search.FieldComparator.TermValComparator - */ - public static final class TextSortFieldComparator extends FieldComparator implements LeafFieldComparator - { - - private final String[] values; - - private BinaryDocValues docTerms; - - /** - * An array of flags - one for each document in the segment. Each bit is set to true if the document has the - * field or false otherwise. If this is set to null then all docs in the segment have the field. - */ - Bits docsWithField; - - private final String field; - - Collator collator; - - String bottom; - - String top; - - Locale collatorLocale; - - TextSortFieldComparator(int numHits, String field, Locale collatorLocale) - { - values = new String[numHits]; - this.field = field; - this.collatorLocale = collatorLocale; - collator = Collator.getInstance(collatorLocale); - } - - public int compare(int slot1, int slot2) - { - final String val1 = values[slot1]; - final String val2 = values[slot2]; - return compareValues(val1, val2); - } - - public void setBottom(final int bottom) - { - this.bottom = values[bottom]; - } - - public int compareBottom(int doc) - { - final String comparableString = findBestValue(doc, docTerms.get(doc)); - return compareValues(bottom, comparableString); - } - - public void copy(int slot, int doc) - { - values[slot] = findBestValue(doc, docTerms.get(doc)); - } - - public String value(int slot) - { - return values[slot]; - } - - private String findBestValue(int doc, BytesRef term) - { - if (term.length == 0 && docsWithField != null && docsWithField.get(doc) == false) - { - return null; - } - - String withLocale = term.utf8ToString(); - - // split strin into MLText object - if (withLocale == null) - { - return withLocale; - } - else if (withLocale.startsWith("\u0000")) - { - String[] parts = withLocale.split("\u0000"); - return parts[2]; - } - else - { - return withLocale; - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#setTopValue(java.lang.Object) - */ - @Override - public void setTopValue(String value) - { - this.top = value; - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#compareTop(int) - */ - @Override - public int compareTop(int doc) throws IOException - { - final String comparableString = findBestValue(doc, docTerms.get(doc)); - return compareValues(top, comparableString); - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.FieldComparator#setNextReader(org.apache.lucene.index.AtomicReaderContext) - */ - @Override - public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException - { - docTerms = DocValues.getBinary(context.reader(), field); - docsWithField = DocValues.getDocsWithField(context.reader(), field); - if (docsWithField instanceof Bits.MatchAllBits) { - docsWithField = null; - } - return this; - } - - @Override - public int compareValues(String val1, String val2) - { - if (val1 == null) - { - if (val2 == null) - { - return 0; - } - return -1; - } - else if (val2 == null) - { - return 1; - } - return collator.compare(val1, val2); - } - - @Override - public void setScorer(Scorer scorer) {} - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import java.io.IOException; +import java.text.Collator; +import java.util.Locale; + +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.FieldComparator; +import org.apache.lucene.search.FieldComparatorSource; +import org.apache.lucene.search.LeafFieldComparator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.SortField; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.schema.StrField; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + * + */ +public class AlfrescoCollatableTextFieldType extends StrField +{ + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#getSortField(org.apache.solr.schema.SchemaField, boolean) + */ + @Override + public SortField getSortField(SchemaField field, boolean reverse) + { + return new SortField(field.getName(), new TextSortFieldComparatorSource(), reverse); + } + + + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#marshalSortValue(java.lang.Object) + */ + @Override + public Object marshalSortValue(Object value) + { + return value; + } + + + /* (non-Javadoc) + * @see org.apache.solr.schema.StrField#unmarshalSortValue(java.lang.Object) + */ + @Override + public Object unmarshalSortValue(Object value) + { + return value; + } + + + + + public static class TextSortFieldComparatorSource extends FieldComparatorSource + { + /* + * (non-Javadoc) + * @see org.apache.lucene.search.FieldComparatorSource#newComparator(java.lang.String, int, int, boolean) + */ + @Override + public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) + { + return new TextSortFieldComparator(numHits, fieldname, I18NUtil.getLocale()); + } + } + + + /* + * Adapted from org.apache.lucene.search.FieldComparator.TermValComparator + */ + public static final class TextSortFieldComparator extends FieldComparator implements LeafFieldComparator + { + + private final String[] values; + + private BinaryDocValues docTerms; + + /** + * An array of flags - one for each document in the segment. Each bit is set to true if the document has the + * field or false otherwise. If this is set to null then all docs in the segment have the field. + */ + Bits docsWithField; + + private final String field; + + Collator collator; + + String bottom; + + String top; + + Locale collatorLocale; + + TextSortFieldComparator(int numHits, String field, Locale collatorLocale) + { + values = new String[numHits]; + this.field = field; + this.collatorLocale = collatorLocale; + collator = Collator.getInstance(collatorLocale); + } + + public int compare(int slot1, int slot2) + { + final String val1 = values[slot1]; + final String val2 = values[slot2]; + return compareValues(val1, val2); + } + + public void setBottom(final int bottom) + { + this.bottom = values[bottom]; + } + + public int compareBottom(int doc) + { + final String comparableString = findBestValue(doc, docTerms.get(doc)); + return compareValues(bottom, comparableString); + } + + public void copy(int slot, int doc) + { + values[slot] = findBestValue(doc, docTerms.get(doc)); + } + + public String value(int slot) + { + return values[slot]; + } + + private String findBestValue(int doc, BytesRef term) + { + if (term.length == 0 && docsWithField != null && docsWithField.get(doc) == false) + { + return null; + } + + String withLocale = term.utf8ToString(); + + // split strin into MLText object + if (withLocale == null) + { + return withLocale; + } + else if (withLocale.startsWith("\u0000")) + { + String[] parts = withLocale.split("\u0000"); + return parts[2]; + } + else + { + return withLocale; + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#setTopValue(java.lang.Object) + */ + @Override + public void setTopValue(String value) + { + this.top = value; + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#compareTop(int) + */ + @Override + public int compareTop(int doc) throws IOException + { + final String comparableString = findBestValue(doc, docTerms.get(doc)); + return compareValues(top, comparableString); + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.FieldComparator#setNextReader(org.apache.lucene.index.AtomicReaderContext) + */ + @Override + public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException + { + docTerms = DocValues.getBinary(context.reader(), field); + docsWithField = DocValues.getDocsWithField(context.reader(), field); + if (docsWithField instanceof Bits.MatchAllBits) { + docsWithField = null; + } + return this; + } + + @Override + public int compareValues(String val1, String val2) + { + if (val1 == null) + { + if (val2 == null) + { + return 0; + } + return -1; + } + else if (val2 == null) + { + return 1; + } + return collator.compare(val1, val2); + } + + @Override + public void setScorer(Scorer scorer) {} + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java index 4b2a75041..57c46ba39 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -2232,4 +2232,4 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler return response; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoFieldType.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoFieldType.java index 757a25591..1f000e4a4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoFieldType.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoFieldType.java @@ -1,271 +1,271 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.core.KeywordAnalyzer; -import org.apache.lucene.analysis.core.KeywordTokenizerFactory; -import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; -import org.apache.lucene.analysis.util.AbstractAnalysisFactory; -import org.apache.lucene.analysis.util.CharFilterFactory; -import org.apache.lucene.analysis.util.MultiTermAwareComponent; -import org.apache.lucene.analysis.util.TokenFilterFactory; -import org.apache.lucene.analysis.util.TokenizerFactory; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.SortField; -import org.apache.lucene.search.TermRangeQuery; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.IOUtils; -import org.apache.lucene.util.QueryBuilder; -import org.apache.solr.analysis.TokenizerChain; -import org.apache.solr.common.SolrException; -import org.apache.solr.response.TextResponseWriter; -import org.apache.solr.schema.FieldType; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.QParser; -import org.apache.solr.uninverting.UninvertingReader.Type; - -/** - * Basic behaviour filtched from TextField - * - * @author Andy - */ -public class AlfrescoFieldType extends FieldType -{ - IndexSchema schema; - - /* - * (non-Javadoc) - * @see org.apache.solr.schema.FieldType#init(org.apache.solr.schema.IndexSchema, java.util.Map) - */ - @Override - protected void init(IndexSchema schema, Map args) - { - this.schema = schema; - properties |= TOKENIZED; - properties &= ~OMIT_TF_POSITIONS; - super.init(schema, args); - - // TODO: Wire up localised analysis driven from the schema - // for now we do something basic - setIndexAnalyzer(new AlfrescoAnalyzerWrapper(schema, AlfrescoAnalyzerWrapper.Mode.INDEX)); - setQueryAnalyzer(new AlfrescoAnalyzerWrapper(schema, AlfrescoAnalyzerWrapper.Mode.QUERY)); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.schema.FieldType#getSortField(org.apache.solr.schema.SchemaField, boolean) - */ - @Override - public SortField getSortField(SchemaField field, boolean reverse) - { - return getStringSort(field, reverse); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.schema.FieldType#write(org.apache.solr.response.TextResponseWriter, java.lang.String, - * org.apache.lucene.index.IndexableField) - */ - @Override - public void write(TextResponseWriter writer, String name, IndexableField f) throws IOException - { - writer.writeStr(name, f.stringValue(), true); - } - - @Override - public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) - { - return parseFieldQuery(parser, getQueryAnalyzer(), field.getName(), externalVal); - } - - @Override - public Object toObject(SchemaField sf, BytesRef term) - { - return term.utf8ToString(); - } - - @Override - public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive, boolean maxInclusive) - { - Analyzer multiAnalyzer = constructMultiTermAnalyzer(getQueryAnalyzer()); - BytesRef lower = analyzeMultiTerm(field.getName(), part1, multiAnalyzer); - BytesRef upper = analyzeMultiTerm(field.getName(), part2, multiAnalyzer); - return new TermRangeQuery(field.getName(), lower, upper, minInclusive, maxInclusive); - } - - private Analyzer constructMultiTermAnalyzer(Analyzer queryAnalyzer) - { - if (queryAnalyzer == null) - return null; - - if (!(queryAnalyzer instanceof TokenizerChain)) - { - return new KeywordAnalyzer(); - } - - TokenizerChain tc = (TokenizerChain) queryAnalyzer; - MultiTermChainBuilder builder = new MultiTermChainBuilder(); - - CharFilterFactory[] charFactories = tc.getCharFilterFactories(); - if (charFactories != null) - { - for (CharFilterFactory fact : charFactories) - { - builder.add(fact); - } - } - - builder.add(tc.getTokenizerFactory()); - - for (TokenFilterFactory fact : tc.getTokenFilterFactories()) - { - builder.add(fact); - } - - return builder.build(); - } - - private static class MultiTermChainBuilder - { - static final KeywordTokenizerFactory keyFactory = new KeywordTokenizerFactory(new HashMap()); - - ArrayList charFilters = null; - - ArrayList filters = new ArrayList(2); - - TokenizerFactory tokenizer = keyFactory; - - public void add(Object current) - { - if (!(current instanceof MultiTermAwareComponent)) - return; - AbstractAnalysisFactory newComponent = ((MultiTermAwareComponent) current).getMultiTermComponent(); - if (newComponent instanceof TokenFilterFactory) - { - if (filters == null) - { - filters = new ArrayList(2); - } - filters.add((TokenFilterFactory) newComponent); - } - else if (newComponent instanceof TokenizerFactory) - { - tokenizer = (TokenizerFactory) newComponent; - } - else if (newComponent instanceof CharFilterFactory) - { - if (charFilters == null) - { - charFilters = new ArrayList(1); - } - charFilters.add((CharFilterFactory) newComponent); - - } - else - { - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown analysis component from MultiTermAwareComponent: " + newComponent); - } - } - - public TokenizerChain build() - { - CharFilterFactory[] charFilterArr = charFilters == null ? null : charFilters.toArray(new CharFilterFactory[charFilters.size()]); - TokenFilterFactory[] filterArr = filters == null ? new TokenFilterFactory[0] : filters.toArray(new TokenFilterFactory[filters.size()]); - return new TokenizerChain(charFilterArr, tokenizer, filterArr); - } - - } - - public static BytesRef analyzeMultiTerm(String field, String part, Analyzer analyzerIn) - { - if (part == null || analyzerIn == null) - return null; - - TokenStream source = null; - try - { - source = analyzerIn.tokenStream(field, part); - source.reset(); - - TermToBytesRefAttribute termAtt = source.getAttribute(TermToBytesRefAttribute.class); - BytesRef bytes = termAtt.getBytesRef(); - - if (!source.incrementToken()) - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "analyzer returned no terms for multiTerm term: " + part); - if (source.incrementToken()) - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "analyzer returned too many terms for multiTerm term: " + part); - - source.end(); - return BytesRef.deepCopyOf(bytes); - } - catch (IOException e) - { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "error analyzing range part: " + part, e); - } - finally - { - IOUtils.closeWhileHandlingException(source); - } - } - - static Query parseFieldQuery(QParser parser, Analyzer analyzer, String field, String queryText) - { - // note, this method always worked this way (but nothing calls it?) because it has no idea of quotes... - return new QueryBuilder(analyzer).createPhraseQuery(field, queryText); - } - - @Override - public Object marshalSortValue(Object value) - { - return marshalStringSortValue(value); - } - - @Override - public Object unmarshalSortValue(Object value) - { - return unmarshalStringSortValue(value); - } - - protected boolean supportsAnalyzers() - { - return true; - } - - @Override - public Type getUninversionType(SchemaField sf) { - return Type.SORTED_SET_BINARY; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.core.KeywordAnalyzer; +import org.apache.lucene.analysis.core.KeywordTokenizerFactory; +import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; +import org.apache.lucene.analysis.util.AbstractAnalysisFactory; +import org.apache.lucene.analysis.util.CharFilterFactory; +import org.apache.lucene.analysis.util.MultiTermAwareComponent; +import org.apache.lucene.analysis.util.TokenFilterFactory; +import org.apache.lucene.analysis.util.TokenizerFactory; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.TermRangeQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.IOUtils; +import org.apache.lucene.util.QueryBuilder; +import org.apache.solr.analysis.TokenizerChain; +import org.apache.solr.common.SolrException; +import org.apache.solr.response.TextResponseWriter; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.QParser; +import org.apache.solr.uninverting.UninvertingReader.Type; + +/** + * Basic behaviour filtched from TextField + * + * @author Andy + */ +public class AlfrescoFieldType extends FieldType +{ + IndexSchema schema; + + /* + * (non-Javadoc) + * @see org.apache.solr.schema.FieldType#init(org.apache.solr.schema.IndexSchema, java.util.Map) + */ + @Override + protected void init(IndexSchema schema, Map args) + { + this.schema = schema; + properties |= TOKENIZED; + properties &= ~OMIT_TF_POSITIONS; + super.init(schema, args); + + // TODO: Wire up localised analysis driven from the schema + // for now we do something basic + setIndexAnalyzer(new AlfrescoAnalyzerWrapper(schema, AlfrescoAnalyzerWrapper.Mode.INDEX)); + setQueryAnalyzer(new AlfrescoAnalyzerWrapper(schema, AlfrescoAnalyzerWrapper.Mode.QUERY)); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.schema.FieldType#getSortField(org.apache.solr.schema.SchemaField, boolean) + */ + @Override + public SortField getSortField(SchemaField field, boolean reverse) + { + return getStringSort(field, reverse); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.schema.FieldType#write(org.apache.solr.response.TextResponseWriter, java.lang.String, + * org.apache.lucene.index.IndexableField) + */ + @Override + public void write(TextResponseWriter writer, String name, IndexableField f) throws IOException + { + writer.writeStr(name, f.stringValue(), true); + } + + @Override + public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) + { + return parseFieldQuery(parser, getQueryAnalyzer(), field.getName(), externalVal); + } + + @Override + public Object toObject(SchemaField sf, BytesRef term) + { + return term.utf8ToString(); + } + + @Override + public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive, boolean maxInclusive) + { + Analyzer multiAnalyzer = constructMultiTermAnalyzer(getQueryAnalyzer()); + BytesRef lower = analyzeMultiTerm(field.getName(), part1, multiAnalyzer); + BytesRef upper = analyzeMultiTerm(field.getName(), part2, multiAnalyzer); + return new TermRangeQuery(field.getName(), lower, upper, minInclusive, maxInclusive); + } + + private Analyzer constructMultiTermAnalyzer(Analyzer queryAnalyzer) + { + if (queryAnalyzer == null) + return null; + + if (!(queryAnalyzer instanceof TokenizerChain)) + { + return new KeywordAnalyzer(); + } + + TokenizerChain tc = (TokenizerChain) queryAnalyzer; + MultiTermChainBuilder builder = new MultiTermChainBuilder(); + + CharFilterFactory[] charFactories = tc.getCharFilterFactories(); + if (charFactories != null) + { + for (CharFilterFactory fact : charFactories) + { + builder.add(fact); + } + } + + builder.add(tc.getTokenizerFactory()); + + for (TokenFilterFactory fact : tc.getTokenFilterFactories()) + { + builder.add(fact); + } + + return builder.build(); + } + + private static class MultiTermChainBuilder + { + static final KeywordTokenizerFactory keyFactory = new KeywordTokenizerFactory(new HashMap()); + + ArrayList charFilters = null; + + ArrayList filters = new ArrayList(2); + + TokenizerFactory tokenizer = keyFactory; + + public void add(Object current) + { + if (!(current instanceof MultiTermAwareComponent)) + return; + AbstractAnalysisFactory newComponent = ((MultiTermAwareComponent) current).getMultiTermComponent(); + if (newComponent instanceof TokenFilterFactory) + { + if (filters == null) + { + filters = new ArrayList(2); + } + filters.add((TokenFilterFactory) newComponent); + } + else if (newComponent instanceof TokenizerFactory) + { + tokenizer = (TokenizerFactory) newComponent; + } + else if (newComponent instanceof CharFilterFactory) + { + if (charFilters == null) + { + charFilters = new ArrayList(1); + } + charFilters.add((CharFilterFactory) newComponent); + + } + else + { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown analysis component from MultiTermAwareComponent: " + newComponent); + } + } + + public TokenizerChain build() + { + CharFilterFactory[] charFilterArr = charFilters == null ? null : charFilters.toArray(new CharFilterFactory[charFilters.size()]); + TokenFilterFactory[] filterArr = filters == null ? new TokenFilterFactory[0] : filters.toArray(new TokenFilterFactory[filters.size()]); + return new TokenizerChain(charFilterArr, tokenizer, filterArr); + } + + } + + public static BytesRef analyzeMultiTerm(String field, String part, Analyzer analyzerIn) + { + if (part == null || analyzerIn == null) + return null; + + TokenStream source = null; + try + { + source = analyzerIn.tokenStream(field, part); + source.reset(); + + TermToBytesRefAttribute termAtt = source.getAttribute(TermToBytesRefAttribute.class); + BytesRef bytes = termAtt.getBytesRef(); + + if (!source.incrementToken()) + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "analyzer returned no terms for multiTerm term: " + part); + if (source.incrementToken()) + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "analyzer returned too many terms for multiTerm term: " + part); + + source.end(); + return BytesRef.deepCopyOf(bytes); + } + catch (IOException e) + { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "error analyzing range part: " + part, e); + } + finally + { + IOUtils.closeWhileHandlingException(source); + } + } + + static Query parseFieldQuery(QParser parser, Analyzer analyzer, String field, String queryText) + { + // note, this method always worked this way (but nothing calls it?) because it has no idea of quotes... + return new QueryBuilder(analyzer).createPhraseQuery(field, queryText); + } + + @Override + public Object marshalSortValue(Object value) + { + return marshalStringSortValue(value); + } + + @Override + public Object unmarshalSortValue(Object value) + { + return unmarshalStringSortValue(value); + } + + protected boolean supportsAnalyzers() + { + return true; + } + + @Override + public Type getUninversionType(SchemaField sf) { + return Type.SORTED_SET_BINARY; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoLockException.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoLockException.java index 3c8e677b1..a5a346e3f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoLockException.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoLockException.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolr4FunctionEvaluationContext.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolr4FunctionEvaluationContext.java index 33c49d575..8ab2990a0 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolr4FunctionEvaluationContext.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolr4FunctionEvaluationContext.java @@ -1,76 +1,76 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; -import org.alfresco.repo.search.impl.parsers.FTSQueryException; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.apache.solr.schema.IndexSchema; - -/** - * @author Andy - * - */ -public class AlfrescoSolr4FunctionEvaluationContext extends AlfrescoFunctionEvaluationContext -{ - - private IndexSchema indexSchema; - - /** - * @param namespacePrefixResolver - * @param dictionaryService - * @param defaultNamespace - * @param indexSchema - */ - public AlfrescoSolr4FunctionEvaluationContext(NamespacePrefixResolver namespacePrefixResolver, DictionaryService dictionaryService, String defaultNamespace, IndexSchema indexSchema) - { - super(namespacePrefixResolver, dictionaryService, defaultNamespace); - this.indexSchema = indexSchema; - } - - public String getLuceneFieldName(String propertyName) - { - - if(indexSchema.getFieldOrNull(propertyName) != null) - { - return propertyName; - } - else - { - try - { - return super.getLuceneFieldName(propertyName); - } - catch(FTSQueryException e) - { - // unknown - return "_dummy_"; - } - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; +import org.alfresco.repo.search.impl.parsers.FTSQueryException; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.apache.solr.schema.IndexSchema; + +/** + * @author Andy + * + */ +public class AlfrescoSolr4FunctionEvaluationContext extends AlfrescoFunctionEvaluationContext +{ + + private IndexSchema indexSchema; + + /** + * @param namespacePrefixResolver + * @param dictionaryService + * @param defaultNamespace + * @param indexSchema + */ + public AlfrescoSolr4FunctionEvaluationContext(NamespacePrefixResolver namespacePrefixResolver, DictionaryService dictionaryService, String defaultNamespace, IndexSchema indexSchema) + { + super(namespacePrefixResolver, dictionaryService, defaultNamespace); + this.indexSchema = indexSchema; + } + + public String getLuceneFieldName(String propertyName) + { + + if(indexSchema.getFieldOrNull(propertyName) != null) + { + return propertyName; + } + else + { + try + { + return super.getLuceneFieldName(propertyName); + } + catch(FTSQueryException e) + { + // unknown + return "_dummy_"; + } + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolrDataModel.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolrDataModel.java index f32dab46e..d8c4a7cd3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolrDataModel.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoSolrDataModel.java @@ -1,1892 +1,1892 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import static java.util.Collections.unmodifiableList; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.stream.Collectors; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.opencmis.dictionary.CMISAbstractDictionaryService; -import org.alfresco.opencmis.dictionary.CMISDictionaryService; -import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; -import org.alfresco.opencmis.dictionary.QNameFilter; -import org.alfresco.opencmis.search.CMISQueryOptions; -import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; -import org.alfresco.opencmis.search.CMISQueryParser; -import org.alfresco.opencmis.search.CmisFunctionEvaluationContext; -import org.alfresco.repo.dictionary.CompiledModelsCache; -import org.alfresco.repo.dictionary.DictionaryComponent; -import org.alfresco.repo.dictionary.DictionaryDAOImpl; -import org.alfresco.repo.dictionary.Facetable; -import org.alfresco.repo.dictionary.IndexTokenisationMode; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.M2ModelDiff; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.repo.i18n.StaticMessageLookup; -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.repo.search.impl.QueryParserUtils; -import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; -import org.alfresco.repo.search.impl.parsers.FTSParser; -import org.alfresco.repo.search.impl.parsers.FTSQueryParser; -import org.alfresco.repo.search.impl.querymodel.Constraint; -import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; -import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective; -import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilder; -import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryModelFactory; -import org.alfresco.repo.search.impl.querymodel.impl.lucene.QueryBuilderContext; -import org.alfresco.repo.tenant.SingleTServiceImpl; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.dictionary.DictionaryException; -import org.alfresco.service.cmr.dictionary.ModelDefinition; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.namespace.NamespaceException; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.AlfrescoClientDataModelServicesFactory.DictionaryKey; -import org.alfresco.solr.client.AlfrescoModel; -import org.alfresco.solr.query.Lucene4QueryBuilderContextSolrImpl; -import org.alfresco.solr.query.Solr4QueryParser; -import org.alfresco.solr.tracker.pool.DefaultTrackerPoolFactory; -import org.alfresco.solr.tracker.pool.TrackerPoolFactory; -import org.alfresco.util.NumericEncoder; -import org.alfresco.util.Pair; -import org.alfresco.util.cache.DefaultAsynchronouslyRefreshedCacheRegistry; -import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; -import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin; -import org.apache.chemistry.opencmis.commons.enums.CmisVersion; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Sort; -import org.apache.lucene.util.Version; -import org.apache.solr.core.CoreDescriptorDecorator; -import org.apache.solr.core.SolrResourceLoader; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.context.support.FileSystemXmlApplicationContext; - -import static java.util.Optional.ofNullable; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_HOUR_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MONTH_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_SECOND_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_YEAR_FIELD_SUFFIX; - -/** - * @author Andy - * - */ -public class AlfrescoSolrDataModel implements QueryConstants -{ - public static class TenantDbId - { - public String tenant; - public Long dbId; - - public Map optionalBag = new HashMap<>(); - - public void setProperty(String name, Object value) - { - optionalBag.put(name, value); - } - } - - public enum FieldUse - { - FTS, // Term/Phrase/Range/Fuzzy/Prefix/Proximity - ID, // ExactRange - Comparison, In, Upper, Lower, Wildcard - EXACT, // Exact - FACET, // Field, Range, Query - MULTI_FACET, // Text fields will require cross language support to avoid tokenisation for facets - STATS, // Stats - SORT, // Locale - SUGGESTION, - COMPLETION, - HIGHLIGHT - } - - public enum SpecializedFieldType - { - CONTENT_DOCID, - CONTENT_SIZE, - CONTENT_LOCALE, - CONTENT_MIMETYPE, - CONTENT_ENCODING, - TRANSFORMATION_STATUS, - TRANSFORMATION_TIME, - TRANSFORMATION_EXCEPTION, - UNIT_OF_TIME_SECOND, - UNIT_OF_TIME_MINUTE, - UNIT_OF_TIME_HOUR, - UNIT_OF_TIME_DAY, - UNIT_OF_TIME_DAY_OF_WEEK, - UNIT_OF_TIME_DAY_OF_YEAR, - UNIT_OF_TIME_MONTH, - UNIT_OF_TIME_QUARTER, - UNIT_OF_TIME_YEAR - } - - public static final String CONTENT_S_LOCALE_PREFIX = "content@s__locale@"; - - static final String PART_FIELDNAME_PREFIX = "part@sd@"; - static final Set DATE_PART_SUFFIXES = - Set.of(UNIT_OF_TIME_YEAR_FIELD_SUFFIX, - UNIT_OF_TIME_QUARTER_FIELD_SUFFIX, - UNIT_OF_TIME_MONTH_FIELD_SUFFIX, - UNIT_OF_TIME_DAY_FIELD_SUFFIX, - UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX, - UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX, - UNIT_OF_TIME_HOUR_FIELD_SUFFIX, - UNIT_OF_TIME_MINUTE_FIELD_SUFFIX, - UNIT_OF_TIME_SECOND_FIELD_SUFFIX); - /** - * Infix used for denoting a primitive single valued field with no doc values enabled - */ - private static final String SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER = "@s_@"; - - /** - * Infix used for denoting a primitive single valued field with no doc values enabled - */ - private static final String SINGLE_VALUE_WITH_DOC_VALUES_MARKER = "@sd@"; - - static final String SHARED_PROPERTIES = "shared.properties"; - - protected final static Logger log = LoggerFactory.getLogger(AlfrescoSolrDataModel.class); - - private static final String CHANGE_SET = "CHANGE_SET"; - - private static final String TX = "TX"; - - public static final String DEFAULT_TENANT = "_DEFAULT_"; - - private final static AlfrescoSolrDataModel INSTANCE = new AlfrescoSolrDataModel(); - - private final TenantService tenantService; - - private final NamespaceDAO namespaceDAO; - - private final DictionaryDAOImpl dictionaryDAO; - - private final Map dictionaryServices; - - private final Map cmisDictionaryServices; - - private final Map> modelErrors = new HashMap<>(); - private final Set suggestableProperties = new HashSet<>(); - private final Set crossLocaleSearchDataTypes = new HashSet<>(); - private final Set crossLocaleSearchProperties = new HashSet<>(); - private final Set identifierProperties = new HashSet<>(); - private final ThreadPoolExecutor threadPool; - - public void close() { - threadPool.shutdown(); - } - - public AlfrescoSolrDataModel() - { - tenantService = new SingleTServiceImpl(); - - dictionaryDAO = new DictionaryDAOImpl(); - dictionaryDAO.setTenantService(tenantService); - - try - { - CompiledModelsCache compiledModelsCache = new CompiledModelsCache(); - compiledModelsCache.setDictionaryDAO(dictionaryDAO); - compiledModelsCache.setTenantService(tenantService); - compiledModelsCache.setRegistry(new DefaultAsynchronouslyRefreshedCacheRegistry()); - TrackerPoolFactory trackerPoolFactory = new DefaultTrackerPoolFactory(new Properties(), "_dictionary_", "_internal_"); - threadPool = trackerPoolFactory.create(); - compiledModelsCache.setThreadPoolExecutor(threadPool); - - dictionaryDAO.setDictionaryRegistryCache(compiledModelsCache); - dictionaryDAO.setResourceClassLoader(getResourceClassLoader()); - dictionaryDAO.init(); - } - catch (Exception e) - { - throw new AlfrescoRuntimeException("Failed to create dictionaryDAO ", e); - } - - namespaceDAO = dictionaryDAO; - - QNameFilter qnameFilter = getQNameFilter(); - dictionaryServices = AlfrescoClientDataModelServicesFactory.constructDictionaryServices(qnameFilter, dictionaryDAO); - DictionaryComponent dictionaryComponent = getDictionaryService(CMISStrictDictionaryService.DEFAULT); - dictionaryComponent.setMessageLookup(new StaticMessageLookup()); - - cmisDictionaryServices = AlfrescoClientDataModelServicesFactory.constructDictionaries(qnameFilter, namespaceDAO, dictionaryComponent, dictionaryDAO); - - Properties props = getCommonConfig(); - for (Object key : props.keySet()) - { - String stringKey = (String) key; - if (stringKey.startsWith("alfresco.suggestable.property.")) - { - QName qName = QName.createQName(props.getProperty(stringKey)); - suggestableProperties.add(qName); - } - else if (stringKey.startsWith("alfresco.cross.locale.property.")) - { - QName qName = QName.createQName(props.getProperty(stringKey)); - crossLocaleSearchProperties.add(qName); - } - else if (stringKey.startsWith("alfresco.cross.locale.datatype.")) - { - QName qName = QName.createQName(props.getProperty(stringKey)); - crossLocaleSearchDataTypes.add(qName); - } - else if (stringKey.startsWith("alfresco.identifier.property.")) - { - QName qName = QName.createQName(props.getProperty(stringKey)); - identifierProperties.add(qName); - } - } - - if(props.isEmpty()) - { - suggestableProperties.add(ContentModel.PROP_NAME); - suggestableProperties.add(ContentModel.PROP_TITLE); - suggestableProperties.add(ContentModel.PROP_DESCRIPTION); - suggestableProperties.add(ContentModel.PROP_CONTENT); - - crossLocaleSearchDataTypes.add(DataTypeDefinition.TEXT); - crossLocaleSearchDataTypes.add(DataTypeDefinition.CONTENT); - crossLocaleSearchDataTypes.add(DataTypeDefinition.MLTEXT); - - identifierProperties.add(ContentModel.PROP_CREATOR); - identifierProperties.add(ContentModel.PROP_MODIFIER); - identifierProperties.add(ContentModel.PROP_USERNAME); - identifierProperties.add(ContentModel.PROP_AUTHORITY_NAME); - } - } - - public static String getTenantId(String tenant) - { - if((tenant == null) || tenant.equals(TenantService.DEFAULT_DOMAIN)) - { - return DEFAULT_TENANT; - } - else - { - return tenant.replaceAll("!", "_-._"); - } - } - - /** - * Returns the ACL Document identifier in the following format <TENANT>!<ACLID>!ACL - * - * @param tenant the tenant code. - * @param aclId the acl identifer - * @return the ACL Document identifier (<TENANT>!<ACLID>!ACL) - */ - public static String getAclDocumentId(String tenant, Long aclId) - { - return getTenantId(tenant) + "!" + NumericEncoder.encode(aclId) + "!ACL"; - } - - /** - * Returns the ACL ChangeSet Document identifier in the following format TRACKER!CHANGESET!<ACL_CHANGESET_ID> - * - * @param aclChangeSetId the ACL changeset identifier. - * @return the ACL ChangeSet Document identifier (TRACKER!CHANGESET!<ACL_CHANGESET_ID>) - */ - public static String getAclChangeSetDocumentId(Long aclChangeSetId) - { - return "TRACKER" + "!" + CHANGE_SET + "!" + NumericEncoder.encode(aclChangeSetId); - } - - - /** - * Extracts the transaction identifier from the input document identifier. - * The "input document identifier" is the alphanumeric identifier used for Solr documents. - * - * @param documentId the document identifier. - * @return the numeric ACL changeset identifier. - */ - public static Long parseTransactionId(String documentId) - { - return parseIdFromDocumentId(documentId); - } - - private static Long parseIdFromDocumentId(String documentId) - { - String[] split = documentId.split("!"); - if (split.length > 0) - return NumericEncoder.decodeLong(split[split.length - 1]); - else - return null; - } - - /** - * Returns the Solr document identifier in the following format <TENANT>!<!<DBID> - * - * @param tenant the tenant code. - * @param dbid the DB identifier. - * @return the Solr document identifier in the following format <TENANT>!<!<DBID> - */ - public static String getNodeDocumentId(String tenant, Long dbid) - { - return getTenantId(tenant) + "!" + NumericEncoder.encode(dbid); - } - - /** - * Destructures a document identifier in the three compounding parts (tenant and dbid). - * - * @param id the document identifier. - * @return a {@link TenantDbId} instance containing the destructured identifier. - */ - public static TenantDbId decodeNodeDocumentId(String id) - { - TenantDbId ids = new TenantDbId(); - String[] split = id.split("!"); - if (split.length > 0) - ids.tenant = split[0]; - if (split.length > 1) - ids.dbId = NumericEncoder.decodeLong(split[1]); - return ids; - } - - /** - * Returns the Transaction Document identifier in the following format TRACKER!TX!<TX_ID> - * - * @param txId the transaction identifier. - * @return the Transaction Document identifier in the following format TRACKER!TX!<TX_ID> - */ - public static String getTransactionDocumentId(Long txId) - { - // TODO - check and encode for ":" - return "TRACKER" + - "!" + - TX + - "!" + - NumericEncoder.encode(txId); - } - - public String getAlfrescoPropertyFromSchemaField(String schemaField) - { - int index = schemaField.lastIndexOf("@{"); - if(index == -1) - { - return schemaField; - } - - String alfrescoQueryField = schemaField.substring(index+1); - QName qName = QName.createQName(alfrescoQueryField); - alfrescoQueryField = qName.toPrefixString(namespaceDAO); - - PropertyDefinition propertyDefinition = getPropertyDefinition(qName); - if((propertyDefinition == null)) - { - return alfrescoQueryField; - } - if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) - { - return alfrescoQueryField; - } - - DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); - if(dataTypeDefinition.getName().equals(DataTypeDefinition.CONTENT)) - { - if(schemaField.contains("__size@")) - { - return alfrescoQueryField + ".size"; - } - else if(schemaField.contains("__locale@")) - { - return alfrescoQueryField + ".locale"; - } - else if(schemaField.contains("__mimetype@")) - { - return alfrescoQueryField + ".mimetype"; - } - else if(schemaField.contains("__encoding@")) - { - return alfrescoQueryField + ".encoding"; - } - else if(schemaField.contains("__docid@")) - { - return alfrescoQueryField + ".docid"; - } - else if(schemaField.contains("__tr_ex@")) - { - return alfrescoQueryField + ".tr_ex"; - } - else if(schemaField.contains("__tr_time@")) - { - return alfrescoQueryField + ".tr_time"; - } - else if(schemaField.contains("__tr_status@")) - { - return alfrescoQueryField + ".tr_status"; - } - else - { - return alfrescoQueryField; - } - } - else - { - return alfrescoQueryField; - } - } - - public static AlfrescoSolrDataModel getInstance() - { - return INSTANCE; - } - - public NamespaceDAO getNamespaceDAO() - { - return namespaceDAO; - } - - /** - * TODO: Fix to load type filter/exclusions from somewhere sensible - * @return QNameFilter - */ - private QNameFilter getQNameFilter() - { - QNameFilter qnameFilter = null; - FileSystemXmlApplicationContext ctx = null; - - File resourceDirectory = getResourceDirectory(); - // If we ever need to filter out models in the future, then we must put a filter somewhere. - // Currently, we do not need to filter any models, so this filter does not exist. - File filterContext = new File(resourceDirectory, "opencmis-qnamefilter-context.xml"); - - if(!filterContext.exists()) - { - log.info("No type filter context found at " + filterContext.getAbsolutePath() + ", no type filtering"); - return qnameFilter; - } - - try - { - ctx = new FileSystemXmlApplicationContext(new String[] { "file:" + filterContext.getAbsolutePath() }, false); - ctx.setClassLoader(this.getClass().getClassLoader()); - ctx.refresh(); - qnameFilter = (QNameFilter)ctx.getBean("cmisTypeExclusions"); - if(qnameFilter == null) - { - log.warn("Unable to find type filter at " + filterContext.getAbsolutePath() + ", no type filtering"); - } - } - catch(BeansException e) - { - log.warn("Unable to parse type filter at " + filterContext.getAbsolutePath() + ", no type filtering"); - } - finally - { - if(ctx != null && ctx.getBeanFactory() != null && ctx.isActive()) - { - ctx.close(); - } - } - - return qnameFilter; - } - - public static Properties getCommonConfig() - { - File resourceDirectory = getResourceDirectory(); - // If we ever need to filter out models in the future, then we must put a filter somewhere. - // Currently, we do not need to filter any models, so this filter does not exist. - File propertiesFile = new File(resourceDirectory, SHARED_PROPERTIES); - - Properties props = new Properties(); - if(!propertiesFile.exists()) - { - log.info("No shared properties found at " + propertiesFile.getAbsolutePath()); - return props; - } - - try(InputStream is = new FileInputStream(propertiesFile)) - { - props.load(is); - } - catch (IOException e) - { - log.info("Failed to read shared properties at " + propertiesFile.getAbsolutePath()); - } - - return props; - } - - /** - * @return ClassLoader - */ - public ClassLoader getResourceClassLoader() - { - File f = getResourceDirectory(); - if (f.canRead() && f.isDirectory()) - { - URL[] urls = new URL[1]; - - try - { - URL url = f.toURI().normalize().toURL(); - urls[0] = url; - } - catch (MalformedURLException e) - { - throw new AlfrescoRuntimeException("Failed to add resources to classpath ", e); - } - - return URLClassLoader.newInstance(urls, this.getClass().getClassLoader()); - } - else - { - return this.getClass().getClassLoader(); - } - } - - public static File getResourceDirectory() - { - return new File(SolrResourceLoader.locateSolrHome().toFile(), "conf"); - } - - /** - * Gets a DictionaryService, if an Alternative dictionary is specified it tries to get that. - * It will attempt to get the DEFAULT dictionary service if null is specified or it can't find - * a dictionary with the name of "alternativeDictionary" - * @param alternativeDictionary - can be null; - * @return DictionaryService - */ - public DictionaryComponent getDictionaryService(String alternativeDictionary) - { - DictionaryComponent dictionaryComponent = null; - - if (alternativeDictionary != null && !alternativeDictionary.trim().isEmpty()) - { - dictionaryComponent = dictionaryServices.get(alternativeDictionary); - } - - if (dictionaryComponent == null) - { - dictionaryComponent = dictionaryServices.get(CMISStrictDictionaryService.DEFAULT); - } - return dictionaryComponent; - } - - public IndexedField getIndexedFieldForSpecializedPropertyMetadata(QName propertyQName, SpecializedFieldType type) - { - IndexedField indexedField = new IndexedField(); - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - if((propertyDefinition == null)) - { - return indexedField; - } - if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) - { - return indexedField; - } - - DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); - if(dataTypeDefinition.getName().equals(DataTypeDefinition.CONTENT)) - { - StringBuilder builder = new StringBuilder(); - builder.append(dataTypeDefinition.getName().getLocalName()); - builder.append('@'); - // TODO wher we support multi value propertis correctly .... builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); - builder.append('s'); - builder.append("_"); - builder.append('_'); - switch (type) - { - case CONTENT_DOCID: - builder.append("docid"); - break; - case CONTENT_ENCODING: - builder.append("encoding"); - break; - case CONTENT_LOCALE: - builder.append("locale"); - break; - case CONTENT_MIMETYPE: - builder.append("mimetype"); - break; - case CONTENT_SIZE: - builder.append("size"); - break; - case TRANSFORMATION_EXCEPTION: - builder.append("tr_ex"); - break; - case TRANSFORMATION_STATUS: - builder.append("tr_status"); - break; - case TRANSFORMATION_TIME: - builder.append("tr_time"); - break; - default: - break; - } - - builder.append('@'); - builder.append(propertyQName); - indexedField.addField(builder.toString(), false, false); - } - else if (isDateOrDatetime(dataTypeDefinition)) - { - String dateDerivedSuffix = getDateDerivedSuffix(type); - if (dateDerivedSuffix != null) - { - indexedField.addField(getDateDerivedField(propertyQName, dateDerivedSuffix), false, true); - } - } - return indexedField; - - } - - - public IndexedField getQueryableFields(QName propertyQName, SpecializedFieldType type, FieldUse fieldUse) - { - if(type != null) - { - return getIndexedFieldForSpecializedPropertyMetadata(propertyQName, type); - } - - IndexedField indexedField = new IndexedField(); - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - if((propertyDefinition == null)) - { - indexedField.addField("_dummy_", false, false); - return indexedField; - } - if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) - { - indexedField.addField("_dummy_", false, false); - return indexedField; - } - - if(isTextField(propertyDefinition)) - { - switch(fieldUse) - { - case COMPLETION: - addCompletionFields(propertyDefinition, indexedField); - break; - case FACET: - addFacetSearchFields(propertyDefinition, indexedField); - break; - case FTS: - addFullTextSearchFields(propertyDefinition, indexedField); - break; - case ID: - addIdentifierSearchFields(propertyDefinition, indexedField); - break; - case EXACT: - addExactSearchFields(propertyDefinition, indexedField); - break; - case MULTI_FACET: - addMultiSearchFields(propertyDefinition, indexedField); - break; - case SORT: - addSortSearchFields(propertyDefinition, indexedField); - break; - case STATS: - addStatsSearchFields(propertyDefinition, indexedField); - break; - case SUGGESTION: - if(isSuggestable(propertyQName)) - { - indexedField.addField("suggest", false, false); - } - addCompletionFields(propertyDefinition, indexedField); - break; - case HIGHLIGHT: - addHighlightSearchFields(propertyDefinition, indexedField); - break; - } - } - else - { - indexedField.addField(getFieldForNonText(propertyDefinition), false, false); - } - return indexedField; - } - - /* - * Adds best completion fields in order of preference - */ - private void addCompletionFields(PropertyDefinition propertyDefinition, IndexedField indexedField) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - else if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE)) - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - else if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE)) - { - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - } - - /* - * Adds best fts fields in order of preference - */ - - private void addFullTextSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - if (((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - && !isIdentifierTextProperty(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - } - else - { - indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - } - - private void addHighlightSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - QName propertyName = propertyDefinition.getName(); - QName propertyDataTypeQName = propertyDefinition.getDataType().getName(); - String fieldName; - - if(propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) - { - fieldName = getStoredMLTextField(propertyName); - } - else if(propertyDataTypeQName.equals(DataTypeDefinition.CONTENT)) - { - fieldName = getStoredContentField(propertyName); - } - else - { - fieldName = getStoredTextField(propertyName); - } - - indexedField.addField(fieldName, false, false); - } - - /* - * If the untokenised version is available, go for it. - * If not it takes the tokenised versions available. - */ - private void addIdentifierSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - - indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - } - } - - /* - * If only the untokenised version is available, go for it. - * If not it takes the tokenised cross locale version. - */ - private void addExactSearchFields(PropertyDefinition propertyDefinition, IndexedField indexedField) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || !(propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - - indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } else{ - throw new UnsupportedOperationException("Exact Term search is not supported unless you configure the field <"+propertyDefinition.getName()+"> for cross locale search"); - } - } - } - - /* - * Adds best identifier fields in order of preference - */ - private void addFacetSearchFields(PropertyDefinition propertyDefinition, IndexedField indexedField) - { - if(propertyDefinition.getDataType().getName().equals(DataTypeDefinition.TEXT)) - { - if (!isIdentifierTextProperty(propertyDefinition.getName())) - { - if(propertyDefinition.getFacetable() == Facetable.TRUE) - { - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - } - } - - - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH) - || isIdentifierTextProperty(propertyDefinition.getName())) - { - - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - } - - private void addMultiSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH) - || isIdentifierTextProperty(propertyDefinition.getName())) - { - - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - } - - private void addStatsSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - addFacetSearchFields(propertyDefinition, indexedField); - } - - private void addSortSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) - { - // Can only order on single valued fields - DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); - if(dataTypeDefinition.getName().equals(DataTypeDefinition.TEXT)) - { - if(!propertyDefinition.isMultiValued()) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); - } - else if (isIdentifierTextProperty(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - } - } - - if(dataTypeDefinition.getName().equals(DataTypeDefinition.MLTEXT)) - { - if(!propertyDefinition.isMultiValued()) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); - } - else - { - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - else - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); - } - } - } - } - } - - - public String getStoredTextField(QName propertyQName) - { - return getStoredTextField(propertyQName, null); - } - - public String getStoredTextField(QName propertyQName, String suffix) - { - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - - StringBuilder sb = new StringBuilder(); - sb.append("text@" + (propertyDefinition.isMultiValued()? "m" : "s") + "_stored_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || - isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); - - sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || - crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || - isIdentifierTextProperty(propertyDefinition.getName())) && !propertyDefinition.isMultiValued()? "s" : "_"); - - sb.append(isSuggestable(propertyQName)? "s": "_"); - - sb.append("@"); - sb.append(propertyDefinition.getName().toString()); - - if (suffix != null) - { - sb.append(suffix); - } - - return sb.toString(); - - } - - public String getStoredMLTextField(QName propertyQName) - { - return getStoredMLTextField(propertyQName, null); - } - - public String getStoredMLTextField(QName propertyQName, String suffix) - { - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - - StringBuilder sb = new StringBuilder(); - sb.append("mltext@m_stored_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || - isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); - - sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || - crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); - - sb.append("_"); - - sb.append(isSuggestable(propertyQName)? "s": "_"); - - sb.append("@"); - sb.append(propertyDefinition.getName().toString()); - - if (suffix != null) - { - sb.append(suffix); - } - - return sb.toString(); - - } - - public String getStoredContentField(QName propertyQName) - { - return getStoredContentField(propertyQName, null); - } - - public String getStoredContentField(QName propertyQName, String suffix) - { - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - - StringBuilder sb = new StringBuilder(); - sb.append("content@s_stored_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); - - sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || - propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || - isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); - - sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || - crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); - - - sb.append("_"); - sb.append(isSuggestable(propertyQName)? "s": "_"); - - sb.append("@"); - sb.append(propertyDefinition.getName().toString()); - - if (suffix != null) - { - sb.append(suffix); - } - - return sb.toString(); - - } - - public String getDateDerivedField(QName propertyQName, String suffix) - { - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - return "part@sd@" + propertyDefinition.getName().toString() + suffix; - } - - public String getDateDerivedSuffix(SpecializedFieldType type) - { - switch (type) - { - case UNIT_OF_TIME_SECOND: - return UNIT_OF_TIME_SECOND_FIELD_SUFFIX; - case UNIT_OF_TIME_MINUTE: - return UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; - case UNIT_OF_TIME_HOUR: - return UNIT_OF_TIME_HOUR_FIELD_SUFFIX; - case UNIT_OF_TIME_DAY: - return UNIT_OF_TIME_DAY_FIELD_SUFFIX; - case UNIT_OF_TIME_DAY_OF_WEEK: - return UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX; - case UNIT_OF_TIME_DAY_OF_YEAR: - return UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX; - case UNIT_OF_TIME_MONTH: - return UNIT_OF_TIME_MONTH_FIELD_SUFFIX; - case UNIT_OF_TIME_QUARTER: - return UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; - case UNIT_OF_TIME_YEAR: - return UNIT_OF_TIME_YEAR_FIELD_SUFFIX; - default: - return null; - } - } - - - /** - * Get all the field names into which we must copy the source data - * - * @param propertyQName QName - * @return IndexedField - */ - public IndexedField getIndexedFieldNamesForProperty(QName propertyQName) - { - // TODO: Cache and throw on model refresh - - IndexedField indexedField = new IndexedField(); - PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); - if((propertyDefinition == null)) - { - return indexedField; - } - if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) - { - return indexedField; - } - - DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); - if(isTextField(propertyDefinition)) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); - if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) - { - indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); - } - } - - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH - || isIdentifierTextProperty(propertyDefinition.getName()))) - { - indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - - if(dataTypeDefinition.getName().equals(DataTypeDefinition.TEXT)) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - if(!propertyDefinition.isMultiValued()) - { - indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); - } - } - else if (!isIdentifierTextProperty(propertyDefinition.getName())) - { - if(propertyDefinition.getFacetable() == Facetable.TRUE) - { - indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); - } - } - } - - if(dataTypeDefinition.getName().equals(DataTypeDefinition.MLTEXT)) - { - if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) - || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) - { - if(!propertyDefinition.isMultiValued()) - { - indexedField.addField(getFieldForText(true, false, true, propertyDefinition), true, true); - } - } - } - - if(isSuggestable(propertyQName)) - { - indexedField.addField("suggest_@" + propertyDefinition.getName().toString(), false, false); - } - } - else - { - indexedField.addField(getFieldForNonText(propertyDefinition), false, false); - } - - return indexedField; - } - - private boolean isIdentifierTextProperty(QName propertyQName) - { - return identifierProperties.contains(propertyQName); - } - - public boolean isTextField(PropertyDefinition propertyDefinition) - { - return ofNullable(propertyDefinition) - .map(PropertyDefinition::getDataType) - .map(DataTypeDefinition::getName) - .map(name -> - name.equals(DataTypeDefinition.MLTEXT) - || - name.equals(DataTypeDefinition.CONTENT) - || - name.equals(DataTypeDefinition.TEXT)) - .orElse(false); - } - - private boolean isSuggestable(QName propertyQName) - { - if(propertyQName == null) - { - return false; - } - return suggestableProperties.contains(propertyQName); - } - - private boolean hasDocValues(PropertyDefinition propertyDefinition) - { - - if(isTextField(propertyDefinition)) - { - // We only call this if text is untokenised and localised - return propertyDefinition.getFacetable() != Facetable.FALSE; - } - else - { - if(propertyDefinition.getFacetable() == Facetable.FALSE) - { - return false; - } - else if(propertyDefinition.getFacetable() == Facetable.TRUE) - { - return true; - } - else - { - if(propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) - { - return false; - } - else - { - return isPrimitive(propertyDefinition.getDataType()); - } - } - } - } - - public String getFieldForNonText(PropertyDefinition propertyDefinition) - { - StringBuilder builder = new StringBuilder(); - QName qName = propertyDefinition.getDataType().getName(); - builder.append(qName.getLocalName()); - builder.append("@"); - builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); - builder.append(hasDocValues(propertyDefinition) ? "d" : "_"); - builder.append("@"); - builder.append(propertyDefinition.getName().toString()); - return builder.toString(); - } - - private String getFieldForText(boolean localised, boolean tokenised, boolean sort, PropertyDefinition propertyDefinition) - { - StringBuilder builder = new StringBuilder(); - QName qName = propertyDefinition.getDataType().getName(); - builder.append(qName.getLocalName()); - builder.append("@"); - QName propertyDataTypeQName = propertyDefinition.getDataType().getName(); - if(propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) - { - builder.append('m'); - } - else if(propertyDataTypeQName.equals(DataTypeDefinition.CONTENT)) - { - builder.append('s'); - } - else - { - builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); - } - if(sort || localised || tokenised || propertyDataTypeQName.equals(DataTypeDefinition.CONTENT) || propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) - { - builder.append('_'); - } - else - { - builder.append(hasDocValues(propertyDefinition) ? "d" : "_"); - } - builder.append('_'); - if(!sort) - { - builder.append(localised ? "l" : "_"); - builder.append(tokenised ? "t" : "_"); - } - else - { - builder.append("sort"); - } - builder.append("@"); - builder.append(propertyDefinition.getName().toString()); - return builder.toString(); - } - - public PropertyDefinition getPropertyDefinition(QName propertyQName) - { - return getDictionaryService(CMISStrictDictionaryService.DEFAULT).getProperty(propertyQName); - } - - public boolean putModel(M2Model model) - { - modelErrors.remove(model.getName()); - dictionaryDAO.putModelIgnoringConstraints(model); - return true; - } - - public void removeModel(QName modelQName) - { - modelErrors.remove(getM2Model(modelQName).getName()); - dictionaryDAO.removeModel(modelQName); - } - - M2Model getM2Model(QName modelQName) - { - return dictionaryDAO.getCompiledModel(modelQName).getM2Model(); - } - - public void afterInitModels() - { - for (CMISAbstractDictionaryService cds : cmisDictionaryServices.values()) - { - cds.afterDictionaryInit(); - } - } - - public org.alfresco.repo.search.impl.querymodel.Query parseCMISQueryToAlfrescoAbstractQuery(CMISQueryMode mode, SearchParameters searchParameters, - SolrQueryRequest req, String alternativeDictionary, CmisVersion cmisVersion) - { - // convert search parameters to cmis query options - // TODO: how to handle store ref - CMISQueryOptions options = new CMISQueryOptions(searchParameters.getQuery(), StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); - options.setQueryMode(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS); - options.setDefaultFieldName(searchParameters.getDefaultFieldName()); - // TODO: options.setDefaultFTSConnective() - // TODO: options.setDefaultFTSFieldConnective() - options.setIncludeInTransactionData(!searchParameters.excludeDataInTheCurrentTransaction()); - options.setLocales(searchParameters.getLocales()); - options.setMlAnalaysisMode(searchParameters.getMlAnalaysisMode()); - options.setQueryParameterDefinitions(searchParameters.getQueryParameterDefinitions()); - for(String name : searchParameters.getQueryTemplates().keySet()) - { - String template = searchParameters.getQueryTemplates().get(name); - options.addQueryTemplate(name, template); - } - - // parse cmis syntax - CapabilityJoin joinSupport = (mode == CMISQueryMode.CMS_STRICT) ? CapabilityJoin.NONE : CapabilityJoin.INNERANDOUTER; - CmisFunctionEvaluationContext functionContext = getCMISFunctionEvaluationContext(mode, cmisVersion, alternativeDictionary); - - CMISDictionaryService cmisDictionary = getCMISDictionary(alternativeDictionary, cmisVersion); - - CMISQueryParser parser = new CMISQueryParser(options, cmisDictionary, joinSupport); - org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = parser.parse(new LuceneQueryModelFactory(), functionContext); - - if (queryModelQuery.getSource() != null) - { - List> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext); - if (selectorGroups.size() == 0) - { - throw new UnsupportedOperationException("No selectors"); - } - } - return queryModelQuery; - } - - public CmisFunctionEvaluationContext getCMISFunctionEvaluationContext(CMISQueryMode mode, CmisVersion cmisVersion, String alternativeDictionary) - { - BaseTypeId[] validScopes = (mode == CMISQueryMode.CMS_STRICT) ? CmisFunctionEvaluationContext.STRICT_SCOPES : CmisFunctionEvaluationContext.ALFRESCO_SCOPES; - CmisFunctionEvaluationContext functionContext = new CmisFunctionEvaluationContext(); - functionContext.setCmisDictionaryService(getCMISDictionary(alternativeDictionary, cmisVersion)); - functionContext.setValidScopes(validScopes); - return functionContext; - } - - /** - * Gets the CMISDictionaryService, if an Alternative dictionary is specified it tries to get that. - * It will attempt to get the DEFAULT dictionary service if null is specified or it can't find - * a dictionary with the name of "alternativeDictionary" - * - * @param alternativeDictionary - can be null; - * @return CMISDictionaryService - */ - public CMISDictionaryService getCMISDictionary(String alternativeDictionary, CmisVersion cmisVersion) - { - CMISDictionaryService cmisDictionary = null; - - if (alternativeDictionary != null && !alternativeDictionary.trim().isEmpty()) - { - DictionaryKey key = new DictionaryKey(cmisVersion, alternativeDictionary); - cmisDictionary = cmisDictionaryServices.get(key); - } - - if (cmisDictionary == null) - { - DictionaryKey key = new DictionaryKey(cmisVersion, CMISStrictDictionaryService.DEFAULT); - cmisDictionary = cmisDictionaryServices.get(key); - } - return cmisDictionary; - } - - /** - * Returns the Alfresco models associated with the current dictionary. - * - * @return the Alfresco models associated with the current dictionary. - */ - public List getAlfrescoModels() - { - return dictionaryDAO.getModels().stream() - .map(qname -> { - M2Model m2Model = dictionaryDAO.getCompiledModel(qname).getM2Model(); - return new AlfrescoModel( - m2Model, - getDictionaryService( - CMISStrictDictionaryService.DEFAULT).getModel(qname).getChecksum(ModelDefinition.XMLBindingType.DEFAULT));}) - .collect(Collectors.toList()); - } - - Map> getModelErrors() - { - return modelErrors; - } - - public static class IndexedField - { - private final List fields = new LinkedList<>(); - - public List getFields() - { - return unmodifiableList(fields); - } - - public void addField(String prefix, boolean localised, boolean sort) - { - fields.add(new FieldInstance(prefix, localised, sort)); - } - } - - public static class FieldInstance - { - final String field; - final boolean localised; - final boolean sort; - - public FieldInstance(String field, boolean localised, boolean sort) - { - this.field = field; - this.localised = localised; - this.sort = sort; - } - - public String getField() - { - return field; - } - - public boolean isLocalised() - { - return localised; - } - - public boolean isSort() - { - return sort; - } - } - - public Query getCMISQuery(CMISQueryMode mode, Pair searchParametersAndFilter, SolrQueryRequest req, org.alfresco.repo.search.impl.querymodel.Query queryModelQuery, CmisVersion cmisVersion, String alternativeDictionary) throws ParseException - { - SearchParameters searchParameters = searchParametersAndFilter.getFirst(); - Boolean isFilter = searchParametersAndFilter.getSecond(); - - CmisFunctionEvaluationContext functionContext = getCMISFunctionEvaluationContext(mode, cmisVersion, alternativeDictionary); - - Set selectorGroup = queryModelQuery.getSource().getSelectorGroups(functionContext).get(0); - - QueryBuilderContext luceneContext = getLuceneQueryBuilderContext(searchParameters, req, alternativeDictionary, FTSQueryParser.RerankPhase.SINGLE_PASS); - @SuppressWarnings("unchecked") - LuceneQueryBuilder builder = (LuceneQueryBuilder) queryModelQuery; - org.apache.lucene.search.Query luceneQuery = builder.buildQuery(selectorGroup, luceneContext, functionContext); - - return new ContextAwareQuery(luceneQuery, Boolean.TRUE.equals(isFilter) ? null : searchParameters); - } - - public QueryBuilderContext getLuceneQueryBuilderContext(SearchParameters searchParameters, SolrQueryRequest req, String alternativeDictionary, FTSQueryParser.RerankPhase rerankPhase) - { - return new Lucene4QueryBuilderContextSolrImpl( - getDictionaryService(alternativeDictionary), - namespaceDAO, - tenantService, - searchParameters, - MLAnalysisMode.EXACT_LANGUAGE, - req, - this, - rerankPhase); - } - - public Solr4QueryParser getLuceneQueryParser(SearchParameters searchParameters, SolrQueryRequest req, FTSQueryParser.RerankPhase rerankPhase) - { - Analyzer analyzer = req.getSchema().getQueryAnalyzer(); - Solr4QueryParser parser = new Solr4QueryParser(req, Version.LATEST, searchParameters.getDefaultFieldName(), analyzer, rerankPhase); - parser.setNamespacePrefixResolver(namespaceDAO); - parser.setDictionaryService(getDictionaryService(CMISStrictDictionaryService.DEFAULT)); - parser.setTenantService(tenantService); - parser.setSearchParameters(searchParameters); - parser.setAllowLeadingWildcard(true); - - Properties props = new CoreDescriptorDecorator(req.getCore().getCoreDescriptor()).getProperties(); - int topTermSpanRewriteLimit = Integer.parseInt(props.getProperty("alfresco.topTermSpanRewriteLimit", "1000")); - parser.setTopTermSpanRewriteLimit(topTermSpanRewriteLimit); - - return parser; - } - - public Query getFTSQuery(Pair searchParametersAndFilter, SolrQueryRequest req, FTSQueryParser.RerankPhase rerankPhase) throws ParseException - { - - SearchParameters searchParameters = searchParametersAndFilter.getFirst(); - Boolean isFilter = searchParametersAndFilter.getSecond(); - - QueryModelFactory factory = new LuceneQueryModelFactory(); - AlfrescoFunctionEvaluationContext functionContext = new AlfrescoSolr4FunctionEvaluationContext(namespaceDAO, getDictionaryService(CMISStrictDictionaryService.DEFAULT), NamespaceService.CONTENT_MODEL_1_0_URI, req.getSchema()); - - FTSParser.Mode mode; - - if (searchParameters.getDefaultFTSOperator() == org.alfresco.service.cmr.search.SearchParameters.Operator.AND) - { - mode = FTSParser.Mode.DEFAULT_CONJUNCTION; - } - else - { - mode = FTSParser.Mode.DEFAULT_DISJUNCTION; - } - - Constraint constraint = FTSQueryParser.buildFTS(searchParameters.getQuery(), factory, functionContext, null, null, mode, - searchParameters.getDefaultFTSOperator() == org.alfresco.service.cmr.search.SearchParameters.Operator.OR ? Connective.OR : Connective.AND, - searchParameters.getQueryTemplates(), searchParameters.getDefaultFieldName(), rerankPhase); - org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = factory.createQuery(null, null, constraint, new ArrayList<>()); - - @SuppressWarnings("unchecked") - LuceneQueryBuilder builder = (LuceneQueryBuilder) queryModelQuery; - - QueryBuilderContext luceneContext = getLuceneQueryBuilderContext(searchParameters, req, CMISStrictDictionaryService.DEFAULT, rerankPhase); - - Set selectorGroup = null; - if (queryModelQuery.getSource() != null) - { - List> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext); - - if (selectorGroups.size() == 0) - { - throw new UnsupportedOperationException("No selectors"); - } - - if (selectorGroups.size() > 1) - { - throw new UnsupportedOperationException("Advanced join is not supported"); - } - - selectorGroup = selectorGroups.get(0); - } - Query luceneQuery = builder.buildQuery(selectorGroup, luceneContext, functionContext); - // query needs some search parameters fro correct caching .... - - return new ContextAwareQuery(luceneQuery, Boolean.TRUE.equals(isFilter) ? null : searchParameters); - } - - private PropertyDefinition getPropertyDefinition(String identifier) - { - return QueryParserUtils.matchPropertyDefinition(NamespaceService.CONTENT_MODEL_1_0_URI, - getNamespaceDAO(), - getDictionaryService(CMISStrictDictionaryService.DEFAULT), - identifier); - } - - public String mapProperty(String potentialProperty, FieldUse fieldUse, SolrQueryRequest req) - { - return mapProperty(potentialProperty, fieldUse, req, 0); - } - - /** - * - * return the stored field associated to potentialProperty parameter - */ - public String mapStoredProperty(String potentialProperty, SolrQueryRequest req) - { - if(potentialProperty.equals("asc") || potentialProperty.equals("desc") || potentialProperty.equals("_docid_")) - { - return potentialProperty; - } - - if(potentialProperty.equalsIgnoreCase("score") || potentialProperty.equalsIgnoreCase("SEARCH_SCORE")) - { - return "score"; - } - - AlfrescoFunctionEvaluationContext functionContext = - new AlfrescoSolr4FunctionEvaluationContext( - getNamespaceDAO(), - getDictionaryService(CMISStrictDictionaryService.DEFAULT), - NamespaceService.CONTENT_MODEL_1_0_URI, - req.getSchema()); - - - Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(potentialProperty); - String luceneField = functionContext.getLuceneFieldName(fieldNameAndEnding.getFirst()); - - PropertyDefinition propertyDef = getPropertyDefinition(fieldNameAndEnding.getFirst()); - //Retry scan using luceneField. - if(propertyDef == null) - { - if(luceneField.contains("@")) - { - int index = luceneField.lastIndexOf("@"); - propertyDef = getPropertyDefinition(luceneField.substring(index +1)); - } - } - - if (propertyDef == null || propertyDef.getName() == null) - { - return mapNonPropertyFields(luceneField); - } - - if (isDateOrDatetime(propertyDef.getDataType()) && isDerivedDateField(fieldNameAndEnding.getSecond())) - { - return getDateDerivedField(propertyDef.getName(), fieldNameAndEnding.getSecond()); - } - else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) - { - return getStoredTextField(propertyDef.getName(), fieldNameAndEnding.getSecond()); - } - else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) - { - return getStoredMLTextField(propertyDef.getName(), fieldNameAndEnding.getSecond()); - } - else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) - { - return getStoredContentField(propertyDef.getName(), fieldNameAndEnding.getSecond()); - } - else - { - return mapAlfrescoField(FieldUse.FTS, 0, fieldNameAndEnding, luceneField, propertyDef) - + fieldNameAndEnding.getSecond(); - } - } - - public boolean isDerivedDateField(String suffix) - { - return DATE_PART_SUFFIXES.contains(suffix); - } - - public String mapProperty(String potentialProperty, FieldUse fieldUse, SolrQueryRequest req, int position) - { - if(potentialProperty.equals("asc") || potentialProperty.equals("desc") || potentialProperty.equals("_docid_")) - { - return potentialProperty; - } - - if(potentialProperty.equalsIgnoreCase("score") || potentialProperty.equalsIgnoreCase("SEARCH_SCORE")) - { - return "score"; - } - - if(req.getSchema().getFieldOrNull(potentialProperty) != null) - { - return mapNonPropertyFields(potentialProperty); - } - - AlfrescoFunctionEvaluationContext functionContext = - new AlfrescoSolr4FunctionEvaluationContext( - getNamespaceDAO(), - getDictionaryService(CMISStrictDictionaryService.DEFAULT), - NamespaceService.CONTENT_MODEL_1_0_URI, - req.getSchema()); - - Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(potentialProperty); - String luceneField = functionContext.getLuceneFieldName(fieldNameAndEnding.getFirst()); - - PropertyDefinition propertyDef = getPropertyDefinition(fieldNameAndEnding.getFirst()); - //Retry scan using luceneField. - if(propertyDef == null) - { - if(luceneField.contains("@")) - { - int index = luceneField.lastIndexOf("@"); - propertyDef = getPropertyDefinition(luceneField.substring(index +1)); - } - } - String solrSortField; - solrSortField = mapAlfrescoField(fieldUse, position, fieldNameAndEnding, luceneField, propertyDef); - return solrSortField; - } - - public String mapNonPropertyFields(String queryField) - { - switch(queryField) - { - case "ID": - return "LID"; - case "EXACTTYPE": - return "TYPE"; - default: - return queryField; - - } - } - - /** - * @param ending String - * @return SpecializedFieldType - */ - public SpecializedFieldType getTextField(String ending) - { - switch(ending) - { - case FIELD_MIMETYPE_SUFFIX: - return SpecializedFieldType.CONTENT_MIMETYPE; - case FIELD_SIZE_SUFFIX: - return SpecializedFieldType.CONTENT_SIZE; - case FIELD_LOCALE_SUFFIX: - return SpecializedFieldType.CONTENT_LOCALE; - case FIELD_ENCODING_SUFFIX: - return SpecializedFieldType.CONTENT_ENCODING; - case UNIT_OF_TIME_SECOND_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_SECOND; - case UNIT_OF_TIME_MINUTE_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_MINUTE; - case UNIT_OF_TIME_HOUR_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_HOUR; - case UNIT_OF_TIME_DAY_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_DAY; - case UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_DAY_OF_WEEK; - case UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_DAY_OF_YEAR; - case UNIT_OF_TIME_MONTH_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_MONTH; - case UNIT_OF_TIME_QUARTER_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_QUARTER; - case UNIT_OF_TIME_YEAR_FIELD_SUFFIX: - return SpecializedFieldType.UNIT_OF_TIME_YEAR; - case FIELD_TRANSFORMATION_STATUS_SUFFIX: - case FIELD_TRANSFORMATION_TIME_SUFFIX: - case FIELD_TRANSFORMATION_EXCEPTION_SUFFIX: - default: - return null; - } - } - - public void setCMDefaultUri() - { - if(getNamespaceDAO().getURIs().contains(NamespaceService.CONTENT_MODEL_1_0_URI)) - { - getNamespaceDAO().addPrefix("", NamespaceService.CONTENT_MODEL_1_0_URI); - } - } - - /** - * Returns the prefix used for denoting a field which is meant to represent a constituent part of a - * date or datetime (e.g. year, month, second, minute). - * - * @param sourceFieldName the date/datetime source field name. - * @return the prefix that can be used for denoting a date or datetime part - */ - public String destructuredDateTimePartFieldNamePrefix(String sourceFieldName) { - // source field name example: datetime@sd@{http://www.alfresco.org/model/content/1.0}created - // prefix: datetime - String prefix = sourceFieldName.substring(0, sourceFieldName.indexOf("@")); - - // prefix, docValues disabled option: datetime@s_@ - String sourceFieldNamePrefixWithoutDocValues = prefix + SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER; - - // prefix, docValues enabled option: datetime@sd@ - String sourceFieldNamePrefixWithDocValues = prefix + SINGLE_VALUE_WITH_DOC_VALUES_MARKER; - - return sourceFieldName.replace(sourceFieldNamePrefixWithoutDocValues, PART_FIELDNAME_PREFIX) - .replace(sourceFieldNamePrefixWithDocValues, PART_FIELDNAME_PREFIX); - } - - private boolean isDateOrDatetime(DataTypeDefinition dataType) - { - return dataType.getName().equals(DataTypeDefinition.DATE) || - dataType.getName().equals(DataTypeDefinition.DATETIME); - } - - private boolean isPrimitive(DataTypeDefinition dataType) - { - QName name = dataType.getName(); - return name.equals(DataTypeDefinition.INT) - || name.equals(DataTypeDefinition.LONG) - || name.equals(DataTypeDefinition.FLOAT) - || name.equals(DataTypeDefinition.DOUBLE) - || isDateOrDatetime(dataType) - || name.equals(DataTypeDefinition.BOOLEAN) - || name.equals(DataTypeDefinition.CATEGORY) - || name.equals(DataTypeDefinition.NODE_REF); - } - - private String mapAlfrescoField(FieldUse fieldUse, int position, Pair fieldNameAndEnding, String luceneField, PropertyDefinition propertyDef) - { - String solrSortField; - if(propertyDef != null) - { - - IndexedField fields = AlfrescoSolrDataModel.getInstance().getQueryableFields(propertyDef.getName(), getTextField(fieldNameAndEnding.getSecond()), fieldUse); - if(fields.getFields().size() > 0) - { - if(fields.getFields().size() > position) - { - solrSortField = fields.getFields().get(position).getField(); - } - else - { - solrSortField = fields.getFields().get(0).getField(); - } - } - else - { - solrSortField = mapNonPropertyFields(luceneField); - } - } - else - { - solrSortField = mapNonPropertyFields(luceneField); - } - return solrSortField; - } - - } +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import static java.util.Collections.unmodifiableList; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.stream.Collectors; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.opencmis.dictionary.CMISAbstractDictionaryService; +import org.alfresco.opencmis.dictionary.CMISDictionaryService; +import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; +import org.alfresco.opencmis.dictionary.QNameFilter; +import org.alfresco.opencmis.search.CMISQueryOptions; +import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; +import org.alfresco.opencmis.search.CMISQueryParser; +import org.alfresco.opencmis.search.CmisFunctionEvaluationContext; +import org.alfresco.repo.dictionary.CompiledModelsCache; +import org.alfresco.repo.dictionary.DictionaryComponent; +import org.alfresco.repo.dictionary.DictionaryDAOImpl; +import org.alfresco.repo.dictionary.Facetable; +import org.alfresco.repo.dictionary.IndexTokenisationMode; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.M2ModelDiff; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.repo.search.impl.QueryParserUtils; +import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; +import org.alfresco.repo.search.impl.parsers.FTSParser; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective; +import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilder; +import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.impl.lucene.QueryBuilderContext; +import org.alfresco.repo.tenant.SingleTServiceImpl; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.AlfrescoClientDataModelServicesFactory.DictionaryKey; +import org.alfresco.solr.client.AlfrescoModel; +import org.alfresco.solr.query.Lucene4QueryBuilderContextSolrImpl; +import org.alfresco.solr.query.Solr4QueryParser; +import org.alfresco.solr.tracker.pool.DefaultTrackerPoolFactory; +import org.alfresco.solr.tracker.pool.TrackerPoolFactory; +import org.alfresco.util.NumericEncoder; +import org.alfresco.util.Pair; +import org.alfresco.util.cache.DefaultAsynchronouslyRefreshedCacheRegistry; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.util.Version; +import org.apache.solr.core.CoreDescriptorDecorator; +import org.apache.solr.core.SolrResourceLoader; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.support.FileSystemXmlApplicationContext; + +import static java.util.Optional.ofNullable; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_HOUR_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MONTH_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_SECOND_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_YEAR_FIELD_SUFFIX; + +/** + * @author Andy + * + */ +public class AlfrescoSolrDataModel implements QueryConstants +{ + public static class TenantDbId + { + public String tenant; + public Long dbId; + + public Map optionalBag = new HashMap<>(); + + public void setProperty(String name, Object value) + { + optionalBag.put(name, value); + } + } + + public enum FieldUse + { + FTS, // Term/Phrase/Range/Fuzzy/Prefix/Proximity + ID, // ExactRange - Comparison, In, Upper, Lower, Wildcard + EXACT, // Exact + FACET, // Field, Range, Query + MULTI_FACET, // Text fields will require cross language support to avoid tokenisation for facets + STATS, // Stats + SORT, // Locale + SUGGESTION, + COMPLETION, + HIGHLIGHT + } + + public enum SpecializedFieldType + { + CONTENT_DOCID, + CONTENT_SIZE, + CONTENT_LOCALE, + CONTENT_MIMETYPE, + CONTENT_ENCODING, + TRANSFORMATION_STATUS, + TRANSFORMATION_TIME, + TRANSFORMATION_EXCEPTION, + UNIT_OF_TIME_SECOND, + UNIT_OF_TIME_MINUTE, + UNIT_OF_TIME_HOUR, + UNIT_OF_TIME_DAY, + UNIT_OF_TIME_DAY_OF_WEEK, + UNIT_OF_TIME_DAY_OF_YEAR, + UNIT_OF_TIME_MONTH, + UNIT_OF_TIME_QUARTER, + UNIT_OF_TIME_YEAR + } + + public static final String CONTENT_S_LOCALE_PREFIX = "content@s__locale@"; + + static final String PART_FIELDNAME_PREFIX = "part@sd@"; + static final Set DATE_PART_SUFFIXES = + Set.of(UNIT_OF_TIME_YEAR_FIELD_SUFFIX, + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX, + UNIT_OF_TIME_MONTH_FIELD_SUFFIX, + UNIT_OF_TIME_DAY_FIELD_SUFFIX, + UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX, + UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX, + UNIT_OF_TIME_HOUR_FIELD_SUFFIX, + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX, + UNIT_OF_TIME_SECOND_FIELD_SUFFIX); + /** + * Infix used for denoting a primitive single valued field with no doc values enabled + */ + private static final String SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER = "@s_@"; + + /** + * Infix used for denoting a primitive single valued field with no doc values enabled + */ + private static final String SINGLE_VALUE_WITH_DOC_VALUES_MARKER = "@sd@"; + + static final String SHARED_PROPERTIES = "shared.properties"; + + protected final static Logger log = LoggerFactory.getLogger(AlfrescoSolrDataModel.class); + + private static final String CHANGE_SET = "CHANGE_SET"; + + private static final String TX = "TX"; + + public static final String DEFAULT_TENANT = "_DEFAULT_"; + + private final static AlfrescoSolrDataModel INSTANCE = new AlfrescoSolrDataModel(); + + private final TenantService tenantService; + + private final NamespaceDAO namespaceDAO; + + private final DictionaryDAOImpl dictionaryDAO; + + private final Map dictionaryServices; + + private final Map cmisDictionaryServices; + + private final Map> modelErrors = new HashMap<>(); + private final Set suggestableProperties = new HashSet<>(); + private final Set crossLocaleSearchDataTypes = new HashSet<>(); + private final Set crossLocaleSearchProperties = new HashSet<>(); + private final Set identifierProperties = new HashSet<>(); + private final ThreadPoolExecutor threadPool; + + public void close() { + threadPool.shutdown(); + } + + public AlfrescoSolrDataModel() + { + tenantService = new SingleTServiceImpl(); + + dictionaryDAO = new DictionaryDAOImpl(); + dictionaryDAO.setTenantService(tenantService); + + try + { + CompiledModelsCache compiledModelsCache = new CompiledModelsCache(); + compiledModelsCache.setDictionaryDAO(dictionaryDAO); + compiledModelsCache.setTenantService(tenantService); + compiledModelsCache.setRegistry(new DefaultAsynchronouslyRefreshedCacheRegistry()); + TrackerPoolFactory trackerPoolFactory = new DefaultTrackerPoolFactory(new Properties(), "_dictionary_", "_internal_"); + threadPool = trackerPoolFactory.create(); + compiledModelsCache.setThreadPoolExecutor(threadPool); + + dictionaryDAO.setDictionaryRegistryCache(compiledModelsCache); + dictionaryDAO.setResourceClassLoader(getResourceClassLoader()); + dictionaryDAO.init(); + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Failed to create dictionaryDAO ", e); + } + + namespaceDAO = dictionaryDAO; + + QNameFilter qnameFilter = getQNameFilter(); + dictionaryServices = AlfrescoClientDataModelServicesFactory.constructDictionaryServices(qnameFilter, dictionaryDAO); + DictionaryComponent dictionaryComponent = getDictionaryService(CMISStrictDictionaryService.DEFAULT); + dictionaryComponent.setMessageLookup(new StaticMessageLookup()); + + cmisDictionaryServices = AlfrescoClientDataModelServicesFactory.constructDictionaries(qnameFilter, namespaceDAO, dictionaryComponent, dictionaryDAO); + + Properties props = getCommonConfig(); + for (Object key : props.keySet()) + { + String stringKey = (String) key; + if (stringKey.startsWith("alfresco.suggestable.property.")) + { + QName qName = QName.createQName(props.getProperty(stringKey)); + suggestableProperties.add(qName); + } + else if (stringKey.startsWith("alfresco.cross.locale.property.")) + { + QName qName = QName.createQName(props.getProperty(stringKey)); + crossLocaleSearchProperties.add(qName); + } + else if (stringKey.startsWith("alfresco.cross.locale.datatype.")) + { + QName qName = QName.createQName(props.getProperty(stringKey)); + crossLocaleSearchDataTypes.add(qName); + } + else if (stringKey.startsWith("alfresco.identifier.property.")) + { + QName qName = QName.createQName(props.getProperty(stringKey)); + identifierProperties.add(qName); + } + } + + if(props.isEmpty()) + { + suggestableProperties.add(ContentModel.PROP_NAME); + suggestableProperties.add(ContentModel.PROP_TITLE); + suggestableProperties.add(ContentModel.PROP_DESCRIPTION); + suggestableProperties.add(ContentModel.PROP_CONTENT); + + crossLocaleSearchDataTypes.add(DataTypeDefinition.TEXT); + crossLocaleSearchDataTypes.add(DataTypeDefinition.CONTENT); + crossLocaleSearchDataTypes.add(DataTypeDefinition.MLTEXT); + + identifierProperties.add(ContentModel.PROP_CREATOR); + identifierProperties.add(ContentModel.PROP_MODIFIER); + identifierProperties.add(ContentModel.PROP_USERNAME); + identifierProperties.add(ContentModel.PROP_AUTHORITY_NAME); + } + } + + public static String getTenantId(String tenant) + { + if((tenant == null) || tenant.equals(TenantService.DEFAULT_DOMAIN)) + { + return DEFAULT_TENANT; + } + else + { + return tenant.replaceAll("!", "_-._"); + } + } + + /** + * Returns the ACL Document identifier in the following format <TENANT>!<ACLID>!ACL + * + * @param tenant the tenant code. + * @param aclId the acl identifer + * @return the ACL Document identifier (<TENANT>!<ACLID>!ACL) + */ + public static String getAclDocumentId(String tenant, Long aclId) + { + return getTenantId(tenant) + "!" + NumericEncoder.encode(aclId) + "!ACL"; + } + + /** + * Returns the ACL ChangeSet Document identifier in the following format TRACKER!CHANGESET!<ACL_CHANGESET_ID> + * + * @param aclChangeSetId the ACL changeset identifier. + * @return the ACL ChangeSet Document identifier (TRACKER!CHANGESET!<ACL_CHANGESET_ID>) + */ + public static String getAclChangeSetDocumentId(Long aclChangeSetId) + { + return "TRACKER" + "!" + CHANGE_SET + "!" + NumericEncoder.encode(aclChangeSetId); + } + + + /** + * Extracts the transaction identifier from the input document identifier. + * The "input document identifier" is the alphanumeric identifier used for Solr documents. + * + * @param documentId the document identifier. + * @return the numeric ACL changeset identifier. + */ + public static Long parseTransactionId(String documentId) + { + return parseIdFromDocumentId(documentId); + } + + private static Long parseIdFromDocumentId(String documentId) + { + String[] split = documentId.split("!"); + if (split.length > 0) + return NumericEncoder.decodeLong(split[split.length - 1]); + else + return null; + } + + /** + * Returns the Solr document identifier in the following format <TENANT>!<!<DBID> + * + * @param tenant the tenant code. + * @param dbid the DB identifier. + * @return the Solr document identifier in the following format <TENANT>!<!<DBID> + */ + public static String getNodeDocumentId(String tenant, Long dbid) + { + return getTenantId(tenant) + "!" + NumericEncoder.encode(dbid); + } + + /** + * Destructures a document identifier in the three compounding parts (tenant and dbid). + * + * @param id the document identifier. + * @return a {@link TenantDbId} instance containing the destructured identifier. + */ + public static TenantDbId decodeNodeDocumentId(String id) + { + TenantDbId ids = new TenantDbId(); + String[] split = id.split("!"); + if (split.length > 0) + ids.tenant = split[0]; + if (split.length > 1) + ids.dbId = NumericEncoder.decodeLong(split[1]); + return ids; + } + + /** + * Returns the Transaction Document identifier in the following format TRACKER!TX!<TX_ID> + * + * @param txId the transaction identifier. + * @return the Transaction Document identifier in the following format TRACKER!TX!<TX_ID> + */ + public static String getTransactionDocumentId(Long txId) + { + // TODO - check and encode for ":" + return "TRACKER" + + "!" + + TX + + "!" + + NumericEncoder.encode(txId); + } + + public String getAlfrescoPropertyFromSchemaField(String schemaField) + { + int index = schemaField.lastIndexOf("@{"); + if(index == -1) + { + return schemaField; + } + + String alfrescoQueryField = schemaField.substring(index+1); + QName qName = QName.createQName(alfrescoQueryField); + alfrescoQueryField = qName.toPrefixString(namespaceDAO); + + PropertyDefinition propertyDefinition = getPropertyDefinition(qName); + if((propertyDefinition == null)) + { + return alfrescoQueryField; + } + if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) + { + return alfrescoQueryField; + } + + DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); + if(dataTypeDefinition.getName().equals(DataTypeDefinition.CONTENT)) + { + if(schemaField.contains("__size@")) + { + return alfrescoQueryField + ".size"; + } + else if(schemaField.contains("__locale@")) + { + return alfrescoQueryField + ".locale"; + } + else if(schemaField.contains("__mimetype@")) + { + return alfrescoQueryField + ".mimetype"; + } + else if(schemaField.contains("__encoding@")) + { + return alfrescoQueryField + ".encoding"; + } + else if(schemaField.contains("__docid@")) + { + return alfrescoQueryField + ".docid"; + } + else if(schemaField.contains("__tr_ex@")) + { + return alfrescoQueryField + ".tr_ex"; + } + else if(schemaField.contains("__tr_time@")) + { + return alfrescoQueryField + ".tr_time"; + } + else if(schemaField.contains("__tr_status@")) + { + return alfrescoQueryField + ".tr_status"; + } + else + { + return alfrescoQueryField; + } + } + else + { + return alfrescoQueryField; + } + } + + public static AlfrescoSolrDataModel getInstance() + { + return INSTANCE; + } + + public NamespaceDAO getNamespaceDAO() + { + return namespaceDAO; + } + + /** + * TODO: Fix to load type filter/exclusions from somewhere sensible + * @return QNameFilter + */ + private QNameFilter getQNameFilter() + { + QNameFilter qnameFilter = null; + FileSystemXmlApplicationContext ctx = null; + + File resourceDirectory = getResourceDirectory(); + // If we ever need to filter out models in the future, then we must put a filter somewhere. + // Currently, we do not need to filter any models, so this filter does not exist. + File filterContext = new File(resourceDirectory, "opencmis-qnamefilter-context.xml"); + + if(!filterContext.exists()) + { + log.info("No type filter context found at " + filterContext.getAbsolutePath() + ", no type filtering"); + return qnameFilter; + } + + try + { + ctx = new FileSystemXmlApplicationContext(new String[] { "file:" + filterContext.getAbsolutePath() }, false); + ctx.setClassLoader(this.getClass().getClassLoader()); + ctx.refresh(); + qnameFilter = (QNameFilter)ctx.getBean("cmisTypeExclusions"); + if(qnameFilter == null) + { + log.warn("Unable to find type filter at " + filterContext.getAbsolutePath() + ", no type filtering"); + } + } + catch(BeansException e) + { + log.warn("Unable to parse type filter at " + filterContext.getAbsolutePath() + ", no type filtering"); + } + finally + { + if(ctx != null && ctx.getBeanFactory() != null && ctx.isActive()) + { + ctx.close(); + } + } + + return qnameFilter; + } + + public static Properties getCommonConfig() + { + File resourceDirectory = getResourceDirectory(); + // If we ever need to filter out models in the future, then we must put a filter somewhere. + // Currently, we do not need to filter any models, so this filter does not exist. + File propertiesFile = new File(resourceDirectory, SHARED_PROPERTIES); + + Properties props = new Properties(); + if(!propertiesFile.exists()) + { + log.info("No shared properties found at " + propertiesFile.getAbsolutePath()); + return props; + } + + try(InputStream is = new FileInputStream(propertiesFile)) + { + props.load(is); + } + catch (IOException e) + { + log.info("Failed to read shared properties at " + propertiesFile.getAbsolutePath()); + } + + return props; + } + + /** + * @return ClassLoader + */ + public ClassLoader getResourceClassLoader() + { + File f = getResourceDirectory(); + if (f.canRead() && f.isDirectory()) + { + URL[] urls = new URL[1]; + + try + { + URL url = f.toURI().normalize().toURL(); + urls[0] = url; + } + catch (MalformedURLException e) + { + throw new AlfrescoRuntimeException("Failed to add resources to classpath ", e); + } + + return URLClassLoader.newInstance(urls, this.getClass().getClassLoader()); + } + else + { + return this.getClass().getClassLoader(); + } + } + + public static File getResourceDirectory() + { + return new File(SolrResourceLoader.locateSolrHome().toFile(), "conf"); + } + + /** + * Gets a DictionaryService, if an Alternative dictionary is specified it tries to get that. + * It will attempt to get the DEFAULT dictionary service if null is specified or it can't find + * a dictionary with the name of "alternativeDictionary" + * @param alternativeDictionary - can be null; + * @return DictionaryService + */ + public DictionaryComponent getDictionaryService(String alternativeDictionary) + { + DictionaryComponent dictionaryComponent = null; + + if (alternativeDictionary != null && !alternativeDictionary.trim().isEmpty()) + { + dictionaryComponent = dictionaryServices.get(alternativeDictionary); + } + + if (dictionaryComponent == null) + { + dictionaryComponent = dictionaryServices.get(CMISStrictDictionaryService.DEFAULT); + } + return dictionaryComponent; + } + + public IndexedField getIndexedFieldForSpecializedPropertyMetadata(QName propertyQName, SpecializedFieldType type) + { + IndexedField indexedField = new IndexedField(); + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + if((propertyDefinition == null)) + { + return indexedField; + } + if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) + { + return indexedField; + } + + DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); + if(dataTypeDefinition.getName().equals(DataTypeDefinition.CONTENT)) + { + StringBuilder builder = new StringBuilder(); + builder.append(dataTypeDefinition.getName().getLocalName()); + builder.append('@'); + // TODO wher we support multi value propertis correctly .... builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); + builder.append('s'); + builder.append("_"); + builder.append('_'); + switch (type) + { + case CONTENT_DOCID: + builder.append("docid"); + break; + case CONTENT_ENCODING: + builder.append("encoding"); + break; + case CONTENT_LOCALE: + builder.append("locale"); + break; + case CONTENT_MIMETYPE: + builder.append("mimetype"); + break; + case CONTENT_SIZE: + builder.append("size"); + break; + case TRANSFORMATION_EXCEPTION: + builder.append("tr_ex"); + break; + case TRANSFORMATION_STATUS: + builder.append("tr_status"); + break; + case TRANSFORMATION_TIME: + builder.append("tr_time"); + break; + default: + break; + } + + builder.append('@'); + builder.append(propertyQName); + indexedField.addField(builder.toString(), false, false); + } + else if (isDateOrDatetime(dataTypeDefinition)) + { + String dateDerivedSuffix = getDateDerivedSuffix(type); + if (dateDerivedSuffix != null) + { + indexedField.addField(getDateDerivedField(propertyQName, dateDerivedSuffix), false, true); + } + } + return indexedField; + + } + + + public IndexedField getQueryableFields(QName propertyQName, SpecializedFieldType type, FieldUse fieldUse) + { + if(type != null) + { + return getIndexedFieldForSpecializedPropertyMetadata(propertyQName, type); + } + + IndexedField indexedField = new IndexedField(); + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + if((propertyDefinition == null)) + { + indexedField.addField("_dummy_", false, false); + return indexedField; + } + if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) + { + indexedField.addField("_dummy_", false, false); + return indexedField; + } + + if(isTextField(propertyDefinition)) + { + switch(fieldUse) + { + case COMPLETION: + addCompletionFields(propertyDefinition, indexedField); + break; + case FACET: + addFacetSearchFields(propertyDefinition, indexedField); + break; + case FTS: + addFullTextSearchFields(propertyDefinition, indexedField); + break; + case ID: + addIdentifierSearchFields(propertyDefinition, indexedField); + break; + case EXACT: + addExactSearchFields(propertyDefinition, indexedField); + break; + case MULTI_FACET: + addMultiSearchFields(propertyDefinition, indexedField); + break; + case SORT: + addSortSearchFields(propertyDefinition, indexedField); + break; + case STATS: + addStatsSearchFields(propertyDefinition, indexedField); + break; + case SUGGESTION: + if(isSuggestable(propertyQName)) + { + indexedField.addField("suggest", false, false); + } + addCompletionFields(propertyDefinition, indexedField); + break; + case HIGHLIGHT: + addHighlightSearchFields(propertyDefinition, indexedField); + break; + } + } + else + { + indexedField.addField(getFieldForNonText(propertyDefinition), false, false); + } + return indexedField; + } + + /* + * Adds best completion fields in order of preference + */ + private void addCompletionFields(PropertyDefinition propertyDefinition, IndexedField indexedField) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + else if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE)) + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + else if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE)) + { + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + } + + /* + * Adds best fts fields in order of preference + */ + + private void addFullTextSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + if (((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + && !isIdentifierTextProperty(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + } + else + { + indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + } + + private void addHighlightSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + QName propertyName = propertyDefinition.getName(); + QName propertyDataTypeQName = propertyDefinition.getDataType().getName(); + String fieldName; + + if(propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) + { + fieldName = getStoredMLTextField(propertyName); + } + else if(propertyDataTypeQName.equals(DataTypeDefinition.CONTENT)) + { + fieldName = getStoredContentField(propertyName); + } + else + { + fieldName = getStoredTextField(propertyName); + } + + indexedField.addField(fieldName, false, false); + } + + /* + * If the untokenised version is available, go for it. + * If not it takes the tokenised versions available. + */ + private void addIdentifierSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + + indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + } + } + + /* + * If only the untokenised version is available, go for it. + * If not it takes the tokenised cross locale version. + */ + private void addExactSearchFields(PropertyDefinition propertyDefinition, IndexedField indexedField) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || !(propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + + indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } else{ + throw new UnsupportedOperationException("Exact Term search is not supported unless you configure the field <"+propertyDefinition.getName()+"> for cross locale search"); + } + } + } + + /* + * Adds best identifier fields in order of preference + */ + private void addFacetSearchFields(PropertyDefinition propertyDefinition, IndexedField indexedField) + { + if(propertyDefinition.getDataType().getName().equals(DataTypeDefinition.TEXT)) + { + if (!isIdentifierTextProperty(propertyDefinition.getName())) + { + if(propertyDefinition.getFacetable() == Facetable.TRUE) + { + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + } + } + + + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH) + || isIdentifierTextProperty(propertyDefinition.getName())) + { + + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + } + + private void addMultiSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH) + || isIdentifierTextProperty(propertyDefinition.getName())) + { + + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + } + + private void addStatsSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + addFacetSearchFields(propertyDefinition, indexedField); + } + + private void addSortSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField) + { + // Can only order on single valued fields + DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); + if(dataTypeDefinition.getName().equals(DataTypeDefinition.TEXT)) + { + if(!propertyDefinition.isMultiValued()) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); + } + else if (isIdentifierTextProperty(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + } + } + + if(dataTypeDefinition.getName().equals(DataTypeDefinition.MLTEXT)) + { + if(!propertyDefinition.isMultiValued()) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); + } + else + { + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + else + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), false, false); + } + } + } + } + } + + + public String getStoredTextField(QName propertyQName) + { + return getStoredTextField(propertyQName, null); + } + + public String getStoredTextField(QName propertyQName, String suffix) + { + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + + StringBuilder sb = new StringBuilder(); + sb.append("text@" + (propertyDefinition.isMultiValued()? "m" : "s") + "_stored_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || + isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); + + sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || + crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || + isIdentifierTextProperty(propertyDefinition.getName())) && !propertyDefinition.isMultiValued()? "s" : "_"); + + sb.append(isSuggestable(propertyQName)? "s": "_"); + + sb.append("@"); + sb.append(propertyDefinition.getName().toString()); + + if (suffix != null) + { + sb.append(suffix); + } + + return sb.toString(); + + } + + public String getStoredMLTextField(QName propertyQName) + { + return getStoredMLTextField(propertyQName, null); + } + + public String getStoredMLTextField(QName propertyQName, String suffix) + { + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + + StringBuilder sb = new StringBuilder(); + sb.append("mltext@m_stored_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || + isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); + + sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || + crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); + + sb.append("_"); + + sb.append(isSuggestable(propertyQName)? "s": "_"); + + sb.append("@"); + sb.append(propertyDefinition.getName().toString()); + + if (suffix != null) + { + sb.append(suffix); + } + + return sb.toString(); + + } + + public String getStoredContentField(QName propertyQName) + { + return getStoredContentField(propertyQName, null); + } + + public String getStoredContentField(QName propertyQName, String suffix) + { + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + + StringBuilder sb = new StringBuilder(); + sb.append("content@s_stored_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)? "t" : "_"); + + sb.append((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE || + propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH || + isIdentifierTextProperty(propertyDefinition.getName()))? "s" : "_"); + + sb.append((crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || + crossLocaleSearchProperties.contains(propertyDefinition.getName())) ? "c" : "_"); + + + sb.append("_"); + sb.append(isSuggestable(propertyQName)? "s": "_"); + + sb.append("@"); + sb.append(propertyDefinition.getName().toString()); + + if (suffix != null) + { + sb.append(suffix); + } + + return sb.toString(); + + } + + public String getDateDerivedField(QName propertyQName, String suffix) + { + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + return "part@sd@" + propertyDefinition.getName().toString() + suffix; + } + + public String getDateDerivedSuffix(SpecializedFieldType type) + { + switch (type) + { + case UNIT_OF_TIME_SECOND: + return UNIT_OF_TIME_SECOND_FIELD_SUFFIX; + case UNIT_OF_TIME_MINUTE: + return UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; + case UNIT_OF_TIME_HOUR: + return UNIT_OF_TIME_HOUR_FIELD_SUFFIX; + case UNIT_OF_TIME_DAY: + return UNIT_OF_TIME_DAY_FIELD_SUFFIX; + case UNIT_OF_TIME_DAY_OF_WEEK: + return UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX; + case UNIT_OF_TIME_DAY_OF_YEAR: + return UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX; + case UNIT_OF_TIME_MONTH: + return UNIT_OF_TIME_MONTH_FIELD_SUFFIX; + case UNIT_OF_TIME_QUARTER: + return UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; + case UNIT_OF_TIME_YEAR: + return UNIT_OF_TIME_YEAR_FIELD_SUFFIX; + default: + return null; + } + } + + + /** + * Get all the field names into which we must copy the source data + * + * @param propertyQName QName + * @return IndexedField + */ + public IndexedField getIndexedFieldNamesForProperty(QName propertyQName) + { + // TODO: Cache and throw on model refresh + + IndexedField indexedField = new IndexedField(); + PropertyDefinition propertyDefinition = getPropertyDefinition(propertyQName); + if((propertyDefinition == null)) + { + return indexedField; + } + if(!propertyDefinition.isIndexed() && !propertyDefinition.isStoredInIndex()) + { + return indexedField; + } + + DataTypeDefinition dataTypeDefinition = propertyDefinition.getDataType(); + if(isTextField(propertyDefinition)) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + indexedField.addField(getFieldForText(true, true, false, propertyDefinition), true, false); + if(crossLocaleSearchDataTypes.contains(propertyDefinition.getDataType().getName()) || crossLocaleSearchProperties.contains(propertyDefinition.getName())) + { + indexedField.addField(getFieldForText(false, true, false, propertyDefinition), false, false); + } + } + + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH + || isIdentifierTextProperty(propertyDefinition.getName()))) + { + indexedField.addField(getFieldForText(true, false, false, propertyDefinition), true, false); + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + + if(dataTypeDefinition.getName().equals(DataTypeDefinition.TEXT)) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + if(!propertyDefinition.isMultiValued()) + { + indexedField.addField(getFieldForText(false, false, true, propertyDefinition), false, true); + } + } + else if (!isIdentifierTextProperty(propertyDefinition.getName())) + { + if(propertyDefinition.getFacetable() == Facetable.TRUE) + { + indexedField.addField(getFieldForText(false, false, false, propertyDefinition), false, false); + } + } + } + + if(dataTypeDefinition.getName().equals(DataTypeDefinition.MLTEXT)) + { + if ((propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.FALSE) + || (propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.BOTH)) + { + if(!propertyDefinition.isMultiValued()) + { + indexedField.addField(getFieldForText(true, false, true, propertyDefinition), true, true); + } + } + } + + if(isSuggestable(propertyQName)) + { + indexedField.addField("suggest_@" + propertyDefinition.getName().toString(), false, false); + } + } + else + { + indexedField.addField(getFieldForNonText(propertyDefinition), false, false); + } + + return indexedField; + } + + private boolean isIdentifierTextProperty(QName propertyQName) + { + return identifierProperties.contains(propertyQName); + } + + public boolean isTextField(PropertyDefinition propertyDefinition) + { + return ofNullable(propertyDefinition) + .map(PropertyDefinition::getDataType) + .map(DataTypeDefinition::getName) + .map(name -> + name.equals(DataTypeDefinition.MLTEXT) + || + name.equals(DataTypeDefinition.CONTENT) + || + name.equals(DataTypeDefinition.TEXT)) + .orElse(false); + } + + private boolean isSuggestable(QName propertyQName) + { + if(propertyQName == null) + { + return false; + } + return suggestableProperties.contains(propertyQName); + } + + private boolean hasDocValues(PropertyDefinition propertyDefinition) + { + + if(isTextField(propertyDefinition)) + { + // We only call this if text is untokenised and localised + return propertyDefinition.getFacetable() != Facetable.FALSE; + } + else + { + if(propertyDefinition.getFacetable() == Facetable.FALSE) + { + return false; + } + else if(propertyDefinition.getFacetable() == Facetable.TRUE) + { + return true; + } + else + { + if(propertyDefinition.getIndexTokenisationMode() == IndexTokenisationMode.TRUE) + { + return false; + } + else + { + return isPrimitive(propertyDefinition.getDataType()); + } + } + } + } + + public String getFieldForNonText(PropertyDefinition propertyDefinition) + { + StringBuilder builder = new StringBuilder(); + QName qName = propertyDefinition.getDataType().getName(); + builder.append(qName.getLocalName()); + builder.append("@"); + builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); + builder.append(hasDocValues(propertyDefinition) ? "d" : "_"); + builder.append("@"); + builder.append(propertyDefinition.getName().toString()); + return builder.toString(); + } + + private String getFieldForText(boolean localised, boolean tokenised, boolean sort, PropertyDefinition propertyDefinition) + { + StringBuilder builder = new StringBuilder(); + QName qName = propertyDefinition.getDataType().getName(); + builder.append(qName.getLocalName()); + builder.append("@"); + QName propertyDataTypeQName = propertyDefinition.getDataType().getName(); + if(propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) + { + builder.append('m'); + } + else if(propertyDataTypeQName.equals(DataTypeDefinition.CONTENT)) + { + builder.append('s'); + } + else + { + builder.append(propertyDefinition.isMultiValued() ? "m" : "s"); + } + if(sort || localised || tokenised || propertyDataTypeQName.equals(DataTypeDefinition.CONTENT) || propertyDataTypeQName.equals(DataTypeDefinition.MLTEXT)) + { + builder.append('_'); + } + else + { + builder.append(hasDocValues(propertyDefinition) ? "d" : "_"); + } + builder.append('_'); + if(!sort) + { + builder.append(localised ? "l" : "_"); + builder.append(tokenised ? "t" : "_"); + } + else + { + builder.append("sort"); + } + builder.append("@"); + builder.append(propertyDefinition.getName().toString()); + return builder.toString(); + } + + public PropertyDefinition getPropertyDefinition(QName propertyQName) + { + return getDictionaryService(CMISStrictDictionaryService.DEFAULT).getProperty(propertyQName); + } + + public boolean putModel(M2Model model) + { + modelErrors.remove(model.getName()); + dictionaryDAO.putModelIgnoringConstraints(model); + return true; + } + + public void removeModel(QName modelQName) + { + modelErrors.remove(getM2Model(modelQName).getName()); + dictionaryDAO.removeModel(modelQName); + } + + M2Model getM2Model(QName modelQName) + { + return dictionaryDAO.getCompiledModel(modelQName).getM2Model(); + } + + public void afterInitModels() + { + for (CMISAbstractDictionaryService cds : cmisDictionaryServices.values()) + { + cds.afterDictionaryInit(); + } + } + + public org.alfresco.repo.search.impl.querymodel.Query parseCMISQueryToAlfrescoAbstractQuery(CMISQueryMode mode, SearchParameters searchParameters, + SolrQueryRequest req, String alternativeDictionary, CmisVersion cmisVersion) + { + // convert search parameters to cmis query options + // TODO: how to handle store ref + CMISQueryOptions options = new CMISQueryOptions(searchParameters.getQuery(), StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + options.setQueryMode(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS); + options.setDefaultFieldName(searchParameters.getDefaultFieldName()); + // TODO: options.setDefaultFTSConnective() + // TODO: options.setDefaultFTSFieldConnective() + options.setIncludeInTransactionData(!searchParameters.excludeDataInTheCurrentTransaction()); + options.setLocales(searchParameters.getLocales()); + options.setMlAnalaysisMode(searchParameters.getMlAnalaysisMode()); + options.setQueryParameterDefinitions(searchParameters.getQueryParameterDefinitions()); + for(String name : searchParameters.getQueryTemplates().keySet()) + { + String template = searchParameters.getQueryTemplates().get(name); + options.addQueryTemplate(name, template); + } + + // parse cmis syntax + CapabilityJoin joinSupport = (mode == CMISQueryMode.CMS_STRICT) ? CapabilityJoin.NONE : CapabilityJoin.INNERANDOUTER; + CmisFunctionEvaluationContext functionContext = getCMISFunctionEvaluationContext(mode, cmisVersion, alternativeDictionary); + + CMISDictionaryService cmisDictionary = getCMISDictionary(alternativeDictionary, cmisVersion); + + CMISQueryParser parser = new CMISQueryParser(options, cmisDictionary, joinSupport); + org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = parser.parse(new LuceneQueryModelFactory(), functionContext); + + if (queryModelQuery.getSource() != null) + { + List> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext); + if (selectorGroups.size() == 0) + { + throw new UnsupportedOperationException("No selectors"); + } + } + return queryModelQuery; + } + + public CmisFunctionEvaluationContext getCMISFunctionEvaluationContext(CMISQueryMode mode, CmisVersion cmisVersion, String alternativeDictionary) + { + BaseTypeId[] validScopes = (mode == CMISQueryMode.CMS_STRICT) ? CmisFunctionEvaluationContext.STRICT_SCOPES : CmisFunctionEvaluationContext.ALFRESCO_SCOPES; + CmisFunctionEvaluationContext functionContext = new CmisFunctionEvaluationContext(); + functionContext.setCmisDictionaryService(getCMISDictionary(alternativeDictionary, cmisVersion)); + functionContext.setValidScopes(validScopes); + return functionContext; + } + + /** + * Gets the CMISDictionaryService, if an Alternative dictionary is specified it tries to get that. + * It will attempt to get the DEFAULT dictionary service if null is specified or it can't find + * a dictionary with the name of "alternativeDictionary" + * + * @param alternativeDictionary - can be null; + * @return CMISDictionaryService + */ + public CMISDictionaryService getCMISDictionary(String alternativeDictionary, CmisVersion cmisVersion) + { + CMISDictionaryService cmisDictionary = null; + + if (alternativeDictionary != null && !alternativeDictionary.trim().isEmpty()) + { + DictionaryKey key = new DictionaryKey(cmisVersion, alternativeDictionary); + cmisDictionary = cmisDictionaryServices.get(key); + } + + if (cmisDictionary == null) + { + DictionaryKey key = new DictionaryKey(cmisVersion, CMISStrictDictionaryService.DEFAULT); + cmisDictionary = cmisDictionaryServices.get(key); + } + return cmisDictionary; + } + + /** + * Returns the Alfresco models associated with the current dictionary. + * + * @return the Alfresco models associated with the current dictionary. + */ + public List getAlfrescoModels() + { + return dictionaryDAO.getModels().stream() + .map(qname -> { + M2Model m2Model = dictionaryDAO.getCompiledModel(qname).getM2Model(); + return new AlfrescoModel( + m2Model, + getDictionaryService( + CMISStrictDictionaryService.DEFAULT).getModel(qname).getChecksum(ModelDefinition.XMLBindingType.DEFAULT));}) + .collect(Collectors.toList()); + } + + Map> getModelErrors() + { + return modelErrors; + } + + public static class IndexedField + { + private final List fields = new LinkedList<>(); + + public List getFields() + { + return unmodifiableList(fields); + } + + public void addField(String prefix, boolean localised, boolean sort) + { + fields.add(new FieldInstance(prefix, localised, sort)); + } + } + + public static class FieldInstance + { + final String field; + final boolean localised; + final boolean sort; + + public FieldInstance(String field, boolean localised, boolean sort) + { + this.field = field; + this.localised = localised; + this.sort = sort; + } + + public String getField() + { + return field; + } + + public boolean isLocalised() + { + return localised; + } + + public boolean isSort() + { + return sort; + } + } + + public Query getCMISQuery(CMISQueryMode mode, Pair searchParametersAndFilter, SolrQueryRequest req, org.alfresco.repo.search.impl.querymodel.Query queryModelQuery, CmisVersion cmisVersion, String alternativeDictionary) throws ParseException + { + SearchParameters searchParameters = searchParametersAndFilter.getFirst(); + Boolean isFilter = searchParametersAndFilter.getSecond(); + + CmisFunctionEvaluationContext functionContext = getCMISFunctionEvaluationContext(mode, cmisVersion, alternativeDictionary); + + Set selectorGroup = queryModelQuery.getSource().getSelectorGroups(functionContext).get(0); + + QueryBuilderContext luceneContext = getLuceneQueryBuilderContext(searchParameters, req, alternativeDictionary, FTSQueryParser.RerankPhase.SINGLE_PASS); + @SuppressWarnings("unchecked") + LuceneQueryBuilder builder = (LuceneQueryBuilder) queryModelQuery; + org.apache.lucene.search.Query luceneQuery = builder.buildQuery(selectorGroup, luceneContext, functionContext); + + return new ContextAwareQuery(luceneQuery, Boolean.TRUE.equals(isFilter) ? null : searchParameters); + } + + public QueryBuilderContext getLuceneQueryBuilderContext(SearchParameters searchParameters, SolrQueryRequest req, String alternativeDictionary, FTSQueryParser.RerankPhase rerankPhase) + { + return new Lucene4QueryBuilderContextSolrImpl( + getDictionaryService(alternativeDictionary), + namespaceDAO, + tenantService, + searchParameters, + MLAnalysisMode.EXACT_LANGUAGE, + req, + this, + rerankPhase); + } + + public Solr4QueryParser getLuceneQueryParser(SearchParameters searchParameters, SolrQueryRequest req, FTSQueryParser.RerankPhase rerankPhase) + { + Analyzer analyzer = req.getSchema().getQueryAnalyzer(); + Solr4QueryParser parser = new Solr4QueryParser(req, Version.LATEST, searchParameters.getDefaultFieldName(), analyzer, rerankPhase); + parser.setNamespacePrefixResolver(namespaceDAO); + parser.setDictionaryService(getDictionaryService(CMISStrictDictionaryService.DEFAULT)); + parser.setTenantService(tenantService); + parser.setSearchParameters(searchParameters); + parser.setAllowLeadingWildcard(true); + + Properties props = new CoreDescriptorDecorator(req.getCore().getCoreDescriptor()).getProperties(); + int topTermSpanRewriteLimit = Integer.parseInt(props.getProperty("alfresco.topTermSpanRewriteLimit", "1000")); + parser.setTopTermSpanRewriteLimit(topTermSpanRewriteLimit); + + return parser; + } + + public Query getFTSQuery(Pair searchParametersAndFilter, SolrQueryRequest req, FTSQueryParser.RerankPhase rerankPhase) throws ParseException + { + + SearchParameters searchParameters = searchParametersAndFilter.getFirst(); + Boolean isFilter = searchParametersAndFilter.getSecond(); + + QueryModelFactory factory = new LuceneQueryModelFactory(); + AlfrescoFunctionEvaluationContext functionContext = new AlfrescoSolr4FunctionEvaluationContext(namespaceDAO, getDictionaryService(CMISStrictDictionaryService.DEFAULT), NamespaceService.CONTENT_MODEL_1_0_URI, req.getSchema()); + + FTSParser.Mode mode; + + if (searchParameters.getDefaultFTSOperator() == org.alfresco.service.cmr.search.SearchParameters.Operator.AND) + { + mode = FTSParser.Mode.DEFAULT_CONJUNCTION; + } + else + { + mode = FTSParser.Mode.DEFAULT_DISJUNCTION; + } + + Constraint constraint = FTSQueryParser.buildFTS(searchParameters.getQuery(), factory, functionContext, null, null, mode, + searchParameters.getDefaultFTSOperator() == org.alfresco.service.cmr.search.SearchParameters.Operator.OR ? Connective.OR : Connective.AND, + searchParameters.getQueryTemplates(), searchParameters.getDefaultFieldName(), rerankPhase); + org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = factory.createQuery(null, null, constraint, new ArrayList<>()); + + @SuppressWarnings("unchecked") + LuceneQueryBuilder builder = (LuceneQueryBuilder) queryModelQuery; + + QueryBuilderContext luceneContext = getLuceneQueryBuilderContext(searchParameters, req, CMISStrictDictionaryService.DEFAULT, rerankPhase); + + Set selectorGroup = null; + if (queryModelQuery.getSource() != null) + { + List> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext); + + if (selectorGroups.size() == 0) + { + throw new UnsupportedOperationException("No selectors"); + } + + if (selectorGroups.size() > 1) + { + throw new UnsupportedOperationException("Advanced join is not supported"); + } + + selectorGroup = selectorGroups.get(0); + } + Query luceneQuery = builder.buildQuery(selectorGroup, luceneContext, functionContext); + // query needs some search parameters fro correct caching .... + + return new ContextAwareQuery(luceneQuery, Boolean.TRUE.equals(isFilter) ? null : searchParameters); + } + + private PropertyDefinition getPropertyDefinition(String identifier) + { + return QueryParserUtils.matchPropertyDefinition(NamespaceService.CONTENT_MODEL_1_0_URI, + getNamespaceDAO(), + getDictionaryService(CMISStrictDictionaryService.DEFAULT), + identifier); + } + + public String mapProperty(String potentialProperty, FieldUse fieldUse, SolrQueryRequest req) + { + return mapProperty(potentialProperty, fieldUse, req, 0); + } + + /** + * + * return the stored field associated to potentialProperty parameter + */ + public String mapStoredProperty(String potentialProperty, SolrQueryRequest req) + { + if(potentialProperty.equals("asc") || potentialProperty.equals("desc") || potentialProperty.equals("_docid_")) + { + return potentialProperty; + } + + if(potentialProperty.equalsIgnoreCase("score") || potentialProperty.equalsIgnoreCase("SEARCH_SCORE")) + { + return "score"; + } + + AlfrescoFunctionEvaluationContext functionContext = + new AlfrescoSolr4FunctionEvaluationContext( + getNamespaceDAO(), + getDictionaryService(CMISStrictDictionaryService.DEFAULT), + NamespaceService.CONTENT_MODEL_1_0_URI, + req.getSchema()); + + + Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(potentialProperty); + String luceneField = functionContext.getLuceneFieldName(fieldNameAndEnding.getFirst()); + + PropertyDefinition propertyDef = getPropertyDefinition(fieldNameAndEnding.getFirst()); + //Retry scan using luceneField. + if(propertyDef == null) + { + if(luceneField.contains("@")) + { + int index = luceneField.lastIndexOf("@"); + propertyDef = getPropertyDefinition(luceneField.substring(index +1)); + } + } + + if (propertyDef == null || propertyDef.getName() == null) + { + return mapNonPropertyFields(luceneField); + } + + if (isDateOrDatetime(propertyDef.getDataType()) && isDerivedDateField(fieldNameAndEnding.getSecond())) + { + return getDateDerivedField(propertyDef.getName(), fieldNameAndEnding.getSecond()); + } + else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) + { + return getStoredTextField(propertyDef.getName(), fieldNameAndEnding.getSecond()); + } + else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) + { + return getStoredMLTextField(propertyDef.getName(), fieldNameAndEnding.getSecond()); + } + else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) + { + return getStoredContentField(propertyDef.getName(), fieldNameAndEnding.getSecond()); + } + else + { + return mapAlfrescoField(FieldUse.FTS, 0, fieldNameAndEnding, luceneField, propertyDef) + + fieldNameAndEnding.getSecond(); + } + } + + public boolean isDerivedDateField(String suffix) + { + return DATE_PART_SUFFIXES.contains(suffix); + } + + public String mapProperty(String potentialProperty, FieldUse fieldUse, SolrQueryRequest req, int position) + { + if(potentialProperty.equals("asc") || potentialProperty.equals("desc") || potentialProperty.equals("_docid_")) + { + return potentialProperty; + } + + if(potentialProperty.equalsIgnoreCase("score") || potentialProperty.equalsIgnoreCase("SEARCH_SCORE")) + { + return "score"; + } + + if(req.getSchema().getFieldOrNull(potentialProperty) != null) + { + return mapNonPropertyFields(potentialProperty); + } + + AlfrescoFunctionEvaluationContext functionContext = + new AlfrescoSolr4FunctionEvaluationContext( + getNamespaceDAO(), + getDictionaryService(CMISStrictDictionaryService.DEFAULT), + NamespaceService.CONTENT_MODEL_1_0_URI, + req.getSchema()); + + Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(potentialProperty); + String luceneField = functionContext.getLuceneFieldName(fieldNameAndEnding.getFirst()); + + PropertyDefinition propertyDef = getPropertyDefinition(fieldNameAndEnding.getFirst()); + //Retry scan using luceneField. + if(propertyDef == null) + { + if(luceneField.contains("@")) + { + int index = luceneField.lastIndexOf("@"); + propertyDef = getPropertyDefinition(luceneField.substring(index +1)); + } + } + String solrSortField; + solrSortField = mapAlfrescoField(fieldUse, position, fieldNameAndEnding, luceneField, propertyDef); + return solrSortField; + } + + public String mapNonPropertyFields(String queryField) + { + switch(queryField) + { + case "ID": + return "LID"; + case "EXACTTYPE": + return "TYPE"; + default: + return queryField; + + } + } + + /** + * @param ending String + * @return SpecializedFieldType + */ + public SpecializedFieldType getTextField(String ending) + { + switch(ending) + { + case FIELD_MIMETYPE_SUFFIX: + return SpecializedFieldType.CONTENT_MIMETYPE; + case FIELD_SIZE_SUFFIX: + return SpecializedFieldType.CONTENT_SIZE; + case FIELD_LOCALE_SUFFIX: + return SpecializedFieldType.CONTENT_LOCALE; + case FIELD_ENCODING_SUFFIX: + return SpecializedFieldType.CONTENT_ENCODING; + case UNIT_OF_TIME_SECOND_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_SECOND; + case UNIT_OF_TIME_MINUTE_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_MINUTE; + case UNIT_OF_TIME_HOUR_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_HOUR; + case UNIT_OF_TIME_DAY_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_DAY; + case UNIT_OF_TIME_DAY_OF_WEEK_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_DAY_OF_WEEK; + case UNIT_OF_TIME_DAY_OF_YEAR_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_DAY_OF_YEAR; + case UNIT_OF_TIME_MONTH_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_MONTH; + case UNIT_OF_TIME_QUARTER_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_QUARTER; + case UNIT_OF_TIME_YEAR_FIELD_SUFFIX: + return SpecializedFieldType.UNIT_OF_TIME_YEAR; + case FIELD_TRANSFORMATION_STATUS_SUFFIX: + case FIELD_TRANSFORMATION_TIME_SUFFIX: + case FIELD_TRANSFORMATION_EXCEPTION_SUFFIX: + default: + return null; + } + } + + public void setCMDefaultUri() + { + if(getNamespaceDAO().getURIs().contains(NamespaceService.CONTENT_MODEL_1_0_URI)) + { + getNamespaceDAO().addPrefix("", NamespaceService.CONTENT_MODEL_1_0_URI); + } + } + + /** + * Returns the prefix used for denoting a field which is meant to represent a constituent part of a + * date or datetime (e.g. year, month, second, minute). + * + * @param sourceFieldName the date/datetime source field name. + * @return the prefix that can be used for denoting a date or datetime part + */ + public String destructuredDateTimePartFieldNamePrefix(String sourceFieldName) { + // source field name example: datetime@sd@{http://www.alfresco.org/model/content/1.0}created + // prefix: datetime + String prefix = sourceFieldName.substring(0, sourceFieldName.indexOf("@")); + + // prefix, docValues disabled option: datetime@s_@ + String sourceFieldNamePrefixWithoutDocValues = prefix + SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER; + + // prefix, docValues enabled option: datetime@sd@ + String sourceFieldNamePrefixWithDocValues = prefix + SINGLE_VALUE_WITH_DOC_VALUES_MARKER; + + return sourceFieldName.replace(sourceFieldNamePrefixWithoutDocValues, PART_FIELDNAME_PREFIX) + .replace(sourceFieldNamePrefixWithDocValues, PART_FIELDNAME_PREFIX); + } + + private boolean isDateOrDatetime(DataTypeDefinition dataType) + { + return dataType.getName().equals(DataTypeDefinition.DATE) || + dataType.getName().equals(DataTypeDefinition.DATETIME); + } + + private boolean isPrimitive(DataTypeDefinition dataType) + { + QName name = dataType.getName(); + return name.equals(DataTypeDefinition.INT) + || name.equals(DataTypeDefinition.LONG) + || name.equals(DataTypeDefinition.FLOAT) + || name.equals(DataTypeDefinition.DOUBLE) + || isDateOrDatetime(dataType) + || name.equals(DataTypeDefinition.BOOLEAN) + || name.equals(DataTypeDefinition.CATEGORY) + || name.equals(DataTypeDefinition.NODE_REF); + } + + private String mapAlfrescoField(FieldUse fieldUse, int position, Pair fieldNameAndEnding, String luceneField, PropertyDefinition propertyDef) + { + String solrSortField; + if(propertyDef != null) + { + + IndexedField fields = AlfrescoSolrDataModel.getInstance().getQueryableFields(propertyDef.getName(), getTextField(fieldNameAndEnding.getSecond()), fieldUse); + if(fields.getFields().size() > 0) + { + if(fields.getFields().size() > position) + { + solrSortField = fields.getFields().get(position).getField(); + } + else + { + solrSortField = fields.getFields().get(0).getField(); + } + } + else + { + solrSortField = mapNonPropertyFields(luceneField); + } + } + else + { + solrSortField = mapNonPropertyFields(luceneField); + } + return solrSortField; + } + + } \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/Cloud.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/Cloud.java index 2f2e8a73d..37882dd0b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/Cloud.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/Cloud.java @@ -1,168 +1,168 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import java.util.Collection; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.solr.common.SolrDocumentList; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.request.SolrRequestHandler; -import org.apache.solr.response.ResultContext; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.search.DocList; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Cloud consists of support methods for running solr queries in the cloud. - * - * @author Ahmed Owian - */ -public class Cloud -{ - public static final String RESPONSE_SOLR_DOCUMENT_LIST = "responseSolrDocumentList"; - public static final String PARAM_GET_SOLR_DOCUMENT_LIST = "alfresco.getSolrDocumentList"; - protected final static Logger log = LoggerFactory.getLogger(Cloud.class); - - /** - * Creates a query to select docs with any of the specified field values - * @param fieldName the name of the field in the condition - * @param operator currently only OR makes sense. To add AND, fieldName must become a list of fields. - * @param valueLists a bunch of possible values for the field - * @return the resulting query string - */ - @SuppressWarnings("rawtypes") - String getQuery(String fieldName, String operator, Collection... valueLists) - { - StringBuilder query = new StringBuilder(); - for (Collection values : valueLists) - { - for (Object value : values) - { - query.append(fieldName).append(":").append("\"").append(value.toString()).append("\"").append(operator); - } - } - - if (query.length() >= operator.length()) - { - // Removes the last appended operator from the query string - query.delete(query.length() - operator.length(), query.length()); - } - - return query.toString(); - } - - /** - * Returns whether or not a doc exists that satisfies the specified query - * @param requestHandler the handler that handles the request - * @param request the request object to put the query on - * @param query the string that specifies the doc - * @return true if the specified query returns a doc - */ - boolean exists(SolrRequestHandler requestHandler, SolrQueryRequest request, String query) - { - ModifiableSolrParams params = new ModifiableSolrParams(request.getParams()); - // Sets 1 because this is effectively a boolean query to see if there exists a single match - params.set("q", query).set("fl", "id").set("rows", "1"); - ResultContext rc = this.getResultContext(requestHandler, request, params); - - if (rc != null) - { -// TODO Should we use rc.docs.matches() instead? - if (rc.getDocList() != null) { return rc.getDocList().iterator().hasNext(); } - } - - return false; - } - - /** - * Returns the doc list resulting from running the query - * @param requestHandler the handler that handles the request - * @param request the request object to put the query on - * @param query the string that specifies the docs - * @return the docs that come back from the query - */ - DocList getDocList(SolrRequestHandler requestHandler, SolrQueryRequest request, String query) - { - // Getting the doc list is shard-specific, and not really cloud-friendly - - ModifiableSolrParams params = new ModifiableSolrParams(request.getParams()); - // Sets MAX_VALUE to get all the rows - params.set("q", query).set("fl", QueryConstants.FIELD_SOLR4_ID).set("rows", Integer.MAX_VALUE); - ResultContext rc = this.getResultContext(requestHandler, request, params); - return rc != null ? rc.getDocList() : null; - } - - /** - * @param requestHandler the handler that handles the request - * @param request the request object to put the params on - * @param params Solr parameters - * @return the result context from the handled request - */ - ResultContext getResultContext(SolrRequestHandler requestHandler, SolrQueryRequest request, SolrParams params) - { - SolrQueryResponse solrRsp = getResponse(requestHandler, request, params); - ResultContext rc = (ResultContext) solrRsp.getValues().get("response"); - return rc; - } - - /** - * Returns the SolrDocumentList from the response, assuming that the specified request handler uses the - * AlfrescoSearchHandler which puts it on the response values. - * @param requestHandler the handler that handles the request - * @param request the request object to put the params on - * @param params Solr parameters - * @return the result context from the handled request - */ - SolrDocumentList getSolrDocumentList(SolrRequestHandler requestHandler, SolrQueryRequest request, - ModifiableSolrParams params) - { - // Tells the AlfrescoSearchHandler to get the SolrDocumentList from the default results and add to response - params.set(PARAM_GET_SOLR_DOCUMENT_LIST, true); - SolrQueryResponse solrRsp = getResponse(requestHandler, request, params); - SolrDocumentList docs = (SolrDocumentList) solrRsp.getValues().get(RESPONSE_SOLR_DOCUMENT_LIST); - return docs; - } - - /** - * @param requestHandler the handler that handles the request - * @param request the request object to put the params on - * @param params Solr parameters - * @return the response - */ - SolrQueryResponse getResponse(SolrRequestHandler requestHandler, SolrQueryRequest request, SolrParams params) - { - request.setParams(params); - log.info("Running query " + params.get("q")); - SolrQueryResponse solrRsp = new SolrQueryResponse(); - requestHandler.handleRequest(request, solrRsp); - return solrRsp; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import java.util.Collection; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestHandler; +import org.apache.solr.response.ResultContext; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.search.DocList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Cloud consists of support methods for running solr queries in the cloud. + * + * @author Ahmed Owian + */ +public class Cloud +{ + public static final String RESPONSE_SOLR_DOCUMENT_LIST = "responseSolrDocumentList"; + public static final String PARAM_GET_SOLR_DOCUMENT_LIST = "alfresco.getSolrDocumentList"; + protected final static Logger log = LoggerFactory.getLogger(Cloud.class); + + /** + * Creates a query to select docs with any of the specified field values + * @param fieldName the name of the field in the condition + * @param operator currently only OR makes sense. To add AND, fieldName must become a list of fields. + * @param valueLists a bunch of possible values for the field + * @return the resulting query string + */ + @SuppressWarnings("rawtypes") + String getQuery(String fieldName, String operator, Collection... valueLists) + { + StringBuilder query = new StringBuilder(); + for (Collection values : valueLists) + { + for (Object value : values) + { + query.append(fieldName).append(":").append("\"").append(value.toString()).append("\"").append(operator); + } + } + + if (query.length() >= operator.length()) + { + // Removes the last appended operator from the query string + query.delete(query.length() - operator.length(), query.length()); + } + + return query.toString(); + } + + /** + * Returns whether or not a doc exists that satisfies the specified query + * @param requestHandler the handler that handles the request + * @param request the request object to put the query on + * @param query the string that specifies the doc + * @return true if the specified query returns a doc + */ + boolean exists(SolrRequestHandler requestHandler, SolrQueryRequest request, String query) + { + ModifiableSolrParams params = new ModifiableSolrParams(request.getParams()); + // Sets 1 because this is effectively a boolean query to see if there exists a single match + params.set("q", query).set("fl", "id").set("rows", "1"); + ResultContext rc = this.getResultContext(requestHandler, request, params); + + if (rc != null) + { +// TODO Should we use rc.docs.matches() instead? + if (rc.getDocList() != null) { return rc.getDocList().iterator().hasNext(); } + } + + return false; + } + + /** + * Returns the doc list resulting from running the query + * @param requestHandler the handler that handles the request + * @param request the request object to put the query on + * @param query the string that specifies the docs + * @return the docs that come back from the query + */ + DocList getDocList(SolrRequestHandler requestHandler, SolrQueryRequest request, String query) + { + // Getting the doc list is shard-specific, and not really cloud-friendly + + ModifiableSolrParams params = new ModifiableSolrParams(request.getParams()); + // Sets MAX_VALUE to get all the rows + params.set("q", query).set("fl", QueryConstants.FIELD_SOLR4_ID).set("rows", Integer.MAX_VALUE); + ResultContext rc = this.getResultContext(requestHandler, request, params); + return rc != null ? rc.getDocList() : null; + } + + /** + * @param requestHandler the handler that handles the request + * @param request the request object to put the params on + * @param params Solr parameters + * @return the result context from the handled request + */ + ResultContext getResultContext(SolrRequestHandler requestHandler, SolrQueryRequest request, SolrParams params) + { + SolrQueryResponse solrRsp = getResponse(requestHandler, request, params); + ResultContext rc = (ResultContext) solrRsp.getValues().get("response"); + return rc; + } + + /** + * Returns the SolrDocumentList from the response, assuming that the specified request handler uses the + * AlfrescoSearchHandler which puts it on the response values. + * @param requestHandler the handler that handles the request + * @param request the request object to put the params on + * @param params Solr parameters + * @return the result context from the handled request + */ + SolrDocumentList getSolrDocumentList(SolrRequestHandler requestHandler, SolrQueryRequest request, + ModifiableSolrParams params) + { + // Tells the AlfrescoSearchHandler to get the SolrDocumentList from the default results and add to response + params.set(PARAM_GET_SOLR_DOCUMENT_LIST, true); + SolrQueryResponse solrRsp = getResponse(requestHandler, request, params); + SolrDocumentList docs = (SolrDocumentList) solrRsp.getValues().get(RESPONSE_SOLR_DOCUMENT_LIST); + return docs; + } + + /** + * @param requestHandler the handler that handles the request + * @param request the request object to put the params on + * @param params Solr parameters + * @return the response + */ + SolrQueryResponse getResponse(SolrRequestHandler requestHandler, SolrQueryRequest request, SolrParams params) + { + request.setParams(params); + log.info("Running query " + params.get("q")); + SolrQueryResponse solrRsp = new SolrQueryResponse(); + requestHandler.handleRequest(request, solrRsp); + return solrRsp; + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/ContextAwareQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/ContextAwareQuery.java index cad8eb4c2..acc700063 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/ContextAwareQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/ContextAwareQuery.java @@ -1,120 +1,120 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import java.io.IOException; - -import org.alfresco.service.cmr.search.SearchParameters; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - * - */ -public class ContextAwareQuery extends Query -{ - protected final static Logger log = LoggerFactory.getLogger(ContextAwareQuery.class); - - private final Query luceneQuery; - private final SearchParameters searchParameters; - - /** - * @param luceneQuery Query - * @param searchParameters SearchParameters - */ - public ContextAwareQuery(Query luceneQuery, SearchParameters searchParameters) - { - this.luceneQuery = luceneQuery; - this.searchParameters = searchParameters; - } - - /** - * @param field String - * @return String - * @see org.apache.lucene.search.Query#toString(java.lang.String) - */ - public String toString(String field) - { - return luceneQuery.toString(field); - } - - /** - * @param searcher IndexSearcher - * @return Weight - * @throws IOException - * @see org.apache.lucene.search.Query#createWeight(IndexSearcher, boolean) - */ - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - return luceneQuery.createWeight(searcher, needsScore); - } - - - - /** - * @param reader IndexReader - * @return Query - * @throws IOException - * @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) - */ - public Query rewrite(IndexReader reader) throws IOException - { - return luceneQuery.rewrite(reader); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ContextAwareQuery)) return false; - - ContextAwareQuery that = (ContextAwareQuery) o; - - if (luceneQuery != null ? !luceneQuery.equals(that.luceneQuery) : that.luceneQuery != null) return false; - return searchParameters != null ? searchParameters.equals(that.searchParameters) : that.searchParameters == null; - - } - - @Override - public int hashCode() { - int result = luceneQuery != null ? luceneQuery.hashCode() : 0; - result = 31 * result + (searchParameters != null ? searchParameters.hashCode() : 0); - return result; - } - - public Query getLuceneQuery() - { - return luceneQuery; - } - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import java.io.IOException; + +import org.alfresco.service.cmr.search.SearchParameters; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + * + */ +public class ContextAwareQuery extends Query +{ + protected final static Logger log = LoggerFactory.getLogger(ContextAwareQuery.class); + + private final Query luceneQuery; + private final SearchParameters searchParameters; + + /** + * @param luceneQuery Query + * @param searchParameters SearchParameters + */ + public ContextAwareQuery(Query luceneQuery, SearchParameters searchParameters) + { + this.luceneQuery = luceneQuery; + this.searchParameters = searchParameters; + } + + /** + * @param field String + * @return String + * @see org.apache.lucene.search.Query#toString(java.lang.String) + */ + public String toString(String field) + { + return luceneQuery.toString(field); + } + + /** + * @param searcher IndexSearcher + * @return Weight + * @throws IOException + * @see org.apache.lucene.search.Query#createWeight(IndexSearcher, boolean) + */ + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + return luceneQuery.createWeight(searcher, needsScore); + } + + + + /** + * @param reader IndexReader + * @return Query + * @throws IOException + * @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) + */ + public Query rewrite(IndexReader reader) throws IOException + { + return luceneQuery.rewrite(reader); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ContextAwareQuery)) return false; + + ContextAwareQuery that = (ContextAwareQuery) o; + + if (luceneQuery != null ? !luceneQuery.equals(that.luceneQuery) : that.luceneQuery != null) return false; + return searchParameters != null ? searchParameters.equals(that.searchParameters) : that.searchParameters == null; + + } + + @Override + public int hashCode() { + int result = luceneQuery != null ? luceneQuery.hashCode() : 0; + result = 31 * result + (searchParameters != null ? searchParameters.hashCode() : 0); + return result; + } + + public Query getLuceneQuery() + { + return luceneQuery; + } + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/DualPivotQuickSort.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/DualPivotQuickSort.java index 065892cb9..bdf10ddfe 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/DualPivotQuickSort.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/DualPivotQuickSort.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerOfResources.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerOfResources.java index 39f9113b3..005c1d4c6 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerOfResources.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerOfResources.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerReportHelper.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerReportHelper.java index 7f1767315..e30f8b8ce 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerReportHelper.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/HandlerReportHelper.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -562,4 +562,4 @@ class HandlerReportHelper report.add(cname, coreSummary); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/InformationServer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/InformationServer.java index 42b207abc..c23d6ca2e 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/InformationServer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/InformationServer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrInformationServer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrInformationServer.java index b4bb57cd3..cdfb2faeb 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrInformationServer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrInformationServer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -4102,4 +4102,4 @@ public class SolrInformationServer implements InformationServer return empty(); } } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrKeyResourceLoader.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrKeyResourceLoader.java index dd4c0d2ae..47efdec19 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrKeyResourceLoader.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/SolrKeyResourceLoader.java @@ -1,76 +1,76 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -import org.alfresco.encryption.KeyResourceLoader; -import org.apache.solr.core.SolrResourceLoader; - -/** - * Loads encryption key resources from a Solr core installation using a SolrResourceLoader. - * - * @since 4.0 - */ -public class SolrKeyResourceLoader implements KeyResourceLoader -{ - private SolrResourceLoader loader; - - public SolrKeyResourceLoader(SolrResourceLoader loader) - { - this.loader = loader; - } - - @Override - public InputStream getKeyStore(String location) - throws FileNotFoundException - { - try - { - return loader.openResource(location); - } - catch (IOException e) - { - // TODO: SOLR API changes mean that IOException must be handled. - // This may need revisiting. - throw new FileNotFoundException("Caused by " + e.getMessage()); - } - } - - @Override - public Properties loadKeyMetaData(String location) throws IOException - { - Properties p = new Properties(); - InputStream stream = loader.openResource(location); - p.load(stream); - stream.close(); - return p; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import org.alfresco.encryption.KeyResourceLoader; +import org.apache.solr.core.SolrResourceLoader; + +/** + * Loads encryption key resources from a Solr core installation using a SolrResourceLoader. + * + * @since 4.0 + */ +public class SolrKeyResourceLoader implements KeyResourceLoader +{ + private SolrResourceLoader loader; + + public SolrKeyResourceLoader(SolrResourceLoader loader) + { + this.loader = loader; + } + + @Override + public InputStream getKeyStore(String location) + throws FileNotFoundException + { + try + { + return loader.openResource(location); + } + catch (IOException e) + { + // TODO: SOLR API changes mean that IOException must be handled. + // This may need revisiting. + throw new FileNotFoundException("Caused by " + e.getMessage()); + } + } + + @Override + public Properties loadKeyMetaData(String location) throws IOException + { + Properties p = new Properties(); + InputStream stream = loader.openResource(location); + p.load(stream); + stream.close(); + return p; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/StripLocaleStrField.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/StripLocaleStrField.java index 9cb51e668..81c16652c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/StripLocaleStrField.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/StripLocaleStrField.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -48,4 +48,4 @@ public class StripLocaleStrField extends StrField .orElse(null); return super.createFields(field, newValue, boost); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/TrackerStateException.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/TrackerStateException.java index 28945d1ea..1c9c39299 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/TrackerStateException.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/TrackerStateException.java @@ -1,79 +1,79 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import org.alfresco.error.AlfrescoRuntimeException; - -/** - * @author Andy - * - */ -public class TrackerStateException extends AlfrescoRuntimeException -{ - - /** - * - */ - private static final long serialVersionUID = -3030261887767267309L; - - /** - * @param msgId - */ - public TrackerStateException(String msgId) - { - super(msgId); - } - - /** - * @param msgId - * @param msgParams - */ - public TrackerStateException(String msgId, Object[] msgParams) - { - super(msgId, msgParams); - } - - /** - * @param msgId - * @param cause - */ - public TrackerStateException(String msgId, Throwable cause) - { - super(msgId, cause); - } - - /** - * @param msgId - * @param msgParams - * @param cause - */ - public TrackerStateException(String msgId, Object[] msgParams, Throwable cause) - { - super(msgId, msgParams, cause); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * @author Andy + * + */ +public class TrackerStateException extends AlfrescoRuntimeException +{ + + /** + * + */ + private static final long serialVersionUID = -3030261887767267309L; + + /** + * @param msgId + */ + public TrackerStateException(String msgId) + { + super(msgId); + } + + /** + * @param msgId + * @param msgParams + */ + public TrackerStateException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + + /** + * @param msgId + * @param cause + */ + public TrackerStateException(String msgId, Throwable cause) + { + super(msgId, cause); + } + + /** + * @param msgId + * @param msgParams + * @param cause + */ + public TrackerStateException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrOpenBitSetAdapter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrOpenBitSetAdapter.java index 6b6fafdd5..05bf759ac 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrOpenBitSetAdapter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrOpenBitSetAdapter.java @@ -1,77 +1,77 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.adapters; - -import org.apache.lucene.util.LongBitSet; - -/** - * The reason we have this class is so that lucene-free dependent classes can be dependent on IOpenBitSet instead of the - * lucene-version-specific OpenBitSet. - * @author Ahmed Owian - */ -public class SolrOpenBitSetAdapter implements IOpenBitSet -{ - LongBitSet delegate; - - public SolrOpenBitSetAdapter() - { - delegate = new LongBitSet(64); - } - - @Override - public void set(long index) - { - delegate = LongBitSet.ensureCapacity(delegate, index); - delegate.set(index); - } - - @Override - public void or(IOpenBitSet other) - { - delegate.or( ((SolrOpenBitSetAdapter)other).delegate); - } - - @Override - public long nextSetBit(long index) - { - return delegate.nextSetBit(index); - } - - @Override - public long cardinality() - { - return delegate.cardinality(); - } - - @Override - public boolean get(long index) - { - return delegate.get(index); - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.adapters; + +import org.apache.lucene.util.LongBitSet; + +/** + * The reason we have this class is so that lucene-free dependent classes can be dependent on IOpenBitSet instead of the + * lucene-version-specific OpenBitSet. + * @author Ahmed Owian + */ +public class SolrOpenBitSetAdapter implements IOpenBitSet +{ + LongBitSet delegate; + + public SolrOpenBitSetAdapter() + { + delegate = new LongBitSet(64); + } + + @Override + public void set(long index) + { + delegate = LongBitSet.ensureCapacity(delegate, index); + delegate.set(index); + } + + @Override + public void or(IOpenBitSet other) + { + delegate.or( ((SolrOpenBitSetAdapter)other).delegate); + } + + @Override + public long nextSetBit(long index) + { + return delegate.nextSetBit(index); + } + + @Override + public long cardinality() + { + return delegate.cardinality(); + } + + @Override + public boolean get(long index) + { + return delegate.get(index); + } + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrSimpleOrderedMap.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrSimpleOrderedMap.java index b38b9d8fb..c1901afb2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrSimpleOrderedMap.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/adapters/SolrSimpleOrderedMap.java @@ -1,39 +1,39 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.adapters; - -import org.apache.solr.common.util.SimpleOrderedMap; - -public class SolrSimpleOrderedMap extends SimpleOrderedMap implements ISimpleOrderedMap -{ - - /** - * - */ - private static final long serialVersionUID = 2799724619330515239L; - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.adapters; + +import org.apache.solr.common.util.SimpleOrderedMap; + +public class SolrSimpleOrderedMap extends SimpleOrderedMap implements ISimpleOrderedMap +{ + + /** + * + */ + private static final long serialVersionUID = 2799724619330515239L; + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/AuthorityCacheRegenerator.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/AuthorityCacheRegenerator.java index 1e3e44b1a..624a64d08 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/AuthorityCacheRegenerator.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/AuthorityCacheRegenerator.java @@ -1,58 +1,58 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.cache; - -import java.io.IOException; - -import org.apache.lucene.search.Query; -import org.apache.solr.search.CacheRegenerator; -import org.apache.solr.search.SolrCache; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Cache regeneration for AUTHORITY and AUTHSET queries. - * - * @author Matt Ward - */ -public class AuthorityCacheRegenerator implements CacheRegenerator -{ - @SuppressWarnings({ "rawtypes" }) - @Override - public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache, - SolrCache oldCache, Object oldKey, Object oldVal) throws IOException - { - if (oldKey instanceof Query) - { - // The authority cache contains results keyed by SolrAuthorityQuery - // and SolrAuthoritySetQuery. - Query authQuery = (Query) oldKey; - // Execute the query on the new searcher - resulting in cache population as a side-effect. - newSearcher.getDocSet(authQuery); - } - return true; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.cache; + +import java.io.IOException; + +import org.apache.lucene.search.Query; +import org.apache.solr.search.CacheRegenerator; +import org.apache.solr.search.SolrCache; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Cache regeneration for AUTHORITY and AUTHSET queries. + * + * @author Matt Ward + */ +public class AuthorityCacheRegenerator implements CacheRegenerator +{ + @SuppressWarnings({ "rawtypes" }) + @Override + public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache, + SolrCache oldCache, Object oldKey, Object oldVal) throws IOException + { + if (oldKey instanceof Query) + { + // The authority cache contains results keyed by SolrAuthorityQuery + // and SolrAuthoritySetQuery. + Query authQuery = (Query) oldKey; + // Execute the query on the new searcher - resulting in cache population as a side-effect. + newSearcher.getDocSet(authQuery); + } + return true; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/CacheConstants.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/CacheConstants.java index 14d0ca056..fcc882cbe 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/CacheConstants.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/CacheConstants.java @@ -1,48 +1,48 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.cache; - -/** - * Constants for per-searcher cache names and keys. - * - * @author Matt Ward - */ -public class CacheConstants -{ - // Cache names -// public static String ALFRESCO_CACHE = "alfrescoCache"; -// public static String ALFRESCO_ARRAYLIST_CACHE = "alfrescoArrayListCache"; -// public static String ALFRESCO_PATH_CACHE = "alfrescoPathCache"; -// public static String ALFRESCO_READER_TO_ACL_IDS_CACHE = "alfrescoReaderToAclIdsCache"; -// public static String ALFRESCO_DENY_TO_ACL_IDS_CACHE = "alfrescoDenyToAclIdsCache"; - - public final static String ALFRESCO_AUTHORITY_CACHE = "alfrescoAuthorityCache"; - public final static String ALFRESCO_OWNERLOOKUP_CACHE = "alfrescoOwnerCache"; - public final static String ALFRESCO_READER_CACHE = "alfrescoReaderCache"; - public final static String ALFRESCO_DENIED_CACHE = "alfrescoDeniedCache"; - public final static String ALFRESCO_PATH_CACHE = "alfrescoPathCache"; -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.cache; + +/** + * Constants for per-searcher cache names and keys. + * + * @author Matt Ward + */ +public class CacheConstants +{ + // Cache names +// public static String ALFRESCO_CACHE = "alfrescoCache"; +// public static String ALFRESCO_ARRAYLIST_CACHE = "alfrescoArrayListCache"; +// public static String ALFRESCO_PATH_CACHE = "alfrescoPathCache"; +// public static String ALFRESCO_READER_TO_ACL_IDS_CACHE = "alfrescoReaderToAclIdsCache"; +// public static String ALFRESCO_DENY_TO_ACL_IDS_CACHE = "alfrescoDenyToAclIdsCache"; + + public final static String ALFRESCO_AUTHORITY_CACHE = "alfrescoAuthorityCache"; + public final static String ALFRESCO_OWNERLOOKUP_CACHE = "alfrescoOwnerCache"; + public final static String ALFRESCO_READER_CACHE = "alfrescoReaderCache"; + public final static String ALFRESCO_DENIED_CACHE = "alfrescoDeniedCache"; + public final static String ALFRESCO_PATH_CACHE = "alfrescoPathCache"; +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/PathCacheRegenerator.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/PathCacheRegenerator.java index a9d3690b2..795f00b1c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/PathCacheRegenerator.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/cache/PathCacheRegenerator.java @@ -1,59 +1,59 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.cache; - -import java.io.IOException; - -import org.alfresco.solr.query.SolrCachingPathQuery; -import org.alfresco.solr.query.SolrPathQuery; -import org.apache.solr.search.CacheRegenerator; -import org.apache.solr.search.SolrCache; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * {@link CacheRegenerator} for alfrescoPathCache - * - * @author Matt Ward - */ -public class PathCacheRegenerator implements CacheRegenerator -{ - @SuppressWarnings({ "rawtypes" }) - @Override - public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache, - SolrCache oldCache, Object oldKey, Object oldVal) throws IOException - { - if (oldKey instanceof SolrPathQuery) - { - SolrPathQuery pathQuery = (SolrPathQuery) oldKey; - // Re-execute the path query in a cache-aware context - causing new results to be cached. - SolrCachingPathQuery cachingPathQuery = new SolrCachingPathQuery(pathQuery); - newSearcher.getDocSet(cachingPathQuery); - } - return true; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.cache; + +import java.io.IOException; + +import org.alfresco.solr.query.SolrCachingPathQuery; +import org.alfresco.solr.query.SolrPathQuery; +import org.apache.solr.search.CacheRegenerator; +import org.apache.solr.search.SolrCache; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * {@link CacheRegenerator} for alfrescoPathCache + * + * @author Matt Ward + */ +public class PathCacheRegenerator implements CacheRegenerator +{ + @SuppressWarnings({ "rawtypes" }) + @Override + public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache, + SolrCache oldCache, Object oldKey, Object oldVal) throws IOException + { + if (oldKey instanceof SolrPathQuery) + { + SolrPathQuery pathQuery = (SolrPathQuery) oldKey; + // Re-execute the path query in a cache-aware context - causing new results to be cached. + SolrCachingPathQuery cachingPathQuery = new SolrCachingPathQuery(pathQuery); + newSearcher.getDocSet(cachingPathQuery); + } + return true; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ClearLocaleComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ClearLocaleComponent.java index 8710e9969..90d0cef67 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ClearLocaleComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ClearLocaleComponent.java @@ -1,78 +1,78 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component; - -import java.io.IOException; - -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * @author Andy - * - */ -public class ClearLocaleComponent extends SearchComponent -{ - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - // Nothing to do - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void process(ResponseBuilder rb) throws IOException - { - I18NUtil.setLocale(null); - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getDescription() - */ - @Override - public String getDescription() - { - return "clearLocale"; - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getSource() - */ - @Override - public String getSource() - { - return ""; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component; + +import java.io.IOException; + +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + * + */ +public class ClearLocaleComponent extends SearchComponent +{ + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + // Nothing to do + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void process(ResponseBuilder rb) throws IOException + { + I18NUtil.setLocale(null); + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getDescription() + */ + @Override + public String getDescription() + { + return "clearLocale"; + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getSource() + */ + @Override + public String getSource() + { + return ""; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ConsistencyComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ConsistencyComponent.java index 8aea5489c..575e8c90f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ConsistencyComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/ConsistencyComponent.java @@ -1,105 +1,105 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component; - -import org.alfresco.solr.AlfrescoCoreAdminHandler; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.TrackerState; -import org.alfresco.solr.tracker.MetadataTracker; -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.apache.solr.request.SolrQueryRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.util.Date; - -/** - * Adds consitency information to the search results - */ -public class ConsistencyComponent extends SearchComponent -{ - private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - // No preparation required. - } - - @Override - public void finishStage(ResponseBuilder rb) - { - super.finishStage(rb); - if (rb.stage != ResponseBuilder.STAGE_GET_FIELDS) - return; - try { - process(rb); - } catch (IOException e) { - LOG.error(e.getMessage()); - } - } - - @Override - public void process(ResponseBuilder rb) throws IOException - { - SolrQueryRequest req = rb.req; - AlfrescoCoreAdminHandler adminHandler = (AlfrescoCoreAdminHandler) - req.getCore(). - getCoreContainer(). - getMultiCoreHandler(); - - boolean isShard = rb.req.getParams().getBool(ShardParams.IS_SHARD, false); - MetadataTracker metaTrkr = adminHandler.getTrackerRegistry().getTrackerForCore(req.getCore().getName(), MetadataTracker.class); - if(metaTrkr != null && !isShard) - { - TrackerState metadataTrkrState = metaTrkr.getTrackerState(); - - long lastIndexedTx = metadataTrkrState.getLastIndexedTxId(); - long lastIndexTxCommitTime = metadataTrkrState.getLastIndexedTxCommitTime(); - long lastTxIdOnServer = metadataTrkrState.getLastTxIdOnServer(); - long transactionsToDo = lastTxIdOnServer - lastIndexedTx; - if (transactionsToDo < 0) - { - transactionsToDo = 0; - } - rb.rsp.add("lastIndexedTx", lastIndexedTx); - rb.rsp.add("lastIndexedTxTime", lastIndexTxCommitTime); - rb.rsp.add("txRemaining", transactionsToDo); - } - } - - @Override - public String getDescription() - { - return "Adds consitency information to the search results."; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component; + +import org.alfresco.solr.AlfrescoCoreAdminHandler; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.TrackerState; +import org.alfresco.solr.tracker.MetadataTracker; +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.request.SolrQueryRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.Date; + +/** + * Adds consitency information to the search results + */ +public class ConsistencyComponent extends SearchComponent +{ + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + // No preparation required. + } + + @Override + public void finishStage(ResponseBuilder rb) + { + super.finishStage(rb); + if (rb.stage != ResponseBuilder.STAGE_GET_FIELDS) + return; + try { + process(rb); + } catch (IOException e) { + LOG.error(e.getMessage()); + } + } + + @Override + public void process(ResponseBuilder rb) throws IOException + { + SolrQueryRequest req = rb.req; + AlfrescoCoreAdminHandler adminHandler = (AlfrescoCoreAdminHandler) + req.getCore(). + getCoreContainer(). + getMultiCoreHandler(); + + boolean isShard = rb.req.getParams().getBool(ShardParams.IS_SHARD, false); + MetadataTracker metaTrkr = adminHandler.getTrackerRegistry().getTrackerForCore(req.getCore().getName(), MetadataTracker.class); + if(metaTrkr != null && !isShard) + { + TrackerState metadataTrkrState = metaTrkr.getTrackerState(); + + long lastIndexedTx = metadataTrkrState.getLastIndexedTxId(); + long lastIndexTxCommitTime = metadataTrkrState.getLastIndexedTxCommitTime(); + long lastTxIdOnServer = metadataTrkrState.getLastTxIdOnServer(); + long transactionsToDo = lastTxIdOnServer - lastIndexedTx; + if (transactionsToDo < 0) + { + transactionsToDo = 0; + } + rb.rsp.add("lastIndexedTx", lastIndexedTx); + rb.rsp.add("lastIndexedTxTime", lastIndexTxCommitTime); + rb.rsp.add("txRemaining", transactionsToDo); + } + } + + @Override + public String getDescription() + { + return "Adds consitency information to the search results."; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/FingerPrintComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/FingerPrintComponent.java index 28075d370..e96f7ad16 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/FingerPrintComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/FingerPrintComponent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -135,4 +135,4 @@ public class FingerPrintComponent extends SearchComponent { return null; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/QueryLoggingComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/QueryLoggingComponent.java index 7d905bf7d..c6c049d3f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/QueryLoggingComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/QueryLoggingComponent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetCountsComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetCountsComponent.java index bd6f4c7c4..15f0365ab 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetCountsComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetCountsComponent.java @@ -1,279 +1,279 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component; - -import java.io.IOException; -import java.util.Collection; -import java.util.Map; - -import com.google.common.collect.BiMap; - -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; - -/** - * @author Andy - * - */ -public class RewriteFacetCountsComponent extends SearchComponent -{ - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#finishStage(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void finishStage(ResponseBuilder rb) - { - /// wait until after distributed faceting is done - if (!rb.doFacets || rb.stage != ResponseBuilder.STAGE_GET_FIELDS) - { - return; - } - else - { - process(rb); - } - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - // Nothing to do - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void process(ResponseBuilder rb) - { - // rewrite - - rewrite(rb, "_field_mappings_", "facet_counts", "facet_fields"); - rewrite(rb, "_date_mappings_", "facet_counts", "facet_dates"); - rewrite(rb, "_range_mappings_", "facet_counts", "facet_ranges"); - rewrite(rb, "_interval_mappings_", "facet_counts", "facet_intervals"); - - rewrite(rb, "_pivot_mappings_", "facet_counts", "facet_pivot"); - rewritePivotFields(rb, "facet_counts", "facet_pivot"); - - - rewrite(rb, "_stats_field_mappings_", "stats", "stats_fields"); - - copyAnalytics(rb, "facet_counts", "facet_fields"); - - Map mappings = (Map) rb.rsp.getValues().get("_stats_field_mappings_"); - if(mappings != null) - { - for(String key : mappings.keySet()) - { - rewrite(rb, "_stats_facet_mappings_", "stats", "stats_fields", key, "facets"); - } - } - } - - - /** - * @param rb - */ - private void copyAnalytics(ResponseBuilder rb, String ... sections) - { - NamedList found = (NamedList) rb.rsp.getValues(); - for(String section : sections) - { - found = (NamedList)found.get(section); - if(found == null) - { - return; - } - } - - NamedList analytics = (NamedList) rb.rsp.getValues(); - analytics = (NamedList)analytics.get("analytics"); - if(analytics != null) - { - for(int i = 0; i < analytics.size(); i++) - { - String name = analytics.getName(i); - Object value = analytics.getVal(i); - found.add(name, value); - } - } - - - - - - } - - - /** - * Update pivot fields in the response to reference ACS properties rather than Solr fields. - * - * @param rb The response builder. - * @param sections The names of the sections in the response to update. - */ - private void rewritePivotFields(ResponseBuilder rb, String ... sections) - { - Map mappings = (Map) rb.rsp.getValues().get("_pivot_mappings_"); - if(mappings != null) - { - NamedList found = (NamedList) rb.rsp.getValues(); - for(String section : sections) - { - found = (NamedList)found.get(section); - if(found == null) - { - return; - } - } - for(int i = 0; i < found.size(); i++) - { - String pivotName = found.getName(i); - String[] fromParts = pivotName.split(","); - String mapping = mappings.get(pivotName); - String[] toParts = mapping != null ? mapping.split(",") : fromParts; - Collection> current = (Collection>)found.getVal(i); - processPivot(rb, fromParts, toParts, current, 0); - } - } - - } - - private void processPivot(ResponseBuilder rb, String[] fromParts, String[] toParts, Collection> current, int level) - { - for(NamedList entry : current) - { - for(int i = 0; i < entry.size(); i++) - { - String name = entry.getName(i); - if(name.equals("field")) - { - entry.setVal(i, fromParts[level].trim()); - } - else if(name.equals("pivot")) - { - Collection> pivot = (Collection>)entry.getVal(i); - processPivot(rb, fromParts, toParts, pivot, level+1); - } - else if(name.equals("ranges")) - { - SimpleOrderedMap ranges = (SimpleOrderedMap)entry.getVal(i); - processRanges(rb, ranges); - } - else - { - // leave alone - } - } - } - } - - /** - * Update the returned ranges to reference ACS properties rather than Solr fields. - * - * @param rb The response builder. - * @param ranges The ranges to update. - */ - private void processRanges(ResponseBuilder rb, SimpleOrderedMap ranges) { - - BiMap mappings = (BiMap) rb.rsp.getValues().get("_range_mappings_"); - if(mappings != null) - { - updateToACSNaming(mappings, ranges); - } - } - - /** - * Update the returned facet sections to reference ACS properties rather than Solr fields. - * - * @param rb the response builder. - * @param mappingName The name of the mapping definition created in {@link RewriteFacetParametersComponent}. - * @param sections The names of the sections to be updated. - */ - private void rewrite(ResponseBuilder rb, String mappingName, String ... sections) - { - BiMap mappings = (BiMap) rb.rsp.getValues().get(mappingName); - if(mappings != null) - { - NamedList found = (NamedList) rb.rsp.getValues(); - for (String section : sections) - { - found = (NamedList) found.get(section); - if (found == null) - { - return; - } - } - // This found already contains the private buckets - updateToACSNaming(mappings, found); - } - } - - /** - * Update entries in a list to reference ACS properties rather than Solr fields. - * - * @param mappings The mapping from ACS properties to Solr fields. - * @param propertyList The list to update. - */ - private void updateToACSNaming(BiMap mappings, NamedList propertyList) - { - for (int i = 0; i < propertyList.size(); i++) - { - String name = propertyList.getName(i); - String newName = mappings.inverse().get(name); - if (newName != null) - { - propertyList.setName(i, newName); - } - } - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getDescription() - */ - @Override - public String getDescription() - { - return "RewriteFacetCounts"; - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getSource() - */ - @Override - public String getSource() - { - return ""; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +import com.google.common.collect.BiMap; + +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; + +/** + * @author Andy + * + */ +public class RewriteFacetCountsComponent extends SearchComponent +{ + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#finishStage(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void finishStage(ResponseBuilder rb) + { + /// wait until after distributed faceting is done + if (!rb.doFacets || rb.stage != ResponseBuilder.STAGE_GET_FIELDS) + { + return; + } + else + { + process(rb); + } + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + // Nothing to do + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void process(ResponseBuilder rb) + { + // rewrite + + rewrite(rb, "_field_mappings_", "facet_counts", "facet_fields"); + rewrite(rb, "_date_mappings_", "facet_counts", "facet_dates"); + rewrite(rb, "_range_mappings_", "facet_counts", "facet_ranges"); + rewrite(rb, "_interval_mappings_", "facet_counts", "facet_intervals"); + + rewrite(rb, "_pivot_mappings_", "facet_counts", "facet_pivot"); + rewritePivotFields(rb, "facet_counts", "facet_pivot"); + + + rewrite(rb, "_stats_field_mappings_", "stats", "stats_fields"); + + copyAnalytics(rb, "facet_counts", "facet_fields"); + + Map mappings = (Map) rb.rsp.getValues().get("_stats_field_mappings_"); + if(mappings != null) + { + for(String key : mappings.keySet()) + { + rewrite(rb, "_stats_facet_mappings_", "stats", "stats_fields", key, "facets"); + } + } + } + + + /** + * @param rb + */ + private void copyAnalytics(ResponseBuilder rb, String ... sections) + { + NamedList found = (NamedList) rb.rsp.getValues(); + for(String section : sections) + { + found = (NamedList)found.get(section); + if(found == null) + { + return; + } + } + + NamedList analytics = (NamedList) rb.rsp.getValues(); + analytics = (NamedList)analytics.get("analytics"); + if(analytics != null) + { + for(int i = 0; i < analytics.size(); i++) + { + String name = analytics.getName(i); + Object value = analytics.getVal(i); + found.add(name, value); + } + } + + + + + + } + + + /** + * Update pivot fields in the response to reference ACS properties rather than Solr fields. + * + * @param rb The response builder. + * @param sections The names of the sections in the response to update. + */ + private void rewritePivotFields(ResponseBuilder rb, String ... sections) + { + Map mappings = (Map) rb.rsp.getValues().get("_pivot_mappings_"); + if(mappings != null) + { + NamedList found = (NamedList) rb.rsp.getValues(); + for(String section : sections) + { + found = (NamedList)found.get(section); + if(found == null) + { + return; + } + } + for(int i = 0; i < found.size(); i++) + { + String pivotName = found.getName(i); + String[] fromParts = pivotName.split(","); + String mapping = mappings.get(pivotName); + String[] toParts = mapping != null ? mapping.split(",") : fromParts; + Collection> current = (Collection>)found.getVal(i); + processPivot(rb, fromParts, toParts, current, 0); + } + } + + } + + private void processPivot(ResponseBuilder rb, String[] fromParts, String[] toParts, Collection> current, int level) + { + for(NamedList entry : current) + { + for(int i = 0; i < entry.size(); i++) + { + String name = entry.getName(i); + if(name.equals("field")) + { + entry.setVal(i, fromParts[level].trim()); + } + else if(name.equals("pivot")) + { + Collection> pivot = (Collection>)entry.getVal(i); + processPivot(rb, fromParts, toParts, pivot, level+1); + } + else if(name.equals("ranges")) + { + SimpleOrderedMap ranges = (SimpleOrderedMap)entry.getVal(i); + processRanges(rb, ranges); + } + else + { + // leave alone + } + } + } + } + + /** + * Update the returned ranges to reference ACS properties rather than Solr fields. + * + * @param rb The response builder. + * @param ranges The ranges to update. + */ + private void processRanges(ResponseBuilder rb, SimpleOrderedMap ranges) { + + BiMap mappings = (BiMap) rb.rsp.getValues().get("_range_mappings_"); + if(mappings != null) + { + updateToACSNaming(mappings, ranges); + } + } + + /** + * Update the returned facet sections to reference ACS properties rather than Solr fields. + * + * @param rb the response builder. + * @param mappingName The name of the mapping definition created in {@link RewriteFacetParametersComponent}. + * @param sections The names of the sections to be updated. + */ + private void rewrite(ResponseBuilder rb, String mappingName, String ... sections) + { + BiMap mappings = (BiMap) rb.rsp.getValues().get(mappingName); + if(mappings != null) + { + NamedList found = (NamedList) rb.rsp.getValues(); + for (String section : sections) + { + found = (NamedList) found.get(section); + if (found == null) + { + return; + } + } + // This found already contains the private buckets + updateToACSNaming(mappings, found); + } + } + + /** + * Update entries in a list to reference ACS properties rather than Solr fields. + * + * @param mappings The mapping from ACS properties to Solr fields. + * @param propertyList The list to update. + */ + private void updateToACSNaming(BiMap mappings, NamedList propertyList) + { + for (int i = 0; i < propertyList.size(); i++) + { + String name = propertyList.getName(i); + String newName = mappings.inverse().get(name); + if (newName != null) + { + propertyList.setName(i, newName); + } + } + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getDescription() + */ + @Override + public String getDescription() + { + return "RewriteFacetCounts"; + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getSource() + */ + @Override + public String getSource() + { + return ""; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetParametersComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetParametersComponent.java index 1731f6a33..7e556b461 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetParametersComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFacetParametersComponent.java @@ -1,568 +1,568 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.google.common.collect.HashBiMap; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.alfresco.solr.query.MimetypeGroupingQParserPlugin; -import org.apache.commons.lang.StringUtils; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.apache.solr.handler.component.ShardRequest; -import org.apache.solr.request.SolrQueryRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Update facet parameters to reference Solr fields rather than ACS properties. - * - * @author Andy - */ -public class RewriteFacetParametersComponent extends SearchComponent -{ - private static final Logger log = LoggerFactory.getLogger(RewriteFacetParametersComponent.class); - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - SolrQueryRequest req = rb.req; - SolrParams params = req.getParams(); - - ModifiableSolrParams fixed = new ModifiableSolrParams(); - ModifiableSolrParams allParamsWithFix = new ModifiableSolrParams(params); - fixFilterQueries(fixed, params, rb); - fixFacetParams(fixed, params, rb); - fixRows(fixed, params, rb); - - Set fixedParameterNames = fixed.getParameterNames(); - for (String fixedParam : fixedParameterNames) - { - allParamsWithFix.set(fixedParam, fixed.getParams(fixedParam)); - } - - if (allParamsWithFix.get(CommonParams.SORT) != null) - { - allParamsWithFix.remove(CommonParams.RQ); - } - - req.setParams(allParamsWithFix); - } - - /** - * Prevents users from requesting a large number of rows by - * replacing an enormous row value with a maximum value that will - * not cause a run time exception. - * @param fixed - * @param params - * @param rb - */ - private void fixRows(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) - { - String rows = params.get("rows"); - if(rows != null && !rows.isEmpty()) - { - Integer row = Integer.valueOf(rows); - // Avoid +1 in SOLR code which produces null:java.lang.NegativeArraySizeException at at org.apache.lucene.util.PriorityQueue.(PriorityQueue.java:56) - if(row > 1000000) - { - fixed.remove("rows"); - fixed.add("rows", "1000000"); - } - } - } - private void fixFilterQueries(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) - { - for(Iterator it = params.getParameterNamesIterator(); it.hasNext(); /**/) - { - String name = it.next(); - - if(name.equals("fq")) - { - String[] values = params.getParams(name); - if(values != null) - { - String[] fixedValues = new String[values.length]; - for(int i = 0; i < values.length; i++) - { - String value = values[i]; - if(value.startsWith("{!")) - { - fixedValues[i] = value; - } - else - { - if(value.startsWith("contentSize():")) - { - value = "cm:content.size:" + removeQuotes(value.substring("contentSize():".length())); - } - else if(value.startsWith("mimetype():")) - { - value = removeQuotes(value.substring("mimetype():".length())); - ArrayList expand = MimetypeGroupingQParserPlugin.getReverseMappings().get(value); - if(expand == null) - { - value = "cm:content.mimetype:\""+value+"\""; - } - else - { - StringBuilder builder = new StringBuilder(); - builder.append("cm:content.mimetype:("); - for(int j = 0; j < expand.size(); j++) - { - if(j > 0) - { - builder.append(" OR "); - } - builder.append('"'); - builder.append(expand.get(j)); - builder.append('"'); - } - builder.append(')'); - value = builder.toString(); - - } - } - fixedValues[i] = "{!afts}"+value; - } - } - fixed.add(name, fixedValues); - } - - } - } - } - - private String removeQuotes(String quoted) - { - if(quoted.startsWith("\"") && quoted.endsWith("\"")) - { - return quoted.substring(1, quoted.length()-1); - } - else - { - return quoted; - } - } - - - /** - * @param fixed ModifiableSolrParams - * @param params SolrParams - * @param rb ResponseBuilder - * @return SolrParams - */ - private SolrParams fixFacetParams(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) - { - // Use BiMaps so that these can be quickly inverted in RewriteFacetCountsComponent. - Map fieldMappings = HashBiMap.create(); - Map dateMappings = HashBiMap.create(); - Map rangeMappings = HashBiMap.create(); - Map pivotMappings = HashBiMap.create(); - Map intervalMappings = HashBiMap.create(); - - Map statsFieldMappings = HashBiMap.create(); - Map statsFacetMappings = HashBiMap.create(); - - Map functionMappings = HashBiMap.create(); - - rewriteFacetFieldList(fixed, params, "facet.field", fieldMappings, rb.req); - rewriteFacetFieldList(fixed, params, "facet.date", dateMappings, rb.req); - rewriteFacetFieldList(fixed, params, "facet.range", rangeMappings, rb.req); - rewriteFacetFieldList(fixed, params, "facet.pivot", pivotMappings, rb.req); - rewriteFacetFieldList(fixed, params, "facet.interval", intervalMappings, rb.req); - - rewriteFacetFieldList(fixed, params, "stats.field", statsFieldMappings, rb.req); - rewriteFacetFieldList(fixed, params, "stats.facet", statsFacetMappings, rb.req); - - mapFacetFunctions(fixed, params, "facet.field", functionMappings); - - rewriteFacetFieldOptions(fixed, params, "facet.prefix", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.contains", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.contains.ignoreCase", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.sort", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.limit", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.offset", fieldMappings); - rewriteMincountFacetFieldOption(fixed, params, "facet.mincount", fieldMappings,rb.req); - rewriteFacetFieldOptions(fixed, params, "facet.missing", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.method", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.enum.cache.minDF", fieldMappings); - rewriteFacetFieldOptions(fixed, params, "facet.enum.cache.minDF", fieldMappings); - - rewriteFacetFieldOptions(fixed, params, "facet.range.start", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.end", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.gap", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.hardend", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.include", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.other", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.method", rangeMappings); - rewriteFacetFieldOptions(fixed, params, "facet.range.limit", rangeMappings); - - rewriteMincountFacetFieldOption(fixed, params, "facet.pivot.mincount", pivotMappings, rb.req); - rewriteFacetFieldOptions(fixed, params, "facet.sort", pivotMappings); - rewriteFacetFieldOptions(fixed, params, "facet.limit", pivotMappings); - rewriteFacetFieldOptions(fixed, params, "facet.offset", pivotMappings); - - rewriteFacetFieldOptions(fixed, params, "facet.interval.set", intervalMappings); - - - - // TODO: - // f..stats.facet= - // would require a more complex rewrite - - - - rb.rsp.add("_original_parameters_", params); - rb.rsp.add("_field_mappings_", fieldMappings); - rb.rsp.add("_date_mappings_", dateMappings); - rb.rsp.add("_range_mappings_", rangeMappings); - rb.rsp.add("_pivot_mappings_", pivotMappings); - rb.rsp.add("_interval_mappings_", intervalMappings); - rb.rsp.add("_stats_field_mappings_", statsFieldMappings); - rb.rsp.add("_stats_facet_mappings_", statsFacetMappings); - rb.rsp.add("_facet_function_mappings_", functionMappings); - - return fixed; - } - - - /** - * Populate a map with the mappings from functions in the query to functions including the Solr filter handler. - * - * @param fixed The updated params object. - * @param params The original params object. - * @param paramName The name of the parameter (e.g. "facet.field"). - * @param facetFunctionMappings The map to update. - */ - private void mapFacetFunctions(ModifiableSolrParams fixed, SolrParams params, String paramName, Map facetFunctionMappings) - { - String[] facetFieldsOrig = params.getParams(paramName); - if(facetFieldsOrig != null) - { - for(String facetFields : facetFieldsOrig) - { - String[] fields = facetFields.split(","); - - for(String field : fields) - { - field = field.trim(); - if(field.endsWith("()")) - { - if(isMimetypeAndHasFQ(params, field)) - { - String function = "{!" + field.substring(0, field.length()-2)+ " group=false }"; - fixed.add("fq", function); - facetFunctionMappings.put(field, function); - } - else - { - String function = "{!" + field.substring(0, field.length()-2)+ " group=true}"; - fixed.add("fq", function); - facetFunctionMappings.put(field, function); - } - } - } - } - } - } - - - /** - * @param params - * @param field - * @return - */ - private boolean isMimetypeAndHasFQ(SolrParams params, String field) - { - if(!field.equals("mimetype()")) - { - return false; - } - else - { - String[] filterQueries = params.getParams("fq"); - if(filterQueries != null) - { - for(String fq : filterQueries) - { - if(fq.startsWith("mimetype():")) - { - return true; - } - } - } - } - return false; - } - - /** - * Ensure the mincount for all given facets is at least 1 to prevent exposing sensitive buckets to users without permission. - * - * @param fixed The updated params object. - * @param params The original params object. - * @param paramName The name of the mincount parameter to rewrite (e.g. "facet.mincount" or "facet.pivot.mincount"). - * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. - * @param req The Solr request - */ - protected void rewriteMincountFacetFieldOption(ModifiableSolrParams fixed, SolrParams params, String paramName, Map fieldMappings, SolrQueryRequest req) - { - rewriteFacetFieldOptions(fixed, params, paramName, fieldMappings); - - String shardPurpose = req.getParams().get(ShardParams.SHARDS_PURPOSE); - boolean isInitialDistributedRequest = (shardPurpose==null); - /* - * After the initial Search request, in case of Sharding the Solr node will build additional http requests to the other shards. - * These requests may have specific parameters rewritten by Solr internally. - * It's not recommended to rewrite them, because some side effect may happen. - * Only the initial request parameters may be rewritten. - * */ - if(isInitialDistributedRequest) - { - // Update any existing mincount entries for facets. - Map updatedValues = new HashMap<>(); - for (Iterator renamedParameters = fixed.getParameterNamesIterator(); renamedParameters.hasNext(); ) - { - String solrParameter = renamedParameters.next(); - if (solrParameter.endsWith("." + paramName)) - { - // If mincount is set then renamedParameters must be an integer. - int value = Integer.valueOf(fixed.get(solrParameter)); - // Don't directly edit the params while we're iterating through the entries. - updatedValues.put(solrParameter, Math.max(value, 1)); - } - } - // Now we've finished iterating, update the fixed parameters. - for (Map.Entry updatedParameterValues : updatedValues.entrySet()) - { - fixed.set(updatedParameterValues.getKey(), updatedParameterValues.getValue()); - } - - String paramValue = params.get(paramName); - int value = 0; - if (paramValue != null) - { - value = Integer.valueOf(paramValue); - } - fixed.set(paramName, Math.max(value, 1)); - } - } - - /** - * Update any existing facet parameters to use the Solr field names. - * - * @param fixed The updated params object. - * @param params The original params object. - * @param paramName The name of the parameter to rewrite (e.g. "facet.limit" or "f.fieldName.facet.limit"). - * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. - */ - private void rewriteFacetFieldOptions(ModifiableSolrParams fixed, SolrParams params, String paramName, Map fieldMappings) - { - for(Iterator originalParamsIterator = params.getParameterNamesIterator(); originalParamsIterator.hasNext(); /**/) - { - String originalSolrParam = originalParamsIterator.next(); //it contains the alfresco field name - if(originalSolrParam.startsWith("f.")) - { - if(originalSolrParam.endsWith("."+paramName)) - { - String originalAlfrescoFieldName = originalSolrParam.substring(2, originalSolrParam.length() - paramName.length() - 1); - if(fieldMappings.containsKey(originalAlfrescoFieldName)) - { - String solrFieldName = fieldMappings.get(originalAlfrescoFieldName); - fixed.set("f."+ solrFieldName +"."+paramName, params.getParams(originalSolrParam)); - } - else - { - fixed.set(originalSolrParam, params.getParams(originalSolrParam)); - } - } - } - } - } - - /** - * Tokenizes a string based on comma's except for the ones in single or double - * qoutes. - * @param line - * @return - */ - public static String[] parseFacetField(String line) - { - if(StringUtils.isEmpty(line)) - { - throw new RuntimeException("String input is requried"); - } - String[] tokens = line.split(",(?=(?:[^'|\"]*\"[^'|\"]*\")*[^'|\"]*$)", -1); - return tokens; - - } - /** - * Update the facet fields to use Solr field names rather than ACS property names. - * - * @param fixed The updated params object. - * @param params The original params object. - * @param paramName The name of the mincount parameter to rewrite (e.g. "facet.mincount" or "facet.pivot.mincount"). - * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. - * @param req The original request. - * @return An array of the facet field names. - */ - private List rewriteFacetFieldList(ModifiableSolrParams fixed, SolrParams params, String paramName, - Map fieldMappings, SolrQueryRequest req) - { - String shardPurpose = req.getParams().get(ShardParams.SHARDS_PURPOSE); - boolean isRefinementRequest = false; - //Fix for https://issues.alfresco.com/jira/browse/MNT-21015 - if (shardPurpose != null) { - int shardPurposeCode = Integer.parseInt(shardPurpose); - log.debug("ShardPurpose: " + shardPurpose); - isRefinementRequest = ((shardPurposeCode & ShardRequest.PURPOSE_REFINE_FACETS) != 0) || ((shardPurposeCode & ShardRequest.PURPOSE_REFINE_PIVOT_FACETS) != 0); - } - String[] facetFieldsOrig = params.getParams(paramName); - List facetFieldList = new ArrayList<>(); - if(facetFieldsOrig != null) - { - ArrayList newFacetFields = new ArrayList(); - for(String facetFields : facetFieldsOrig) - { - StringBuilder commaSeparated = new StringBuilder(); - StringBuilder mapping = new StringBuilder(); - StringBuilder unmapped = new StringBuilder(); - - String[] fields = parseFacetField(facetFields); - - for(String field : fields) - { - String prefix = ""; - field = field.trim(); - - if(field.endsWith("()")) - { - // skip facet functions - continue; - } - - if(field.startsWith("{!") &&!(isRefinementRequest)) - { - int index = field.indexOf("}"); - if((index > 0) && (index < (field.length() - 1))) - { - prefix = field.substring(0, index+1); - field = field.substring(index+1); - } - } - - boolean noMappingIsRequired = req.getSchema().getFieldOrNull(field) != null|| isRefinementRequest; - if(noMappingIsRequired) - { - if(commaSeparated.length() > 0) - { - commaSeparated.append(","); - mapping.append(","); - unmapped.append(","); - } - commaSeparated.append(prefix).append(field); - mapping.append(field); - unmapped.append(field); - facetFieldList.add(field); - } - else - { - String mappedField = AlfrescoSolrDataModel.getInstance().mapProperty(field, FieldUse.FACET, req); - - if(commaSeparated.length() > 0) - { - commaSeparated.append(","); - mapping.append(","); - unmapped.append(","); - } - commaSeparated.append(prefix).append(mappedField); - mapping.append(mappedField); - unmapped.append(field); - facetFieldList.add(mappedField); - } - } - if(!facetFields.equals(commaSeparated.toString())) - { - fieldMappings.put(unmapped.toString(), mapping.toString()); - } - if(commaSeparated.length() > 0) - { - newFacetFields.add(commaSeparated.toString()); - } - } - fixed.set(paramName, newFacetFields.toArray(new String[newFacetFields.size()])); - } - - return facetFieldList; - } - - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void process(ResponseBuilder rb) throws IOException - { - - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getDescription() - */ - @Override - public String getDescription() - { - return "RewriteFacetParameters"; - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getSource() - */ - @Override - public String getSource() - { - return ""; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.HashBiMap; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.alfresco.solr.query.MimetypeGroupingQParserPlugin; +import org.apache.commons.lang.StringUtils; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.handler.component.ShardRequest; +import org.apache.solr.request.SolrQueryRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Update facet parameters to reference Solr fields rather than ACS properties. + * + * @author Andy + */ +public class RewriteFacetParametersComponent extends SearchComponent +{ + private static final Logger log = LoggerFactory.getLogger(RewriteFacetParametersComponent.class); + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + SolrQueryRequest req = rb.req; + SolrParams params = req.getParams(); + + ModifiableSolrParams fixed = new ModifiableSolrParams(); + ModifiableSolrParams allParamsWithFix = new ModifiableSolrParams(params); + fixFilterQueries(fixed, params, rb); + fixFacetParams(fixed, params, rb); + fixRows(fixed, params, rb); + + Set fixedParameterNames = fixed.getParameterNames(); + for (String fixedParam : fixedParameterNames) + { + allParamsWithFix.set(fixedParam, fixed.getParams(fixedParam)); + } + + if (allParamsWithFix.get(CommonParams.SORT) != null) + { + allParamsWithFix.remove(CommonParams.RQ); + } + + req.setParams(allParamsWithFix); + } + + /** + * Prevents users from requesting a large number of rows by + * replacing an enormous row value with a maximum value that will + * not cause a run time exception. + * @param fixed + * @param params + * @param rb + */ + private void fixRows(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) + { + String rows = params.get("rows"); + if(rows != null && !rows.isEmpty()) + { + Integer row = Integer.valueOf(rows); + // Avoid +1 in SOLR code which produces null:java.lang.NegativeArraySizeException at at org.apache.lucene.util.PriorityQueue.(PriorityQueue.java:56) + if(row > 1000000) + { + fixed.remove("rows"); + fixed.add("rows", "1000000"); + } + } + } + private void fixFilterQueries(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) + { + for(Iterator it = params.getParameterNamesIterator(); it.hasNext(); /**/) + { + String name = it.next(); + + if(name.equals("fq")) + { + String[] values = params.getParams(name); + if(values != null) + { + String[] fixedValues = new String[values.length]; + for(int i = 0; i < values.length; i++) + { + String value = values[i]; + if(value.startsWith("{!")) + { + fixedValues[i] = value; + } + else + { + if(value.startsWith("contentSize():")) + { + value = "cm:content.size:" + removeQuotes(value.substring("contentSize():".length())); + } + else if(value.startsWith("mimetype():")) + { + value = removeQuotes(value.substring("mimetype():".length())); + ArrayList expand = MimetypeGroupingQParserPlugin.getReverseMappings().get(value); + if(expand == null) + { + value = "cm:content.mimetype:\""+value+"\""; + } + else + { + StringBuilder builder = new StringBuilder(); + builder.append("cm:content.mimetype:("); + for(int j = 0; j < expand.size(); j++) + { + if(j > 0) + { + builder.append(" OR "); + } + builder.append('"'); + builder.append(expand.get(j)); + builder.append('"'); + } + builder.append(')'); + value = builder.toString(); + + } + } + fixedValues[i] = "{!afts}"+value; + } + } + fixed.add(name, fixedValues); + } + + } + } + } + + private String removeQuotes(String quoted) + { + if(quoted.startsWith("\"") && quoted.endsWith("\"")) + { + return quoted.substring(1, quoted.length()-1); + } + else + { + return quoted; + } + } + + + /** + * @param fixed ModifiableSolrParams + * @param params SolrParams + * @param rb ResponseBuilder + * @return SolrParams + */ + private SolrParams fixFacetParams(ModifiableSolrParams fixed, SolrParams params, ResponseBuilder rb) + { + // Use BiMaps so that these can be quickly inverted in RewriteFacetCountsComponent. + Map fieldMappings = HashBiMap.create(); + Map dateMappings = HashBiMap.create(); + Map rangeMappings = HashBiMap.create(); + Map pivotMappings = HashBiMap.create(); + Map intervalMappings = HashBiMap.create(); + + Map statsFieldMappings = HashBiMap.create(); + Map statsFacetMappings = HashBiMap.create(); + + Map functionMappings = HashBiMap.create(); + + rewriteFacetFieldList(fixed, params, "facet.field", fieldMappings, rb.req); + rewriteFacetFieldList(fixed, params, "facet.date", dateMappings, rb.req); + rewriteFacetFieldList(fixed, params, "facet.range", rangeMappings, rb.req); + rewriteFacetFieldList(fixed, params, "facet.pivot", pivotMappings, rb.req); + rewriteFacetFieldList(fixed, params, "facet.interval", intervalMappings, rb.req); + + rewriteFacetFieldList(fixed, params, "stats.field", statsFieldMappings, rb.req); + rewriteFacetFieldList(fixed, params, "stats.facet", statsFacetMappings, rb.req); + + mapFacetFunctions(fixed, params, "facet.field", functionMappings); + + rewriteFacetFieldOptions(fixed, params, "facet.prefix", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.contains", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.contains.ignoreCase", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.sort", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.limit", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.offset", fieldMappings); + rewriteMincountFacetFieldOption(fixed, params, "facet.mincount", fieldMappings,rb.req); + rewriteFacetFieldOptions(fixed, params, "facet.missing", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.method", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.enum.cache.minDF", fieldMappings); + rewriteFacetFieldOptions(fixed, params, "facet.enum.cache.minDF", fieldMappings); + + rewriteFacetFieldOptions(fixed, params, "facet.range.start", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.end", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.gap", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.hardend", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.include", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.other", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.method", rangeMappings); + rewriteFacetFieldOptions(fixed, params, "facet.range.limit", rangeMappings); + + rewriteMincountFacetFieldOption(fixed, params, "facet.pivot.mincount", pivotMappings, rb.req); + rewriteFacetFieldOptions(fixed, params, "facet.sort", pivotMappings); + rewriteFacetFieldOptions(fixed, params, "facet.limit", pivotMappings); + rewriteFacetFieldOptions(fixed, params, "facet.offset", pivotMappings); + + rewriteFacetFieldOptions(fixed, params, "facet.interval.set", intervalMappings); + + + + // TODO: + // f..stats.facet= + // would require a more complex rewrite + + + + rb.rsp.add("_original_parameters_", params); + rb.rsp.add("_field_mappings_", fieldMappings); + rb.rsp.add("_date_mappings_", dateMappings); + rb.rsp.add("_range_mappings_", rangeMappings); + rb.rsp.add("_pivot_mappings_", pivotMappings); + rb.rsp.add("_interval_mappings_", intervalMappings); + rb.rsp.add("_stats_field_mappings_", statsFieldMappings); + rb.rsp.add("_stats_facet_mappings_", statsFacetMappings); + rb.rsp.add("_facet_function_mappings_", functionMappings); + + return fixed; + } + + + /** + * Populate a map with the mappings from functions in the query to functions including the Solr filter handler. + * + * @param fixed The updated params object. + * @param params The original params object. + * @param paramName The name of the parameter (e.g. "facet.field"). + * @param facetFunctionMappings The map to update. + */ + private void mapFacetFunctions(ModifiableSolrParams fixed, SolrParams params, String paramName, Map facetFunctionMappings) + { + String[] facetFieldsOrig = params.getParams(paramName); + if(facetFieldsOrig != null) + { + for(String facetFields : facetFieldsOrig) + { + String[] fields = facetFields.split(","); + + for(String field : fields) + { + field = field.trim(); + if(field.endsWith("()")) + { + if(isMimetypeAndHasFQ(params, field)) + { + String function = "{!" + field.substring(0, field.length()-2)+ " group=false }"; + fixed.add("fq", function); + facetFunctionMappings.put(field, function); + } + else + { + String function = "{!" + field.substring(0, field.length()-2)+ " group=true}"; + fixed.add("fq", function); + facetFunctionMappings.put(field, function); + } + } + } + } + } + } + + + /** + * @param params + * @param field + * @return + */ + private boolean isMimetypeAndHasFQ(SolrParams params, String field) + { + if(!field.equals("mimetype()")) + { + return false; + } + else + { + String[] filterQueries = params.getParams("fq"); + if(filterQueries != null) + { + for(String fq : filterQueries) + { + if(fq.startsWith("mimetype():")) + { + return true; + } + } + } + } + return false; + } + + /** + * Ensure the mincount for all given facets is at least 1 to prevent exposing sensitive buckets to users without permission. + * + * @param fixed The updated params object. + * @param params The original params object. + * @param paramName The name of the mincount parameter to rewrite (e.g. "facet.mincount" or "facet.pivot.mincount"). + * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. + * @param req The Solr request + */ + protected void rewriteMincountFacetFieldOption(ModifiableSolrParams fixed, SolrParams params, String paramName, Map fieldMappings, SolrQueryRequest req) + { + rewriteFacetFieldOptions(fixed, params, paramName, fieldMappings); + + String shardPurpose = req.getParams().get(ShardParams.SHARDS_PURPOSE); + boolean isInitialDistributedRequest = (shardPurpose==null); + /* + * After the initial Search request, in case of Sharding the Solr node will build additional http requests to the other shards. + * These requests may have specific parameters rewritten by Solr internally. + * It's not recommended to rewrite them, because some side effect may happen. + * Only the initial request parameters may be rewritten. + * */ + if(isInitialDistributedRequest) + { + // Update any existing mincount entries for facets. + Map updatedValues = new HashMap<>(); + for (Iterator renamedParameters = fixed.getParameterNamesIterator(); renamedParameters.hasNext(); ) + { + String solrParameter = renamedParameters.next(); + if (solrParameter.endsWith("." + paramName)) + { + // If mincount is set then renamedParameters must be an integer. + int value = Integer.valueOf(fixed.get(solrParameter)); + // Don't directly edit the params while we're iterating through the entries. + updatedValues.put(solrParameter, Math.max(value, 1)); + } + } + // Now we've finished iterating, update the fixed parameters. + for (Map.Entry updatedParameterValues : updatedValues.entrySet()) + { + fixed.set(updatedParameterValues.getKey(), updatedParameterValues.getValue()); + } + + String paramValue = params.get(paramName); + int value = 0; + if (paramValue != null) + { + value = Integer.valueOf(paramValue); + } + fixed.set(paramName, Math.max(value, 1)); + } + } + + /** + * Update any existing facet parameters to use the Solr field names. + * + * @param fixed The updated params object. + * @param params The original params object. + * @param paramName The name of the parameter to rewrite (e.g. "facet.limit" or "f.fieldName.facet.limit"). + * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. + */ + private void rewriteFacetFieldOptions(ModifiableSolrParams fixed, SolrParams params, String paramName, Map fieldMappings) + { + for(Iterator originalParamsIterator = params.getParameterNamesIterator(); originalParamsIterator.hasNext(); /**/) + { + String originalSolrParam = originalParamsIterator.next(); //it contains the alfresco field name + if(originalSolrParam.startsWith("f.")) + { + if(originalSolrParam.endsWith("."+paramName)) + { + String originalAlfrescoFieldName = originalSolrParam.substring(2, originalSolrParam.length() - paramName.length() - 1); + if(fieldMappings.containsKey(originalAlfrescoFieldName)) + { + String solrFieldName = fieldMappings.get(originalAlfrescoFieldName); + fixed.set("f."+ solrFieldName +"."+paramName, params.getParams(originalSolrParam)); + } + else + { + fixed.set(originalSolrParam, params.getParams(originalSolrParam)); + } + } + } + } + } + + /** + * Tokenizes a string based on comma's except for the ones in single or double + * qoutes. + * @param line + * @return + */ + public static String[] parseFacetField(String line) + { + if(StringUtils.isEmpty(line)) + { + throw new RuntimeException("String input is requried"); + } + String[] tokens = line.split(",(?=(?:[^'|\"]*\"[^'|\"]*\")*[^'|\"]*$)", -1); + return tokens; + + } + /** + * Update the facet fields to use Solr field names rather than ACS property names. + * + * @param fixed The updated params object. + * @param params The original params object. + * @param paramName The name of the mincount parameter to rewrite (e.g. "facet.mincount" or "facet.pivot.mincount"). + * @param fieldMappings A list of mappings from Alfresco property names to Solr field names. + * @param req The original request. + * @return An array of the facet field names. + */ + private List rewriteFacetFieldList(ModifiableSolrParams fixed, SolrParams params, String paramName, + Map fieldMappings, SolrQueryRequest req) + { + String shardPurpose = req.getParams().get(ShardParams.SHARDS_PURPOSE); + boolean isRefinementRequest = false; + //Fix for https://issues.alfresco.com/jira/browse/MNT-21015 + if (shardPurpose != null) { + int shardPurposeCode = Integer.parseInt(shardPurpose); + log.debug("ShardPurpose: " + shardPurpose); + isRefinementRequest = ((shardPurposeCode & ShardRequest.PURPOSE_REFINE_FACETS) != 0) || ((shardPurposeCode & ShardRequest.PURPOSE_REFINE_PIVOT_FACETS) != 0); + } + String[] facetFieldsOrig = params.getParams(paramName); + List facetFieldList = new ArrayList<>(); + if(facetFieldsOrig != null) + { + ArrayList newFacetFields = new ArrayList(); + for(String facetFields : facetFieldsOrig) + { + StringBuilder commaSeparated = new StringBuilder(); + StringBuilder mapping = new StringBuilder(); + StringBuilder unmapped = new StringBuilder(); + + String[] fields = parseFacetField(facetFields); + + for(String field : fields) + { + String prefix = ""; + field = field.trim(); + + if(field.endsWith("()")) + { + // skip facet functions + continue; + } + + if(field.startsWith("{!") &&!(isRefinementRequest)) + { + int index = field.indexOf("}"); + if((index > 0) && (index < (field.length() - 1))) + { + prefix = field.substring(0, index+1); + field = field.substring(index+1); + } + } + + boolean noMappingIsRequired = req.getSchema().getFieldOrNull(field) != null|| isRefinementRequest; + if(noMappingIsRequired) + { + if(commaSeparated.length() > 0) + { + commaSeparated.append(","); + mapping.append(","); + unmapped.append(","); + } + commaSeparated.append(prefix).append(field); + mapping.append(field); + unmapped.append(field); + facetFieldList.add(field); + } + else + { + String mappedField = AlfrescoSolrDataModel.getInstance().mapProperty(field, FieldUse.FACET, req); + + if(commaSeparated.length() > 0) + { + commaSeparated.append(","); + mapping.append(","); + unmapped.append(","); + } + commaSeparated.append(prefix).append(mappedField); + mapping.append(mappedField); + unmapped.append(field); + facetFieldList.add(mappedField); + } + } + if(!facetFields.equals(commaSeparated.toString())) + { + fieldMappings.put(unmapped.toString(), mapping.toString()); + } + if(commaSeparated.length() > 0) + { + newFacetFields.add(commaSeparated.toString()); + } + } + fixed.set(paramName, newFacetFields.toArray(new String[newFacetFields.size()])); + } + + return facetFieldList; + } + + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void process(ResponseBuilder rb) throws IOException + { + + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getDescription() + */ + @Override + public String getDescription() + { + return "RewriteFacetParameters"; + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getSource() + */ + @Override + public String getSource() + { + return ""; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java index a876bf1c9..345494ec4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/RewriteFieldListComponent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetLocaleComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetLocaleComponent.java index d95301850..672186e40 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetLocaleComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetLocaleComponent.java @@ -1,89 +1,89 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component; - -import java.io.IOException; -import java.util.Locale; - -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.apache.solr.request.SolrQueryRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * @author Andy - * - */ -public class SetLocaleComponent extends SearchComponent -{ - private static final Logger log = LoggerFactory.getLogger(SetLocaleComponent.class); - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - SolrQueryRequest req = rb.req; - SolrParams params = req.getParams(); - String localeStr = params.get("locale"); - Locale locale = I18NUtil.parseLocale(localeStr); - I18NUtil.setLocale(locale); - log.info("Set locale to " + localeStr); - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) - */ - @Override - public void process(ResponseBuilder rb) throws IOException - { - // Nothing to do - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getDescription() - */ - @Override - public String getDescription() - { - return "setLocale"; - } - - /* (non-Javadoc) - * @see org.apache.solr.handler.component.SearchComponent#getSource() - */ - @Override - public String getSource() - { - return ""; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component; + +import java.io.IOException; +import java.util.Locale; + +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.request.SolrQueryRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + * + */ +public class SetLocaleComponent extends SearchComponent +{ + private static final Logger log = LoggerFactory.getLogger(SetLocaleComponent.class); + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + SolrQueryRequest req = rb.req; + SolrParams params = req.getParams(); + String localeStr = params.get("locale"); + Locale locale = I18NUtil.parseLocale(localeStr); + I18NUtil.setLocale(locale); + log.info("Set locale to " + localeStr); + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder) + */ + @Override + public void process(ResponseBuilder rb) throws IOException + { + // Nothing to do + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getDescription() + */ + @Override + public String getDescription() + { + return "setLocale"; + } + + /* (non-Javadoc) + * @see org.apache.solr.handler.component.SearchComponent#getSource() + */ + @Override + public String getSource() + { + return ""; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetProcessedDeniesComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetProcessedDeniesComponent.java index 80df24bc0..addba8b18 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetProcessedDeniesComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/SetProcessedDeniesComponent.java @@ -1,87 +1,87 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component; - -import java.io.IOException; - -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; - -/** - * Sets a boolean flag ("processedDenies") in the JSON response indicating that - * the results (should) have been processed with respect to anyDenyDenies - * (i.e. {@link org.alfresco.solr.query.AbstractQParser} has added the correct clause to the search query). - * - * @author Matt Ward - */ -public class SetProcessedDeniesComponent extends SearchComponent -{ - public static final String PROCESSED_DENIES = "processedDenies"; - - @Override - public void prepare(ResponseBuilder rb) throws IOException - { - // No preparation required - } - - @Override - public void process(ResponseBuilder rb) throws IOException - { - Boolean processedDenies = (Boolean) rb.req.getContext().get(PROCESSED_DENIES); - processedDenies = (processedDenies == null) ? false : processedDenies; - rb.rsp.add(PROCESSED_DENIES, processedDenies); - } - - @Override - public String getDescription() - { - return "Adds the processedDenies boolean flag to the search results."; - } - - @Override - public String getSource() - { - return "http://www.alfresco.com"; - } - - @Override - public String getVersion() - { - return "1.0"; - } - - @Override - public void finishStage(ResponseBuilder rb) { - if (rb.stage != ResponseBuilder.STAGE_GET_FIELDS) { - return; - } - - Boolean processedDenies = (Boolean) rb.req.getContext().get(PROCESSED_DENIES); - processedDenies = (processedDenies == null) ? false : processedDenies; - rb.rsp.add(PROCESSED_DENIES, processedDenies); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component; + +import java.io.IOException; + +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; + +/** + * Sets a boolean flag ("processedDenies") in the JSON response indicating that + * the results (should) have been processed with respect to anyDenyDenies + * (i.e. {@link org.alfresco.solr.query.AbstractQParser} has added the correct clause to the search query). + * + * @author Matt Ward + */ +public class SetProcessedDeniesComponent extends SearchComponent +{ + public static final String PROCESSED_DENIES = "processedDenies"; + + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + // No preparation required + } + + @Override + public void process(ResponseBuilder rb) throws IOException + { + Boolean processedDenies = (Boolean) rb.req.getContext().get(PROCESSED_DENIES); + processedDenies = (processedDenies == null) ? false : processedDenies; + rb.rsp.add(PROCESSED_DENIES, processedDenies); + } + + @Override + public String getDescription() + { + return "Adds the processedDenies boolean flag to the search results."; + } + + @Override + public String getSource() + { + return "http://www.alfresco.com"; + } + + @Override + public String getVersion() + { + return "1.0"; + } + + @Override + public void finishStage(ResponseBuilder rb) { + if (rb.stage != ResponseBuilder.STAGE_GET_FIELDS) { + return; + } + + Boolean processedDenies = (Boolean) rb.req.getContext().get(PROCESSED_DENIES); + processedDenies = (processedDenies == null) ? false : processedDenies; + rb.rsp.add(PROCESSED_DENIES, processedDenies); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/TempFileWarningLogger.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/TempFileWarningLogger.java index 1c69ba762..21a4a8cfa 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/TempFileWarningLogger.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/TempFileWarningLogger.java @@ -1,109 +1,109 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component; - -import java.io.IOException; - -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; - -import org.slf4j.Logger; -import org.springframework.util.StringUtils; - -import edu.umd.cs.findbugs.annotations.SuppressWarnings; - -/** - * Temp files may take up a lot of space, warn administrators - * of their existence, giving them the chance to manage them. - * - * @author Matt Ward - */ -public class TempFileWarningLogger -{ - private final Logger log; - private final Path dir; - private final String glob; - - public TempFileWarningLogger(Logger log, String prefix, String[] extensions, Path dir) - { - this.log = log; - this.dir = dir; - // Match files with glob, e.g. "SomePrefix*.{tmp,~bak}" - glob = prefix + ".{"+ StringUtils.arrayToCommaDelimitedString(extensions) + "}"; - } - - // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) - @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") - public boolean checkFiles() - { - if (log.isDebugEnabled()) - { - log.debug("Looking for temp files matching " + glob + " in directory " + dir); - } - - try(DirectoryStream stream = Files.newDirectoryStream(dir, glob)) - { - for (Path file : stream) - { - if (log.isDebugEnabled()) - { - log.debug("Solr suggester temp file found matching file pattern: " + glob + ", path: " + file); - log.debug("Removing suggester temp files."); - } - return true; - } - return false; - } - catch (IOException e) - { - throw new RuntimeException("Unable to create directory stream", e); - } - } - - // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) - @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") - public void removeFiles() - { - try(DirectoryStream stream = Files.newDirectoryStream(dir, glob)) - { - for (Path file : stream) - { - file.toFile().delete(); - } - } - catch (IOException e) - { - log.debug("Unable to delete temp file", e); - } - } - - protected String getGlob() - { - return glob; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component; + +import java.io.IOException; + +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.slf4j.Logger; +import org.springframework.util.StringUtils; + +import edu.umd.cs.findbugs.annotations.SuppressWarnings; + +/** + * Temp files may take up a lot of space, warn administrators + * of their existence, giving them the chance to manage them. + * + * @author Matt Ward + */ +public class TempFileWarningLogger +{ + private final Logger log; + private final Path dir; + private final String glob; + + public TempFileWarningLogger(Logger log, String prefix, String[] extensions, Path dir) + { + this.log = log; + this.dir = dir; + // Match files with glob, e.g. "SomePrefix*.{tmp,~bak}" + glob = prefix + ".{"+ StringUtils.arrayToCommaDelimitedString(extensions) + "}"; + } + + // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) + @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") + public boolean checkFiles() + { + if (log.isDebugEnabled()) + { + log.debug("Looking for temp files matching " + glob + " in directory " + dir); + } + + try(DirectoryStream stream = Files.newDirectoryStream(dir, glob)) + { + for (Path file : stream) + { + if (log.isDebugEnabled()) + { + log.debug("Solr suggester temp file found matching file pattern: " + glob + ", path: " + file); + log.debug("Removing suggester temp files."); + } + return true; + } + return false; + } + catch (IOException e) + { + throw new RuntimeException("Unable to create directory stream", e); + } + } + + // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) + @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") + public void removeFiles() + { + try(DirectoryStream stream = Files.newDirectoryStream(dir, glob)) + { + for (Path file : stream) + { + file.toFile().delete(); + } + } + catch (IOException e) + { + log.debug("Unable to delete temp file", e); + } + } + + protected String getGlob() + { + return glob; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckBackCompatComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckBackCompatComponent.java index a60658681..ba7531da1 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckBackCompatComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckBackCompatComponent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollation.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollation.java index f17cc57a9..eee5b97df 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollation.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollation.java @@ -1,55 +1,55 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component.spellcheck; - -import org.apache.solr.spelling.SpellCheckCollation; - -/** - * @author Jamal Kaabi-Mofrad - * @since 5.0 - */ -public class AlfrescoSpellCheckCollation extends SpellCheckCollation -{ - private String collationQueryString; - - /** - * @return the collationQueryString - */ - public String getCollationQueryString() - { - return this.collationQueryString; - } - - /** - * @param collationQueryString the collationQueryString to set - */ - public void setCollationQueryString(String collationQueryString) - { - this.collationQueryString = collationQueryString; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component.spellcheck; + +import org.apache.solr.spelling.SpellCheckCollation; + +/** + * @author Jamal Kaabi-Mofrad + * @since 5.0 + */ +public class AlfrescoSpellCheckCollation extends SpellCheckCollation +{ + private String collationQueryString; + + /** + * @return the collationQueryString + */ + public String getCollationQueryString() + { + return this.collationQueryString; + } + + /** + * @param collationQueryString the collationQueryString to set + */ + public void setCollationQueryString(String collationQueryString) + { + this.collationQueryString = collationQueryString; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollator.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollator.java index 38bd8171a..a6b277433 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollator.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckCollator.java @@ -1,369 +1,369 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component.spellcheck; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.alfresco.solr.query.AbstractQParser; -import org.apache.lucene.analysis.Token; -import org.apache.lucene.index.IndexReader; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.DisMaxParams; -import org.apache.solr.common.params.GroupParams; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.params.SpellingParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.handler.component.QueryComponent; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SearchComponent; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.search.EarlyTerminatingCollectorException; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.spelling.PossibilityIterator; -import org.apache.solr.spelling.QueryConverter; -import org.apache.solr.spelling.SpellCheckCorrection; -import org.apache.solr.spelling.SpellingResult; -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Disclaimer: The code copied from - * {@link org.apache.solr.spelling.SpellCheckCollator} but only modified the - * {@link org.apache.solr.spelling.SpellCheckCollator#collate(SpellingResult, String, ResponseBuilder)} - * method to pass the suggested term to the original alfresco JSON request rather - * than via 'q' parameter. - * - * @author Jamal Kaabi-Mofrad - * @since 5.0 - */ -public class AlfrescoSpellCheckCollator -{ - private final static Logger LOG = LoggerFactory.getLogger(AlfrescoSpellCheckCollator.class); - - private int maxCollations = 1; - private int maxCollationTries = 0; - private int maxCollationEvaluations = 10000; - private boolean suggestionsMayOverlap = false; - private int docCollectionLimit = 0; - - public List collate(SpellingResult result, String originalQuery, - ResponseBuilder ultimateResponse) - { - List collations = new ArrayList<>(); - - QueryComponent queryComponent = null; - if (ultimateResponse.components != null) - { - for (SearchComponent sc : ultimateResponse.components) - { - if (sc instanceof QueryComponent) - { - queryComponent = (QueryComponent) sc; - break; - } - } - } - - boolean verifyCandidateWithQuery = true; - int maxTries = maxCollationTries; - int maxNumberToIterate = maxTries; - if (maxTries < 1) - { - maxTries = 1; - maxNumberToIterate = maxCollations; - verifyCandidateWithQuery = false; - } - if (queryComponent == null && verifyCandidateWithQuery) - { - LOG.info("Could not find an instance of QueryComponent. Disabling collation verification against the index."); - maxTries = 1; - verifyCandidateWithQuery = false; - } - docCollectionLimit = docCollectionLimit > 0 ? docCollectionLimit : 0; - int maxDocId = -1; - if (verifyCandidateWithQuery && docCollectionLimit > 0) - { - IndexReader reader = ultimateResponse.req.getSearcher().getIndexReader(); - maxDocId = reader.maxDoc(); - } - - JSONObject alfrescoJSON = (JSONObject) ultimateResponse.req.getContext().get(AbstractQParser.ALFRESCO_JSON); - String originalAftsQuery = alfrescoJSON != null? alfrescoJSON.getString("query") : ultimateResponse.getQueryString(); - - - int tryNo = 0; - int collNo = 0; - PossibilityIterator possibilityIter = new PossibilityIterator(result.getSuggestions(), maxNumberToIterate, - maxCollationEvaluations, suggestionsMayOverlap); - while (tryNo < maxTries && collNo < maxCollations && possibilityIter.hasNext()) - { - PossibilityIterator.RankedSpellPossibility possibility = possibilityIter.next(); - String collationQueryStr = getCollation(originalQuery, possibility.corrections); - int hits = 0; - String aftsQuery = null; - - if (verifyCandidateWithQuery) - { - tryNo++; - SolrQueryRequest req = ultimateResponse.req; - SolrParams origParams = req.getParams(); - ModifiableSolrParams params = new ModifiableSolrParams(origParams); - Iterator origParamIterator = origParams.getParameterNamesIterator(); - int pl = SpellingParams.SPELLCHECK_COLLATE_PARAM_OVERRIDE.length(); - while (origParamIterator.hasNext()) - { - String origParamName = origParamIterator.next(); - if (origParamName.startsWith(SpellingParams.SPELLCHECK_COLLATE_PARAM_OVERRIDE) - && origParamName.length() > pl) - { - String[] val = origParams.getParams(origParamName); - if (val.length == 1 && val[0].length() == 0) - { - params.set(origParamName.substring(pl), (String[]) null); - } - else - { - params.set(origParamName.substring(pl), val); - } - } - } - // we don't set the 'q' param, as we'll pass the query via JSON. - // params.set(CommonParams.Q, collationQueryStr); - params.remove(CommonParams.START); - params.set(CommonParams.ROWS, "" + docCollectionLimit); - // we don't want any stored fields - params.set(CommonParams.FL, "id"); - // we'll sort by doc id to ensure no scoring is done. - params.set(CommonParams.SORT, "_docid_ asc"); - // If a dismax query, don't add unnecessary clauses for scoring - params.remove(DisMaxParams.TIE); - params.remove(DisMaxParams.PF); - params.remove(DisMaxParams.PF2); - params.remove(DisMaxParams.PF3); - params.remove(DisMaxParams.BQ); - params.remove(DisMaxParams.BF); - // Collate testing does not support Grouping (see SOLR-2577) - params.remove(GroupParams.GROUP); - - boolean useQStr = true; - - if (alfrescoJSON != null) - { - try - { - aftsQuery = originalAftsQuery.replaceAll(Pattern.quote(originalQuery), Matcher.quoteReplacement(collationQueryStr)); - alfrescoJSON.put("query", aftsQuery); - req.getContext().put(AbstractQParser.ALFRESCO_JSON, alfrescoJSON); - useQStr = false; - } - catch (JSONException e) - { - LOG.warn("Exception trying to get/set the query from/to ALFRESCO_JSON.]" + e); - } - } - else - { - aftsQuery = collationQueryStr; - } - req.setParams(params); - // creating a request here... make sure to close it! - ResponseBuilder checkResponse = new ResponseBuilder(req, new SolrQueryResponse(), - Arrays. asList(queryComponent)); - checkResponse.setQparser(ultimateResponse.getQparser()); - checkResponse.setFilters(ultimateResponse.getFilters()); - checkResponse.components = Arrays. asList(queryComponent); - if (useQStr) - { - checkResponse.setQueryString(collationQueryStr); - } - try - { - queryComponent.prepare(checkResponse); - if (docCollectionLimit > 0) - { - int f = checkResponse.getFieldFlags(); - checkResponse.setFieldFlags(f |= SolrIndexSearcher.TERMINATE_EARLY); - } - queryComponent.process(checkResponse); - hits = (Integer) checkResponse.rsp.getToLog().get("hits"); - } - catch (EarlyTerminatingCollectorException etce) - { - assert (docCollectionLimit > 0); - assert 0 < etce.getNumberScanned(); - assert 0 < etce.getNumberCollected(); - - if (etce.getNumberScanned() == maxDocId) - { - hits = etce.getNumberCollected(); - } - else - { - hits = (int) (((float) (maxDocId * etce.getNumberCollected())) / (float) etce - .getNumberScanned()); - } - } - catch (Exception e) - { - LOG.warn("Exception trying to re-query to check if a spell check possibility would return any hits." + e); - } - finally - { - checkResponse.req.close(); - } - } - if (hits > 0 || !verifyCandidateWithQuery) - { - collNo++; - AlfrescoSpellCheckCollation collation = new AlfrescoSpellCheckCollation(); - collation.setCollationQuery(aftsQuery); - collation.setCollationQueryString(collationQueryStr); - collation.setHits(hits); - collation.setInternalRank(suggestionsMayOverlap ? ((possibility.rank * 1000) + possibility.index) - : possibility.rank); - - NamedList misspellingsAndCorrections = new NamedList<>(); - for (SpellCheckCorrection corr : possibility.corrections) - { - misspellingsAndCorrections.add(corr.getOriginal().toString(), corr.getCorrection()); - } - collation.setMisspellingsAndCorrections(misspellingsAndCorrections); - collations.add(collation); - } - if (LOG.isDebugEnabled()) - { - LOG.debug("Collation: " + aftsQuery + (verifyCandidateWithQuery ? (" will return " + hits + " hits.") : "")); - } - } - return collations; - } - - @SuppressWarnings("deprecation") - private String getCollation(String origQuery, List corrections) - { - StringBuilder collation = new StringBuilder(origQuery); - int offset = 0; - String corr = ""; - for (int i = 0; i < corrections.size(); i++) - { - SpellCheckCorrection correction = corrections.get(i); - Token tok = correction.getOriginal(); - // we are replacing the query in order, but injected terms might - // cause illegal offsets due to previous replacements. - if (tok.getPositionIncrement() == 0) - continue; - corr = correction.getCorrection(); - boolean addParenthesis = false; - Character requiredOrProhibited = null; - int indexOfSpace = corr.indexOf(' '); - StringBuilder corrSb = new StringBuilder(corr); - int bump = 1; - - // If the correction contains whitespace (because it involved - // breaking a word in 2+ words), - // then be sure all of the new words have the same - // optional/required/prohibited status in the query. - while (indexOfSpace > -1 && indexOfSpace < corr.length() - 1) - { - addParenthesis = true; - char previousChar = tok.startOffset() > 0 ? origQuery.charAt(tok.startOffset() - 1) : ' '; - if (previousChar == '-' || previousChar == '+') - { - corrSb.insert(indexOfSpace + bump, previousChar); - if (requiredOrProhibited == null) - { - requiredOrProhibited = previousChar; - } - bump++; - } - else if ((tok.getFlags() & QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG) == QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG) - { - corrSb.insert(indexOfSpace + bump, "AND "); - bump += 4; - } - indexOfSpace = correction.getCorrection().indexOf(' ', indexOfSpace + bump); - } - - int oneForReqOrProhib = 0; - if (addParenthesis) - { - if (requiredOrProhibited != null) - { - corrSb.insert(0, requiredOrProhibited); - oneForReqOrProhib++; - } - corrSb.insert(0, '('); - corrSb.append(')'); - } - corr = corrSb.toString(); - int startIndex = tok.startOffset() + offset - oneForReqOrProhib; - int endIndex = tok.endOffset() + offset; - collation.replace(startIndex, endIndex, corr); - offset += corr.length() - oneForReqOrProhib - (tok.endOffset() - tok.startOffset()); - } - return collation.toString(); - } - - public AlfrescoSpellCheckCollator setMaxCollations(int maxCollations) - { - this.maxCollations = maxCollations; - return this; - } - - public AlfrescoSpellCheckCollator setMaxCollationTries(int maxCollationTries) - { - this.maxCollationTries = maxCollationTries; - return this; - } - - public AlfrescoSpellCheckCollator setMaxCollationEvaluations(int maxCollationEvaluations) - { - this.maxCollationEvaluations = maxCollationEvaluations; - return this; - } - - public AlfrescoSpellCheckCollator setSuggestionsMayOverlap(boolean suggestionsMayOverlap) - { - this.suggestionsMayOverlap = suggestionsMayOverlap; - return this; - } - - public AlfrescoSpellCheckCollator setDocCollectionLimit(int docCollectionLimit) - { - this.docCollectionLimit = docCollectionLimit; - return this; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component.spellcheck; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.alfresco.solr.query.AbstractQParser; +import org.apache.lucene.analysis.Token; +import org.apache.lucene.index.IndexReader; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.DisMaxParams; +import org.apache.solr.common.params.GroupParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.params.SpellingParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.component.QueryComponent; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.search.EarlyTerminatingCollectorException; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.spelling.PossibilityIterator; +import org.apache.solr.spelling.QueryConverter; +import org.apache.solr.spelling.SpellCheckCorrection; +import org.apache.solr.spelling.SpellingResult; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Disclaimer: The code copied from + * {@link org.apache.solr.spelling.SpellCheckCollator} but only modified the + * {@link org.apache.solr.spelling.SpellCheckCollator#collate(SpellingResult, String, ResponseBuilder)} + * method to pass the suggested term to the original alfresco JSON request rather + * than via 'q' parameter. + * + * @author Jamal Kaabi-Mofrad + * @since 5.0 + */ +public class AlfrescoSpellCheckCollator +{ + private final static Logger LOG = LoggerFactory.getLogger(AlfrescoSpellCheckCollator.class); + + private int maxCollations = 1; + private int maxCollationTries = 0; + private int maxCollationEvaluations = 10000; + private boolean suggestionsMayOverlap = false; + private int docCollectionLimit = 0; + + public List collate(SpellingResult result, String originalQuery, + ResponseBuilder ultimateResponse) + { + List collations = new ArrayList<>(); + + QueryComponent queryComponent = null; + if (ultimateResponse.components != null) + { + for (SearchComponent sc : ultimateResponse.components) + { + if (sc instanceof QueryComponent) + { + queryComponent = (QueryComponent) sc; + break; + } + } + } + + boolean verifyCandidateWithQuery = true; + int maxTries = maxCollationTries; + int maxNumberToIterate = maxTries; + if (maxTries < 1) + { + maxTries = 1; + maxNumberToIterate = maxCollations; + verifyCandidateWithQuery = false; + } + if (queryComponent == null && verifyCandidateWithQuery) + { + LOG.info("Could not find an instance of QueryComponent. Disabling collation verification against the index."); + maxTries = 1; + verifyCandidateWithQuery = false; + } + docCollectionLimit = docCollectionLimit > 0 ? docCollectionLimit : 0; + int maxDocId = -1; + if (verifyCandidateWithQuery && docCollectionLimit > 0) + { + IndexReader reader = ultimateResponse.req.getSearcher().getIndexReader(); + maxDocId = reader.maxDoc(); + } + + JSONObject alfrescoJSON = (JSONObject) ultimateResponse.req.getContext().get(AbstractQParser.ALFRESCO_JSON); + String originalAftsQuery = alfrescoJSON != null? alfrescoJSON.getString("query") : ultimateResponse.getQueryString(); + + + int tryNo = 0; + int collNo = 0; + PossibilityIterator possibilityIter = new PossibilityIterator(result.getSuggestions(), maxNumberToIterate, + maxCollationEvaluations, suggestionsMayOverlap); + while (tryNo < maxTries && collNo < maxCollations && possibilityIter.hasNext()) + { + PossibilityIterator.RankedSpellPossibility possibility = possibilityIter.next(); + String collationQueryStr = getCollation(originalQuery, possibility.corrections); + int hits = 0; + String aftsQuery = null; + + if (verifyCandidateWithQuery) + { + tryNo++; + SolrQueryRequest req = ultimateResponse.req; + SolrParams origParams = req.getParams(); + ModifiableSolrParams params = new ModifiableSolrParams(origParams); + Iterator origParamIterator = origParams.getParameterNamesIterator(); + int pl = SpellingParams.SPELLCHECK_COLLATE_PARAM_OVERRIDE.length(); + while (origParamIterator.hasNext()) + { + String origParamName = origParamIterator.next(); + if (origParamName.startsWith(SpellingParams.SPELLCHECK_COLLATE_PARAM_OVERRIDE) + && origParamName.length() > pl) + { + String[] val = origParams.getParams(origParamName); + if (val.length == 1 && val[0].length() == 0) + { + params.set(origParamName.substring(pl), (String[]) null); + } + else + { + params.set(origParamName.substring(pl), val); + } + } + } + // we don't set the 'q' param, as we'll pass the query via JSON. + // params.set(CommonParams.Q, collationQueryStr); + params.remove(CommonParams.START); + params.set(CommonParams.ROWS, "" + docCollectionLimit); + // we don't want any stored fields + params.set(CommonParams.FL, "id"); + // we'll sort by doc id to ensure no scoring is done. + params.set(CommonParams.SORT, "_docid_ asc"); + // If a dismax query, don't add unnecessary clauses for scoring + params.remove(DisMaxParams.TIE); + params.remove(DisMaxParams.PF); + params.remove(DisMaxParams.PF2); + params.remove(DisMaxParams.PF3); + params.remove(DisMaxParams.BQ); + params.remove(DisMaxParams.BF); + // Collate testing does not support Grouping (see SOLR-2577) + params.remove(GroupParams.GROUP); + + boolean useQStr = true; + + if (alfrescoJSON != null) + { + try + { + aftsQuery = originalAftsQuery.replaceAll(Pattern.quote(originalQuery), Matcher.quoteReplacement(collationQueryStr)); + alfrescoJSON.put("query", aftsQuery); + req.getContext().put(AbstractQParser.ALFRESCO_JSON, alfrescoJSON); + useQStr = false; + } + catch (JSONException e) + { + LOG.warn("Exception trying to get/set the query from/to ALFRESCO_JSON.]" + e); + } + } + else + { + aftsQuery = collationQueryStr; + } + req.setParams(params); + // creating a request here... make sure to close it! + ResponseBuilder checkResponse = new ResponseBuilder(req, new SolrQueryResponse(), + Arrays. asList(queryComponent)); + checkResponse.setQparser(ultimateResponse.getQparser()); + checkResponse.setFilters(ultimateResponse.getFilters()); + checkResponse.components = Arrays. asList(queryComponent); + if (useQStr) + { + checkResponse.setQueryString(collationQueryStr); + } + try + { + queryComponent.prepare(checkResponse); + if (docCollectionLimit > 0) + { + int f = checkResponse.getFieldFlags(); + checkResponse.setFieldFlags(f |= SolrIndexSearcher.TERMINATE_EARLY); + } + queryComponent.process(checkResponse); + hits = (Integer) checkResponse.rsp.getToLog().get("hits"); + } + catch (EarlyTerminatingCollectorException etce) + { + assert (docCollectionLimit > 0); + assert 0 < etce.getNumberScanned(); + assert 0 < etce.getNumberCollected(); + + if (etce.getNumberScanned() == maxDocId) + { + hits = etce.getNumberCollected(); + } + else + { + hits = (int) (((float) (maxDocId * etce.getNumberCollected())) / (float) etce + .getNumberScanned()); + } + } + catch (Exception e) + { + LOG.warn("Exception trying to re-query to check if a spell check possibility would return any hits." + e); + } + finally + { + checkResponse.req.close(); + } + } + if (hits > 0 || !verifyCandidateWithQuery) + { + collNo++; + AlfrescoSpellCheckCollation collation = new AlfrescoSpellCheckCollation(); + collation.setCollationQuery(aftsQuery); + collation.setCollationQueryString(collationQueryStr); + collation.setHits(hits); + collation.setInternalRank(suggestionsMayOverlap ? ((possibility.rank * 1000) + possibility.index) + : possibility.rank); + + NamedList misspellingsAndCorrections = new NamedList<>(); + for (SpellCheckCorrection corr : possibility.corrections) + { + misspellingsAndCorrections.add(corr.getOriginal().toString(), corr.getCorrection()); + } + collation.setMisspellingsAndCorrections(misspellingsAndCorrections); + collations.add(collation); + } + if (LOG.isDebugEnabled()) + { + LOG.debug("Collation: " + aftsQuery + (verifyCandidateWithQuery ? (" will return " + hits + " hits.") : "")); + } + } + return collations; + } + + @SuppressWarnings("deprecation") + private String getCollation(String origQuery, List corrections) + { + StringBuilder collation = new StringBuilder(origQuery); + int offset = 0; + String corr = ""; + for (int i = 0; i < corrections.size(); i++) + { + SpellCheckCorrection correction = corrections.get(i); + Token tok = correction.getOriginal(); + // we are replacing the query in order, but injected terms might + // cause illegal offsets due to previous replacements. + if (tok.getPositionIncrement() == 0) + continue; + corr = correction.getCorrection(); + boolean addParenthesis = false; + Character requiredOrProhibited = null; + int indexOfSpace = corr.indexOf(' '); + StringBuilder corrSb = new StringBuilder(corr); + int bump = 1; + + // If the correction contains whitespace (because it involved + // breaking a word in 2+ words), + // then be sure all of the new words have the same + // optional/required/prohibited status in the query. + while (indexOfSpace > -1 && indexOfSpace < corr.length() - 1) + { + addParenthesis = true; + char previousChar = tok.startOffset() > 0 ? origQuery.charAt(tok.startOffset() - 1) : ' '; + if (previousChar == '-' || previousChar == '+') + { + corrSb.insert(indexOfSpace + bump, previousChar); + if (requiredOrProhibited == null) + { + requiredOrProhibited = previousChar; + } + bump++; + } + else if ((tok.getFlags() & QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG) == QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG) + { + corrSb.insert(indexOfSpace + bump, "AND "); + bump += 4; + } + indexOfSpace = correction.getCorrection().indexOf(' ', indexOfSpace + bump); + } + + int oneForReqOrProhib = 0; + if (addParenthesis) + { + if (requiredOrProhibited != null) + { + corrSb.insert(0, requiredOrProhibited); + oneForReqOrProhib++; + } + corrSb.insert(0, '('); + corrSb.append(')'); + } + corr = corrSb.toString(); + int startIndex = tok.startOffset() + offset - oneForReqOrProhib; + int endIndex = tok.endOffset() + offset; + collation.replace(startIndex, endIndex, corr); + offset += corr.length() - oneForReqOrProhib - (tok.endOffset() - tok.startOffset()); + } + return collation.toString(); + } + + public AlfrescoSpellCheckCollator setMaxCollations(int maxCollations) + { + this.maxCollations = maxCollations; + return this; + } + + public AlfrescoSpellCheckCollator setMaxCollationTries(int maxCollationTries) + { + this.maxCollationTries = maxCollationTries; + return this; + } + + public AlfrescoSpellCheckCollator setMaxCollationEvaluations(int maxCollationEvaluations) + { + this.maxCollationEvaluations = maxCollationEvaluations; + return this; + } + + public AlfrescoSpellCheckCollator setSuggestionsMayOverlap(boolean suggestionsMayOverlap) + { + this.suggestionsMayOverlap = suggestionsMayOverlap; + return this; + } + + public AlfrescoSpellCheckCollator setDocCollectionLimit(int docCollectionLimit) + { + this.docCollectionLimit = docCollectionLimit; + return this; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckComponent.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckComponent.java index 43d7f209e..2bfe76ed2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckComponent.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/component/spellcheck/AlfrescoSpellCheckComponent.java @@ -1,116 +1,116 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component.spellcheck; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.handler.component.SpellCheckComponent; -import org.apache.solr.spelling.SpellingResult; - -/** - * @author Jamal Kaabi-Mofrad - * @since 5.0 - */ -public class AlfrescoSpellCheckComponent extends SpellCheckComponent -{ - - /** - * Disclaimer: The code copied from the super class ( - * {@link org.apache.solr.handler.component.SpellCheckComponent}) but only - * replaced the collator with the AlfrescoSpellCheckCollator - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - protected void addCollationsToResponse(SolrParams params, SpellingResult spellingResult, ResponseBuilder rb, - String q, NamedList response, boolean suggestionsMayOverlap) - { - int maxCollations = params.getInt(SPELLCHECK_MAX_COLLATIONS, 1); - int maxCollationTries = params.getInt(SPELLCHECK_MAX_COLLATION_TRIES, 0); - int maxCollationEvaluations = params.getInt(SPELLCHECK_MAX_COLLATION_EVALUATIONS, 10000); - boolean collationExtendedResults = params.getBool(SPELLCHECK_COLLATE_EXTENDED_RESULTS, false); - int maxCollationCollectDocs = params.getInt(SPELLCHECK_COLLATE_MAX_COLLECT_DOCS, 0); - // If not reporting hits counts, don't bother collecting more than 1 document per try. - if (!collationExtendedResults) - { - maxCollationCollectDocs = 1; - } - boolean shard = params.getBool(ShardParams.IS_SHARD, false); - AlfrescoSpellCheckCollator collator = new AlfrescoSpellCheckCollator(); - collator.setMaxCollations(maxCollations); - collator.setMaxCollationTries(maxCollationTries); - collator.setMaxCollationEvaluations(maxCollationEvaluations); - collator.setSuggestionsMayOverlap(suggestionsMayOverlap); - collator.setDocCollectionLimit(maxCollationCollectDocs); - - List collations = collator.collate(spellingResult, q, rb); - // by sorting here we guarantee a non-distributed request returns all - // results in the same order as a distributed request would, - // even in cases when the internal rank is the same. - Collections.sort(collations); - - NamedList collationList = new NamedList(); - - for (AlfrescoSpellCheckCollation collation : collations) - { - if (collationExtendedResults) - { - NamedList extendedResult = new SimpleOrderedMap(); - extendedResult.add("collationQuery", collation.getCollationQuery()); - extendedResult.add("hits", collation.getHits()); - extendedResult.add("misspellingsAndCorrections", collation.getMisspellingsAndCorrections()); - if (maxCollationTries > 0 && shard) - { - extendedResult.add("collationInternalRank", collation.getInternalRank()); - } - extendedResult.add("collationQueryString", collation.getCollationQueryString()); - collationList.add("collation", extendedResult); - } - else - { - collationList.add("collation", collation.getCollationQuery()); - if (maxCollationTries > 0 && shard) - { - collationList.add("collationInternalRank", collation.getInternalRank()); - } - } - } - - //Support Solr 4 output format - NamedList suggestions = (NamedList)response.get("suggestions"); - suggestions.addAll(collationList); - - //Support Solr distributed merge format. - response.add("collations", collationList); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component.spellcheck; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SpellCheckComponent; +import org.apache.solr.spelling.SpellingResult; + +/** + * @author Jamal Kaabi-Mofrad + * @since 5.0 + */ +public class AlfrescoSpellCheckComponent extends SpellCheckComponent +{ + + /** + * Disclaimer: The code copied from the super class ( + * {@link org.apache.solr.handler.component.SpellCheckComponent}) but only + * replaced the collator with the AlfrescoSpellCheckCollator + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + protected void addCollationsToResponse(SolrParams params, SpellingResult spellingResult, ResponseBuilder rb, + String q, NamedList response, boolean suggestionsMayOverlap) + { + int maxCollations = params.getInt(SPELLCHECK_MAX_COLLATIONS, 1); + int maxCollationTries = params.getInt(SPELLCHECK_MAX_COLLATION_TRIES, 0); + int maxCollationEvaluations = params.getInt(SPELLCHECK_MAX_COLLATION_EVALUATIONS, 10000); + boolean collationExtendedResults = params.getBool(SPELLCHECK_COLLATE_EXTENDED_RESULTS, false); + int maxCollationCollectDocs = params.getInt(SPELLCHECK_COLLATE_MAX_COLLECT_DOCS, 0); + // If not reporting hits counts, don't bother collecting more than 1 document per try. + if (!collationExtendedResults) + { + maxCollationCollectDocs = 1; + } + boolean shard = params.getBool(ShardParams.IS_SHARD, false); + AlfrescoSpellCheckCollator collator = new AlfrescoSpellCheckCollator(); + collator.setMaxCollations(maxCollations); + collator.setMaxCollationTries(maxCollationTries); + collator.setMaxCollationEvaluations(maxCollationEvaluations); + collator.setSuggestionsMayOverlap(suggestionsMayOverlap); + collator.setDocCollectionLimit(maxCollationCollectDocs); + + List collations = collator.collate(spellingResult, q, rb); + // by sorting here we guarantee a non-distributed request returns all + // results in the same order as a distributed request would, + // even in cases when the internal rank is the same. + Collections.sort(collations); + + NamedList collationList = new NamedList(); + + for (AlfrescoSpellCheckCollation collation : collations) + { + if (collationExtendedResults) + { + NamedList extendedResult = new SimpleOrderedMap(); + extendedResult.add("collationQuery", collation.getCollationQuery()); + extendedResult.add("hits", collation.getHits()); + extendedResult.add("misspellingsAndCorrections", collation.getMisspellingsAndCorrections()); + if (maxCollationTries > 0 && shard) + { + extendedResult.add("collationInternalRank", collation.getInternalRank()); + } + extendedResult.add("collationQueryString", collation.getCollationQueryString()); + collationList.add("collation", extendedResult); + } + else + { + collationList.add("collation", collation.getCollationQuery()); + if (maxCollationTries > 0 && shard) + { + collationList.add("collationInternalRank", collation.getInternalRank()); + } + } + } + + //Support Solr 4 output format + NamedList suggestions = (NamedList)response.get("suggestions"); + suggestions.addAll(collationList); + + //Support Solr distributed merge format. + response.add("collations", collationList); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/config/ConfigUtil.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/config/ConfigUtil.java index f6a322889..08e8183aa 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/config/ConfigUtil.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/config/ConfigUtil.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/data/GlobalReaders.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/data/GlobalReaders.java index 1dcb31e85..bdf656a49 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/data/GlobalReaders.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/data/GlobalReaders.java @@ -1,54 +1,54 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.data; - -import java.util.HashSet; - -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.security.PermissionService; - -/** - * Statically configured set of authorities that always have read access. - * - * @author Matt Ward - */ -public class GlobalReaders -{ - private static HashSet readers = new HashSet(); - - static - { - readers.add(PermissionService.OWNER_AUTHORITY); - readers.add(PermissionService.ADMINISTRATOR_AUTHORITY); - readers.add(AuthenticationUtil.getSystemUserName()); - } - - public static HashSet getReaders() - { - return readers; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.data; + +import java.util.HashSet; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.security.PermissionService; + +/** + * Statically configured set of authorities that always have read access. + * + * @author Matt Ward + */ +public class GlobalReaders +{ + private static HashSet readers = new HashSet(); + + static + { + readers.add(PermissionService.OWNER_AUTHORITY); + readers.add(PermissionService.ADMINISTRATOR_AUTHORITY); + readers.add(AuthenticationUtil.getSystemUserName()); + } + + public static HashSet getReaders() + { + return readers; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java index 77916188f..fff070f6f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -462,4 +462,4 @@ public class SolrCoreLoadListener extends AbstractSolrEventListener .map(isSlaveModeEnabled) .orElse(false); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/logging/Log.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/logging/Log.java index c51952314..d074ffefe 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/logging/Log.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/logging/Log.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbsoluteStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbsoluteStructuredFieldPosition.java index aeba76d58..e2e54b808 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbsoluteStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbsoluteStructuredFieldPosition.java @@ -1,142 +1,142 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -/** - * This class patches a term at a specified location. - * - * @author andyh - */ -public class AbsoluteStructuredFieldPosition extends AbstractStructuredFieldPosition -{ - - int requiredPosition; - - /** - * Search for a term at the specified position. - */ - - public AbsoluteStructuredFieldPosition(String termText, int position) - { - super(termText, true, true); - this.requiredPosition = position; - } - - /* - * (non-Javadoc) - * - * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, - * org.apache.lucene.index.TermPositions) - */ - public int matches(int start, int end, int offset) throws IOException - { - if (offset >= requiredPosition) - { - return -1; - } - - if (getCachingTermPositions() != null) - { - // Doing "termText" - getCachingTermPositions().reset(); - int count = getCachingTermPositions().freq(); - int realPosition = 0; - int adjustedPosition = 0; - for (int i = 0; i < count; i++) - { - realPosition = getCachingTermPositions().nextPosition(); - adjustedPosition = realPosition - start; - if ((end != -1) && (realPosition > end)) - { - return -1; - } - if (adjustedPosition > requiredPosition) - { - return -1; - } - if (adjustedPosition == requiredPosition) - { - return adjustedPosition; - } - - } - } - else - { - // Doing "*" - if ((offset + 1) == requiredPosition) - { - return offset + 1; - } - } - return -1; - - } - - /* - * (non-Javadoc) - * - * @see org.alfresco.lucene.extensions.StructuredFieldPosition#getPosition() - */ - public int getPosition() - { - return requiredPosition; - } - - public String getDescription() - { - return "Absolute Named child"; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + requiredPosition; - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - AbsoluteStructuredFieldPosition other = (AbsoluteStructuredFieldPosition) obj; - if (requiredPosition != other.requiredPosition) - return false; - return true; - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +/** + * This class patches a term at a specified location. + * + * @author andyh + */ +public class AbsoluteStructuredFieldPosition extends AbstractStructuredFieldPosition +{ + + int requiredPosition; + + /** + * Search for a term at the specified position. + */ + + public AbsoluteStructuredFieldPosition(String termText, int position) + { + super(termText, true, true); + this.requiredPosition = position; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, + * org.apache.lucene.index.TermPositions) + */ + public int matches(int start, int end, int offset) throws IOException + { + if (offset >= requiredPosition) + { + return -1; + } + + if (getCachingTermPositions() != null) + { + // Doing "termText" + getCachingTermPositions().reset(); + int count = getCachingTermPositions().freq(); + int realPosition = 0; + int adjustedPosition = 0; + for (int i = 0; i < count; i++) + { + realPosition = getCachingTermPositions().nextPosition(); + adjustedPosition = realPosition - start; + if ((end != -1) && (realPosition > end)) + { + return -1; + } + if (adjustedPosition > requiredPosition) + { + return -1; + } + if (adjustedPosition == requiredPosition) + { + return adjustedPosition; + } + + } + } + else + { + // Doing "*" + if ((offset + 1) == requiredPosition) + { + return offset + 1; + } + } + return -1; + + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.lucene.extensions.StructuredFieldPosition#getPosition() + */ + public int getPosition() + { + return requiredPosition; + } + + public String getDescription() + { + return "Absolute Named child"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + requiredPosition; + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + AbsoluteStructuredFieldPosition other = (AbsoluteStructuredFieldPosition) obj; + if (requiredPosition != other.requiredPosition) + return false; + return true; + } + + +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQuery.java index bef366ccf..3b845ee12 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQuery.java @@ -1,76 +1,76 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; - -/** - * Base class for queries relating to an authority. - */ -public abstract class AbstractAuthorityQuery extends Query -{ - protected final String authority; - - /** - * Construct with authority. - * - * @param authority - */ - public AbstractAuthorityQuery(String authority) - { - if (authority == null) throw new IllegalStateException("authority cannot be null"); - this.authority = authority; - } - - @Override - public abstract Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException; - - public String toString(String field) - { - return toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof AbstractAuthorityQuery)) return false; - - AbstractAuthorityQuery that = (AbstractAuthorityQuery) o; - - return authority.equals(that.authority); - - } - - @Override - public int hashCode() { - return authority.hashCode(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; + +/** + * Base class for queries relating to an authority. + */ +public abstract class AbstractAuthorityQuery extends Query +{ + protected final String authority; + + /** + * Construct with authority. + * + * @param authority + */ + public AbstractAuthorityQuery(String authority) + { + if (authority == null) throw new IllegalStateException("authority cannot be null"); + this.authority = authority; + } + + @Override + public abstract Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException; + + public String toString(String field) + { + return toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AbstractAuthorityQuery)) return false; + + AbstractAuthorityQuery that = (AbstractAuthorityQuery) o; + + return authority.equals(that.authority); + + } + + @Override + public int hashCode() { + return authority.hashCode(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQueryWeight.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQueryWeight.java index 465fbbc8f..3b46846e3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQueryWeight.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthorityQueryWeight.java @@ -1,85 +1,85 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.apache.lucene.index.IndexReaderContext; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermContext; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Abstract {@link Weight} implementation for authority related queries. - * - * @see AbstractAuthoritySetQuery - */ -public abstract class AbstractAuthorityQueryWeight extends Weight -{ - protected Query query; - protected SolrIndexSearcher searcher; - protected float value; - protected boolean needsScores; - - public AbstractAuthorityQueryWeight(SolrIndexSearcher searcher, boolean needsScores, Query query, String authTermName, String authTermText) throws IOException - { - super(query); - this.searcher = searcher; - searcher.collectionStatistics(authTermName); - final IndexReaderContext context = searcher.getTopReaderContext(); - final Term term = new Term(authTermName, authTermText); - final TermContext termContext = TermContext.build(context, term); - searcher.termStatistics(term, termContext); - this.needsScores = needsScores; - } - - @Override - public Explanation explain(LeafReaderContext context, int doc) - { - throw new UnsupportedOperationException(); - } - - @Override - public float getValueForNormalization() - { - return sumOfSquaredWeights(); - } - - @Override - public void normalize(float queryNorm, float topLevelBoost) - { - } - - protected float sumOfSquaredWeights() - { - return 0; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.apache.lucene.index.IndexReaderContext; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.index.TermContext; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Abstract {@link Weight} implementation for authority related queries. + * + * @see AbstractAuthoritySetQuery + */ +public abstract class AbstractAuthorityQueryWeight extends Weight +{ + protected Query query; + protected SolrIndexSearcher searcher; + protected float value; + protected boolean needsScores; + + public AbstractAuthorityQueryWeight(SolrIndexSearcher searcher, boolean needsScores, Query query, String authTermName, String authTermText) throws IOException + { + super(query); + this.searcher = searcher; + searcher.collectionStatistics(authTermName); + final IndexReaderContext context = searcher.getTopReaderContext(); + final Term term = new Term(authTermName, authTermText); + final TermContext termContext = TermContext.build(context, term); + searcher.termStatistics(term, termContext); + this.needsScores = needsScores; + } + + @Override + public Explanation explain(LeafReaderContext context, int doc) + { + throw new UnsupportedOperationException(); + } + + @Override + public float getValueForNormalization() + { + return sumOfSquaredWeights(); + } + + @Override + public void normalize(float queryNorm, float topLevelBoost) + { + } + + protected float sumOfSquaredWeights() + { + return 0; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthoritySetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthoritySetQuery.java index ef14d2625..65da0c144 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthoritySetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractAuthoritySetQuery.java @@ -1,192 +1,192 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Base class for queries relating to a set of authorities, e.g. reader set query. - */ -public abstract class AbstractAuthoritySetQuery extends Query -{ - protected final String authorities; - - /** - * Construct with authorities. - * - * @param authorities - */ - public AbstractAuthoritySetQuery(String authorities) - { - super(); - if (authorities == null) throw new IllegalStateException("authorities cannot be null"); - this.authorities = authorities; - } - - @Override - public abstract Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException; - - @Override - public String toString(String field) - { - return toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof AbstractAuthoritySetQuery)) return false; - - AbstractAuthoritySetQuery that = (AbstractAuthoritySetQuery) o; - - return authorities.equals(that.authorities); - - } - - @Override - public int hashCode() { - return authorities.hashCode(); - } - - - /* - * This method collects the bitset of documents that match the authorities. - */ - - protected HybridBitSet getACLSet(String[] auths, String field, SolrIndexSearcher searcher) throws IOException - { - /* - * Build a query that matches the authorities with a field in the ACL records in the index. - */ - - BooleanQuery.Builder queryBuilder = new BooleanQuery.Builder(); - for(String current : auths) - { - queryBuilder.add(new TermQuery(new Term(field, current)), BooleanClause.Occur.SHOULD); - } - - - /* - * Collect a docset containing the ACL records that match the query. - * This query will be in the filter cache. Ideally it would remain cached throughout the users session. - */ - - DocSet docSet = searcher.getDocSet(queryBuilder.build()); - - DocIterator iterator = docSet.iterator(); - if(!iterator.hasNext()) - { - return new EmptyHybridBitSet(); - } - - //TODO : makes this configurable. For some systems this is huge and for others not big enough. - HybridBitSet hybridBitSet = new HybridBitSet(60000000); - - /* - * Collect the ACLID's from the matching acl records. - * This is done in a separate step so the initial ACL query can be cached in the FilterCache - * The initial ACL query may be expensive if the number of authorities is very large. - */ - - List leaves = searcher.getTopReaderContext().leaves(); - LeafReaderContext context = leaves.get(0); - NumericDocValues aclValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); - LeafReader reader = context.reader(); - int ceil = reader.maxDoc(); - int base = 0; - int ord = 0; - while (iterator.hasNext()) { - int doc = iterator.nextDoc(); - if(doc >= ceil) - { - do - { - ++ord; - context = leaves.get(ord); - reader = context.reader(); - base = context.docBase; - ceil = base+reader.maxDoc(); - aclValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, reader); - } - while(doc >= ceil); - } - - if(aclValues != null) { - long aclId = aclValues.get(doc - base); - hybridBitSet.set(aclId); - } - } - - return hybridBitSet; - } - - protected BitsFilter getACLFilter(String[] auths, String field, SolrIndexSearcher searcher) throws IOException - { - HybridBitSet aclBits = getACLSet(auths, field, searcher); - List leaves = searcher.getTopReaderContext().leaves(); - List bitSets = new ArrayList(leaves.size()); - - for(LeafReaderContext readerContext : leaves) - { - LeafReader reader = readerContext.reader(); - int maxDoc = reader.maxDoc(); - FixedBitSet bits = new FixedBitSet(maxDoc); - bitSets.add(bits); - - NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, reader); - if (fieldValues != null) { - for (int i = 0; i < maxDoc; i++) { - long aclID = fieldValues.get(i); - if (aclBits.get(aclID)) { - bits.set(i); - } - } - } - } - - return new BitsFilter(bitSets); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Base class for queries relating to a set of authorities, e.g. reader set query. + */ +public abstract class AbstractAuthoritySetQuery extends Query +{ + protected final String authorities; + + /** + * Construct with authorities. + * + * @param authorities + */ + public AbstractAuthoritySetQuery(String authorities) + { + super(); + if (authorities == null) throw new IllegalStateException("authorities cannot be null"); + this.authorities = authorities; + } + + @Override + public abstract Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException; + + @Override + public String toString(String field) + { + return toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AbstractAuthoritySetQuery)) return false; + + AbstractAuthoritySetQuery that = (AbstractAuthoritySetQuery) o; + + return authorities.equals(that.authorities); + + } + + @Override + public int hashCode() { + return authorities.hashCode(); + } + + + /* + * This method collects the bitset of documents that match the authorities. + */ + + protected HybridBitSet getACLSet(String[] auths, String field, SolrIndexSearcher searcher) throws IOException + { + /* + * Build a query that matches the authorities with a field in the ACL records in the index. + */ + + BooleanQuery.Builder queryBuilder = new BooleanQuery.Builder(); + for(String current : auths) + { + queryBuilder.add(new TermQuery(new Term(field, current)), BooleanClause.Occur.SHOULD); + } + + + /* + * Collect a docset containing the ACL records that match the query. + * This query will be in the filter cache. Ideally it would remain cached throughout the users session. + */ + + DocSet docSet = searcher.getDocSet(queryBuilder.build()); + + DocIterator iterator = docSet.iterator(); + if(!iterator.hasNext()) + { + return new EmptyHybridBitSet(); + } + + //TODO : makes this configurable. For some systems this is huge and for others not big enough. + HybridBitSet hybridBitSet = new HybridBitSet(60000000); + + /* + * Collect the ACLID's from the matching acl records. + * This is done in a separate step so the initial ACL query can be cached in the FilterCache + * The initial ACL query may be expensive if the number of authorities is very large. + */ + + List leaves = searcher.getTopReaderContext().leaves(); + LeafReaderContext context = leaves.get(0); + NumericDocValues aclValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); + LeafReader reader = context.reader(); + int ceil = reader.maxDoc(); + int base = 0; + int ord = 0; + while (iterator.hasNext()) { + int doc = iterator.nextDoc(); + if(doc >= ceil) + { + do + { + ++ord; + context = leaves.get(ord); + reader = context.reader(); + base = context.docBase; + ceil = base+reader.maxDoc(); + aclValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, reader); + } + while(doc >= ceil); + } + + if(aclValues != null) { + long aclId = aclValues.get(doc - base); + hybridBitSet.set(aclId); + } + } + + return hybridBitSet; + } + + protected BitsFilter getACLFilter(String[] auths, String field, SolrIndexSearcher searcher) throws IOException + { + HybridBitSet aclBits = getACLSet(auths, field, searcher); + List leaves = searcher.getTopReaderContext().leaves(); + List bitSets = new ArrayList(leaves.size()); + + for(LeafReaderContext readerContext : leaves) + { + LeafReader reader = readerContext.reader(); + int maxDoc = reader.maxDoc(); + FixedBitSet bits = new FixedBitSet(maxDoc); + bitSets.add(bits); + + NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, reader); + if (fieldValues != null) { + for (int i = 0; i < maxDoc; i++) { + long aclID = fieldValues.get(i); + if (aclBits.get(aclID)) { + bits.set(i); + } + } + } + } + + return new BitsFilter(bitSets); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractQParser.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractQParser.java index c3fd28537..68e6cea4b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractQParser.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractQParser.java @@ -1,707 +1,707 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; -import org.alfresco.service.cmr.search.QueryConsistency; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchParameters.Operator; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.alfresco.util.Pair; -import org.apache.commons.io.IOUtils; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.ContentStream; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.SortSpec; -import org.apache.solr.search.SortSpecParsing; -import org.apache.solr.search.SyntaxError; -import org.apache.solr.update.processor.LangDetectLanguageIdentifierUpdateProcessor; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.extensions.surf.util.I18NUtil; - -import com.cybozu.labs.langdetect.Detector; -import com.cybozu.labs.langdetect.DetectorFactory; -import com.cybozu.labs.langdetect.LangDetectException; -import com.cybozu.labs.langdetect.Language; - -/** - * @author Andy - */ -public abstract class AbstractQParser extends QParser implements QueryConstants -{ - private static char[] SEPARATORS = new char[] { ':', ',', '-', '!', '+', '=', ';', '~', '/' }; - - protected final static Logger log = LoggerFactory.getLogger(AbstractQParser.class); - - protected boolean authset; - - public static final String ALFRESCO_JSON = "ALFRESCO_JSON"; - - private static final String AUTHORITY_FILTER_FROM_JSON = "AUTHORITY_FILTER_FROM_JSON"; - - private static final String TENANT_FILTER_FROM_JSON = "TENANT_FILTER_FROM_JSON"; - - private static final String RERANK_QUERY_FROM_CONTEXT = "RERANK_QUERY_FROM_CONTEXT"; - - static final String languages[] = { - "af", "ar", "bg", "bn", "cs", "da", "de", "el", "en", "es", "et", "fa", "fi", "fr", "gu", - "he", "hi", "hr", "hu", "id", "it", "ja", "kn", "ko", "lt", "lv", "mk", "ml", "mr", "ne", - "nl", "no", "pa", "pl", "pt", "ro", "ru", "sk", "sl", "so", "sq", "sv", "sw", "ta", "te", - "th", "tl", "tr", "uk", "ur", "vi", "zh-cn", "zh-tw" - }; - - static - { - try { - List profileData = new ArrayList<>(); - for (String language : languages) { - InputStream stream = LangDetectLanguageIdentifierUpdateProcessor.class.getResourceAsStream("langdetect-profiles/" + language); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); - profileData.add(new String(IOUtils.toCharArray(reader))); - reader.close(); - } - DetectorFactory.loadProfile(profileData); - DetectorFactory.setSeed(0); - } catch (Exception e) { - throw new RuntimeException("Couldn't load profile data, will return empty languages always!", e); - } - } - - private boolean autoDetectQueryLocale = false; - - private HashSet autoDetectQueryLocales = new HashSet(); - - private HashSet fixedQueryLocales = new HashSet(); - - /** - * @param qstr String - * @param localParams SolrParams - * @param params SolrParams - * @param req SolrQueryRequest - * @param args - */ - public AbstractQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) - { - super(qstr, localParams, params, req); - if(args != null) - { - Object arg = args.get("autoDetectQueryLocale"); - if(arg != null) - { - this.autoDetectQueryLocale = Boolean.parseBoolean(arg.toString()); - } - - arg = args.get("autoDetectQueryLocales"); - if(arg != null) - { - String[] locales = arg.toString().split(","); - for(String locale : locales) - { - String mappedLanguage = isKnownLocale(locale); - if(mappedLanguage != null) - { - autoDetectQueryLocales.add(mappedLanguage); - } - } - } - - - arg = args.get("fixedQueryLocales"); - if(arg != null) - { - String[] locales = arg.toString().split(","); - for(String locale : locales) - { - String mappedLanguage = isKnownLocale(locale); - if(mappedLanguage != null) - { - fixedQueryLocales.add(mappedLanguage); - } - } - } - } - } - - private String isKnownLocale(String locale) - { - for(String test : languages) - { - if(locale.equalsIgnoreCase(test)) - { - return test; - } - } - return null; - } - - - protected Pair getSearchParameters() - { - SearchParameters searchParameters = new SearchParameters(); - - Boolean isFilter = Boolean.FALSE; - - Iterable streams = req.getContentStreams(); - - JSONObject json = (JSONObject) req.getContext().get(ALFRESCO_JSON); - - if (json == null) - { - if (streams != null) - { - - try - { - Reader reader = null; - for (ContentStream stream : streams) - { - reader = new BufferedReader(new InputStreamReader(stream.getStream(), "UTF-8")); - } - - // TODO - replace with streaming-based solution e.g. SimpleJSON ContentHandler - if (reader != null) - { - json = new JSONObject(new JSONTokener(reader)); - req.getContext().put(ALFRESCO_JSON, json); - } - } - catch (JSONException e) - { - // This is expected when there is no json element to the request - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("IO Error parsing query parameters", e); - } - } - } - - if (json != null) - { - try - { - if (getString() != null) - { - if (getString().equals(AUTHORITY_FILTER_FROM_JSON)) - { - isFilter =Boolean.TRUE; - - ArrayList tenantList = new ArrayList(1); - JSONArray tenants = json.getJSONArray("tenants"); - for (int i = 0; i < tenants.length(); i++) - { - String tenantString = tenants.getString(i); - tenantList.add(tenantString); - } - - ArrayList authorityList = new ArrayList(1); - JSONArray authorities = json.getJSONArray("authorities"); - for (int i = 0; i < authorities.length(); i++) - { - String authorityString = authorities.getString(i); - authorityList.add(authorityString); - } - - char separator = getSeparator(authorityList); - - StringBuilder authQuery = new StringBuilder(); - StringBuilder denyQuery = new StringBuilder(); - - for (String tenant : tenantList) - { - for (String authority : authorityList) - { - if (separator == 0) - { - if (authQuery.length() > 0) - { - authQuery.append(" "); - denyQuery.append(" "); - } - switch (AuthorityType.getAuthorityType(authority)) - { - case USER: - authQuery.append("|AUTHORITY:\"").append(authority).append("\""); - denyQuery.append("|DENIED:\"").append(authority).append("\""); - break; - case GROUP: - case EVERYONE: - case GUEST: - if (tenant.length() == 0) - { - // Default tenant matches 4.0 - authQuery.append("|AUTHORITY:\"").append(authority).append("\""); - denyQuery.append("|DENIED:\"").append(authority).append("\""); - } - else - { - authQuery.append("|AUTHORITY:\"").append(authority).append("@").append(tenant).append("\""); - denyQuery.append("|DENIED:\"").append(authority).append("@").append(tenant).append("\""); - } - break; - default: - authQuery.append("|AUTHORITY:\"").append(authority).append("\""); - denyQuery.append("|DENIED:\"").append(authority).append("\""); - break; - } - } - else - { - if(authQuery.length() == 0) - { - authset = true; - authQuery.append("|AUTHSET:\""); - denyQuery.append("|DENYSET:\""); - } - - switch (AuthorityType.getAuthorityType(authority)) - { - case USER: - authQuery.append(separator).append(authority); - denyQuery.append(separator).append(authority); - break; - case GROUP: - case EVERYONE: - case GUEST: - if (tenant.length() == 0) - { - // Default tenant matches 4.0 - authQuery.append(separator).append(authority); - denyQuery.append(separator).append(authority); - } - else - { - authQuery.append(separator).append(authority).append("@").append(tenant); - denyQuery.append(separator).append(authority).append("@").append(tenant); - } - break; - default: - authQuery.append(separator).append(authority); - denyQuery.append(separator).append(authority); - break; - } - } - - } - } - if(separator != 0) - { - authQuery.append("\""); - denyQuery.append("\""); - } - - if (authQuery.length() > 0) - { - // Default to true for safety reasons. - final boolean anyDenyDenies = json.optBoolean("anyDenyDenies", true); - - if (anyDenyDenies) - { - authQuery.insert(0, "("). - append(") AND NOT ("). - append(denyQuery). - append(")"); - // Record that the clause has been added. - // We only ever set this to true for solr4+ - req.getContext().put("processedDenies", Boolean.TRUE); - } - searchParameters.setQuery(authQuery.toString()); - } - } - else if (getString().equals(TENANT_FILTER_FROM_JSON)) - { - isFilter =Boolean.TRUE; - - ArrayList tenantList = new ArrayList(1); - JSONArray tenants = json.getJSONArray("tenants"); - for (int i = 0; i < tenants.length(); i++) - { - String tenantString = tenants.getString(i); - tenantList.add(tenantString); - } - - StringBuilder tenantQuery = new StringBuilder(); - for (String tenant : tenantList) - { - if (tenantQuery.length() > 0) - { - tenantQuery.append(" "); - } - - if (tenant.length() > 0) - - { - tenantQuery.append("|TENANT:\"").append(tenant).append("\""); - } - else - { - // TODO: Need to check for the default tenant or no tenant (4.0) or we force a reindex - // requirement later ... - // Better to add default tenant to the 4.0 index - tenantQuery.append("|TENANT:\"").append("_DEFAULT_").append("\""); - // tenantQuery.append(" |(+ISNODE:T -TENANT:*)"); - } - - } - searchParameters.setQuery(tenantQuery.toString()); - } - else if (getString().equals(RERANK_QUERY_FROM_CONTEXT)) - { - String searchTerm = getParam("spellcheck.q"); - searchParameters.setQuery(searchTerm); - } - } - else - { - String query = json.getString("query"); - if (query != null) - { - searchParameters.setQuery(query); - } - } - - JSONArray locales = json.getJSONArray("locales"); - for (int i = 0; i < locales.length(); i++) - { - String localeString = locales.getString(i); - Locale locale = DefaultTypeConverter.INSTANCE.convert(Locale.class, localeString); - searchParameters.addLocale(locale); - } - - JSONArray templates = json.getJSONArray("templates"); - for (int i = 0; i < templates.length(); i++) - { - JSONObject template = templates.getJSONObject(i); - String name = template.getString("name"); - String queryTemplate = template.getString("template"); - searchParameters.addQueryTemplate(name, queryTemplate); - } - - JSONArray allAttributes = json.getJSONArray("allAttributes"); - for (int i = 0; i < allAttributes.length(); i++) - { - String allAttribute = allAttributes.getString(i); - searchParameters.addAllAttribute(allAttribute); - } - - searchParameters.setDefaultFTSOperator(Operator.valueOf(json.getString("defaultFTSOperator"))); - searchParameters.setDefaultFTSFieldConnective(Operator.valueOf(json.getString("defaultFTSFieldOperator"))); - if (json.has("mlAnalaysisMode")) - { - searchParameters.setMlAnalaysisMode(MLAnalysisMode.valueOf(json.getString("mlAnalaysisMode"))); - } - searchParameters.setNamespace(json.getString("defaultNamespace")); - - JSONArray textAttributes = json.getJSONArray("textAttributes"); - for (int i = 0; i < textAttributes.length(); i++) - { - String textAttribute = textAttributes.getString(i); - searchParameters.addTextAttribute(textAttribute); - } - - - searchParameters.setQueryConsistency(QueryConsistency.valueOf(json.getString("queryConsistency"))); - - } - catch (JSONException e) - { - // This is expected when there is no json element to the request - } - } - - if (json != null) - { - if (log.isDebugEnabled()) - { - log.debug(json.toString()); - } - } - - if (searchParameters.getQuery() == null) - { - searchParameters.setQuery(getString()); - } - - if (searchParameters.getLocales().size() == 0) - { - searchParameters.addLocale(I18NUtil.getLocale()); - } - - String defaultField = getParam(CommonParams.DF); - if (defaultField != null) - { - searchParameters.setDefaultFieldName(defaultField); - } - - if(autoDetectQueryLocale) - { - String searchTerm = getParam("spellcheck.q"); - if (searchTerm != null) - { - searchParameters.setSearchTerm(searchTerm); - List detetcted = detectLanguage(searchTerm); - if((detetcted != null) && (detetcted.size() > 0)) - { - Locale detectedLocale = Locale.forLanguageTag(detetcted.get(0).getLangCode()); - if(localeIsNotIncluded(searchParameters, detectedLocale)) - { - searchParameters.addLocale( Locale.forLanguageTag(detectedLocale.getLanguage())); - } - } - - } - } - - if(fixedQueryLocales.size() > 0) - { - for(String locale : fixedQueryLocales) - { - searchParameters.addLocale( Locale.forLanguageTag(locale)); - } - } - - // searchParameters.setMlAnalaysisMode(getMLAnalysisMode()); - searchParameters.setNamespace(NamespaceService.CONTENT_MODEL_1_0_URI); - - return new Pair(searchParameters, isFilter); - } - - /** - * @param searchParameters SearchParameters - * @param detectedLocale Locale - * @return boolean - */ - private boolean localeIsNotIncluded(SearchParameters searchParameters, Locale detectedLocale) - { - for(Locale locale : searchParameters.getLocales()) - { - if(locale.getLanguage().equals(detectedLocale.getLanguage())) - { - return false; - } - } - return true; - } - - - /** - * @param authorityList ArrayList - * @return char - */ - private char getSeparator(ArrayList authorityList) - { - StringBuilder builder = new StringBuilder(); - for(String auth : authorityList) - { - builder.append(auth); - } - String test = builder.toString(); - - for(int i = 0; i < SEPARATORS.length; i++) - { - if(test.indexOf(SEPARATORS[i]) == -1) - { - return SEPARATORS[i]; - } - } - return 0; - } - - /* (non-Javadoc) - * @see org.apache.solr.search.QParser#getSort(boolean) - */ - @Override - public SortSpec getSortSpec(boolean useGlobalParams) throws SyntaxError - { - - getQuery(); // ensure query is parsed first - - String sortStr = null; - String startS = null; - String rowsS = null; - - if (localParams != null) { - sortStr = localParams.get(CommonParams.SORT); - startS = localParams.get(CommonParams.START); - rowsS = localParams.get(CommonParams.ROWS); - - // if any of these parameters are present, don't go back to the global params - if (sortStr != null || startS != null || rowsS != null) { - useGlobalParams = false; - } - } - - if (useGlobalParams) { - if (sortStr ==null) { - sortStr = params.get(CommonParams.SORT); - } - if (startS==null) { - startS = params.get(CommonParams.START); - } - if (rowsS==null) { - rowsS = params.get(CommonParams.ROWS); - } - } - - int start = startS != null ? Integer.parseInt(startS) : 0; - int rows = rowsS != null ? Integer.parseInt(rowsS) : 10; - - // Fix sort fields here - if(sortStr != null) - { - StringBuilder builder = new StringBuilder(); - StringBuilder propertyBuilder = null; - char c; - for(int i = 0; i < sortStr.length(); i++) - { - c = sortStr.charAt(i); - if(propertyBuilder == null) - { - if(!Character.isWhitespace(c) && (c != ',')) - { - propertyBuilder = new StringBuilder(); - propertyBuilder.append(c); - } - else - { - builder.append(c); - } - } - else - { - if(Character.isWhitespace(c) || (c == ',')) - { - String toAppend = AlfrescoSolrDataModel.getInstance().mapProperty(propertyBuilder.toString(), FieldUse.SORT, getReq()); - builder.append(toAppend); - builder.append(c); - propertyBuilder = null; - } - else - { - propertyBuilder.append(c); - } - } - } - if(propertyBuilder != null) - { - String toAppend = AlfrescoSolrDataModel.getInstance().mapProperty(propertyBuilder.toString(), FieldUse.SORT, getReq()); - builder.append(toAppend); - } - sortStr = builder.toString(); - } - - if(sortStr != null) - { - sortStr = sortStr.replaceAll("^ID(\\s)", "id$1"); - sortStr = sortStr.replaceAll("(\\s)ID(\\s)", "$1id$2"); - } - SortSpec sort = SortSpecParsing.parseSortSpec(sortStr, req); - - sort.setOffset(start); - sort.setCount(rows); - return sort; - } - - - - private List detectLanguage(String content) { - if (content.trim().length() == 0) { // to be consistent with the tika impl? - log.debug("No input text to detect language from, returning empty list"); - return Collections.emptyList(); - } - - try { - Detector detector = DetectorFactory.create(); - detector.append(content); - ArrayList langlist = detector.getProbabilities(); - ArrayList solrLangList = new ArrayList<>(); - for (Language l: langlist) - { - if((autoDetectQueryLocales.size() == 0) || (autoDetectQueryLocales.contains(l.lang))) - { - solrLangList.add(new DetectedLanguage(l.lang, l.prob)); - } - } - return solrLangList; - } catch (LangDetectException e) { - log.debug("Could not determine language, returning empty list: ", e); - return Collections.emptyList(); - } - } - - public class DetectedLanguage { - private final String langCode; - private final Double certainty; - - DetectedLanguage(String lang, Double certainty) { - this.langCode = lang; - this.certainty = certainty; - } - - /** - * Returns the detected language code - * @return language code as a string - */ - public String getLangCode() { - return langCode; - } - - /** - * Returns the detected certainty for this language - * @return certainty as a value between 0.0 and 1.0 where 1.0 is 100% certain - */ - public Double getCertainty() { - return certainty; - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.search.QueryConsistency; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchParameters.Operator; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.alfresco.util.Pair; +import org.apache.commons.io.IOUtils; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.ContentStream; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.SortSpec; +import org.apache.solr.search.SortSpecParsing; +import org.apache.solr.search.SyntaxError; +import org.apache.solr.update.processor.LangDetectLanguageIdentifierUpdateProcessor; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +import com.cybozu.labs.langdetect.Detector; +import com.cybozu.labs.langdetect.DetectorFactory; +import com.cybozu.labs.langdetect.LangDetectException; +import com.cybozu.labs.langdetect.Language; + +/** + * @author Andy + */ +public abstract class AbstractQParser extends QParser implements QueryConstants +{ + private static char[] SEPARATORS = new char[] { ':', ',', '-', '!', '+', '=', ';', '~', '/' }; + + protected final static Logger log = LoggerFactory.getLogger(AbstractQParser.class); + + protected boolean authset; + + public static final String ALFRESCO_JSON = "ALFRESCO_JSON"; + + private static final String AUTHORITY_FILTER_FROM_JSON = "AUTHORITY_FILTER_FROM_JSON"; + + private static final String TENANT_FILTER_FROM_JSON = "TENANT_FILTER_FROM_JSON"; + + private static final String RERANK_QUERY_FROM_CONTEXT = "RERANK_QUERY_FROM_CONTEXT"; + + static final String languages[] = { + "af", "ar", "bg", "bn", "cs", "da", "de", "el", "en", "es", "et", "fa", "fi", "fr", "gu", + "he", "hi", "hr", "hu", "id", "it", "ja", "kn", "ko", "lt", "lv", "mk", "ml", "mr", "ne", + "nl", "no", "pa", "pl", "pt", "ro", "ru", "sk", "sl", "so", "sq", "sv", "sw", "ta", "te", + "th", "tl", "tr", "uk", "ur", "vi", "zh-cn", "zh-tw" + }; + + static + { + try { + List profileData = new ArrayList<>(); + for (String language : languages) { + InputStream stream = LangDetectLanguageIdentifierUpdateProcessor.class.getResourceAsStream("langdetect-profiles/" + language); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); + profileData.add(new String(IOUtils.toCharArray(reader))); + reader.close(); + } + DetectorFactory.loadProfile(profileData); + DetectorFactory.setSeed(0); + } catch (Exception e) { + throw new RuntimeException("Couldn't load profile data, will return empty languages always!", e); + } + } + + private boolean autoDetectQueryLocale = false; + + private HashSet autoDetectQueryLocales = new HashSet(); + + private HashSet fixedQueryLocales = new HashSet(); + + /** + * @param qstr String + * @param localParams SolrParams + * @param params SolrParams + * @param req SolrQueryRequest + * @param args + */ + public AbstractQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) + { + super(qstr, localParams, params, req); + if(args != null) + { + Object arg = args.get("autoDetectQueryLocale"); + if(arg != null) + { + this.autoDetectQueryLocale = Boolean.parseBoolean(arg.toString()); + } + + arg = args.get("autoDetectQueryLocales"); + if(arg != null) + { + String[] locales = arg.toString().split(","); + for(String locale : locales) + { + String mappedLanguage = isKnownLocale(locale); + if(mappedLanguage != null) + { + autoDetectQueryLocales.add(mappedLanguage); + } + } + } + + + arg = args.get("fixedQueryLocales"); + if(arg != null) + { + String[] locales = arg.toString().split(","); + for(String locale : locales) + { + String mappedLanguage = isKnownLocale(locale); + if(mappedLanguage != null) + { + fixedQueryLocales.add(mappedLanguage); + } + } + } + } + } + + private String isKnownLocale(String locale) + { + for(String test : languages) + { + if(locale.equalsIgnoreCase(test)) + { + return test; + } + } + return null; + } + + + protected Pair getSearchParameters() + { + SearchParameters searchParameters = new SearchParameters(); + + Boolean isFilter = Boolean.FALSE; + + Iterable streams = req.getContentStreams(); + + JSONObject json = (JSONObject) req.getContext().get(ALFRESCO_JSON); + + if (json == null) + { + if (streams != null) + { + + try + { + Reader reader = null; + for (ContentStream stream : streams) + { + reader = new BufferedReader(new InputStreamReader(stream.getStream(), "UTF-8")); + } + + // TODO - replace with streaming-based solution e.g. SimpleJSON ContentHandler + if (reader != null) + { + json = new JSONObject(new JSONTokener(reader)); + req.getContext().put(ALFRESCO_JSON, json); + } + } + catch (JSONException e) + { + // This is expected when there is no json element to the request + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("IO Error parsing query parameters", e); + } + } + } + + if (json != null) + { + try + { + if (getString() != null) + { + if (getString().equals(AUTHORITY_FILTER_FROM_JSON)) + { + isFilter =Boolean.TRUE; + + ArrayList tenantList = new ArrayList(1); + JSONArray tenants = json.getJSONArray("tenants"); + for (int i = 0; i < tenants.length(); i++) + { + String tenantString = tenants.getString(i); + tenantList.add(tenantString); + } + + ArrayList authorityList = new ArrayList(1); + JSONArray authorities = json.getJSONArray("authorities"); + for (int i = 0; i < authorities.length(); i++) + { + String authorityString = authorities.getString(i); + authorityList.add(authorityString); + } + + char separator = getSeparator(authorityList); + + StringBuilder authQuery = new StringBuilder(); + StringBuilder denyQuery = new StringBuilder(); + + for (String tenant : tenantList) + { + for (String authority : authorityList) + { + if (separator == 0) + { + if (authQuery.length() > 0) + { + authQuery.append(" "); + denyQuery.append(" "); + } + switch (AuthorityType.getAuthorityType(authority)) + { + case USER: + authQuery.append("|AUTHORITY:\"").append(authority).append("\""); + denyQuery.append("|DENIED:\"").append(authority).append("\""); + break; + case GROUP: + case EVERYONE: + case GUEST: + if (tenant.length() == 0) + { + // Default tenant matches 4.0 + authQuery.append("|AUTHORITY:\"").append(authority).append("\""); + denyQuery.append("|DENIED:\"").append(authority).append("\""); + } + else + { + authQuery.append("|AUTHORITY:\"").append(authority).append("@").append(tenant).append("\""); + denyQuery.append("|DENIED:\"").append(authority).append("@").append(tenant).append("\""); + } + break; + default: + authQuery.append("|AUTHORITY:\"").append(authority).append("\""); + denyQuery.append("|DENIED:\"").append(authority).append("\""); + break; + } + } + else + { + if(authQuery.length() == 0) + { + authset = true; + authQuery.append("|AUTHSET:\""); + denyQuery.append("|DENYSET:\""); + } + + switch (AuthorityType.getAuthorityType(authority)) + { + case USER: + authQuery.append(separator).append(authority); + denyQuery.append(separator).append(authority); + break; + case GROUP: + case EVERYONE: + case GUEST: + if (tenant.length() == 0) + { + // Default tenant matches 4.0 + authQuery.append(separator).append(authority); + denyQuery.append(separator).append(authority); + } + else + { + authQuery.append(separator).append(authority).append("@").append(tenant); + denyQuery.append(separator).append(authority).append("@").append(tenant); + } + break; + default: + authQuery.append(separator).append(authority); + denyQuery.append(separator).append(authority); + break; + } + } + + } + } + if(separator != 0) + { + authQuery.append("\""); + denyQuery.append("\""); + } + + if (authQuery.length() > 0) + { + // Default to true for safety reasons. + final boolean anyDenyDenies = json.optBoolean("anyDenyDenies", true); + + if (anyDenyDenies) + { + authQuery.insert(0, "("). + append(") AND NOT ("). + append(denyQuery). + append(")"); + // Record that the clause has been added. + // We only ever set this to true for solr4+ + req.getContext().put("processedDenies", Boolean.TRUE); + } + searchParameters.setQuery(authQuery.toString()); + } + } + else if (getString().equals(TENANT_FILTER_FROM_JSON)) + { + isFilter =Boolean.TRUE; + + ArrayList tenantList = new ArrayList(1); + JSONArray tenants = json.getJSONArray("tenants"); + for (int i = 0; i < tenants.length(); i++) + { + String tenantString = tenants.getString(i); + tenantList.add(tenantString); + } + + StringBuilder tenantQuery = new StringBuilder(); + for (String tenant : tenantList) + { + if (tenantQuery.length() > 0) + { + tenantQuery.append(" "); + } + + if (tenant.length() > 0) + + { + tenantQuery.append("|TENANT:\"").append(tenant).append("\""); + } + else + { + // TODO: Need to check for the default tenant or no tenant (4.0) or we force a reindex + // requirement later ... + // Better to add default tenant to the 4.0 index + tenantQuery.append("|TENANT:\"").append("_DEFAULT_").append("\""); + // tenantQuery.append(" |(+ISNODE:T -TENANT:*)"); + } + + } + searchParameters.setQuery(tenantQuery.toString()); + } + else if (getString().equals(RERANK_QUERY_FROM_CONTEXT)) + { + String searchTerm = getParam("spellcheck.q"); + searchParameters.setQuery(searchTerm); + } + } + else + { + String query = json.getString("query"); + if (query != null) + { + searchParameters.setQuery(query); + } + } + + JSONArray locales = json.getJSONArray("locales"); + for (int i = 0; i < locales.length(); i++) + { + String localeString = locales.getString(i); + Locale locale = DefaultTypeConverter.INSTANCE.convert(Locale.class, localeString); + searchParameters.addLocale(locale); + } + + JSONArray templates = json.getJSONArray("templates"); + for (int i = 0; i < templates.length(); i++) + { + JSONObject template = templates.getJSONObject(i); + String name = template.getString("name"); + String queryTemplate = template.getString("template"); + searchParameters.addQueryTemplate(name, queryTemplate); + } + + JSONArray allAttributes = json.getJSONArray("allAttributes"); + for (int i = 0; i < allAttributes.length(); i++) + { + String allAttribute = allAttributes.getString(i); + searchParameters.addAllAttribute(allAttribute); + } + + searchParameters.setDefaultFTSOperator(Operator.valueOf(json.getString("defaultFTSOperator"))); + searchParameters.setDefaultFTSFieldConnective(Operator.valueOf(json.getString("defaultFTSFieldOperator"))); + if (json.has("mlAnalaysisMode")) + { + searchParameters.setMlAnalaysisMode(MLAnalysisMode.valueOf(json.getString("mlAnalaysisMode"))); + } + searchParameters.setNamespace(json.getString("defaultNamespace")); + + JSONArray textAttributes = json.getJSONArray("textAttributes"); + for (int i = 0; i < textAttributes.length(); i++) + { + String textAttribute = textAttributes.getString(i); + searchParameters.addTextAttribute(textAttribute); + } + + + searchParameters.setQueryConsistency(QueryConsistency.valueOf(json.getString("queryConsistency"))); + + } + catch (JSONException e) + { + // This is expected when there is no json element to the request + } + } + + if (json != null) + { + if (log.isDebugEnabled()) + { + log.debug(json.toString()); + } + } + + if (searchParameters.getQuery() == null) + { + searchParameters.setQuery(getString()); + } + + if (searchParameters.getLocales().size() == 0) + { + searchParameters.addLocale(I18NUtil.getLocale()); + } + + String defaultField = getParam(CommonParams.DF); + if (defaultField != null) + { + searchParameters.setDefaultFieldName(defaultField); + } + + if(autoDetectQueryLocale) + { + String searchTerm = getParam("spellcheck.q"); + if (searchTerm != null) + { + searchParameters.setSearchTerm(searchTerm); + List detetcted = detectLanguage(searchTerm); + if((detetcted != null) && (detetcted.size() > 0)) + { + Locale detectedLocale = Locale.forLanguageTag(detetcted.get(0).getLangCode()); + if(localeIsNotIncluded(searchParameters, detectedLocale)) + { + searchParameters.addLocale( Locale.forLanguageTag(detectedLocale.getLanguage())); + } + } + + } + } + + if(fixedQueryLocales.size() > 0) + { + for(String locale : fixedQueryLocales) + { + searchParameters.addLocale( Locale.forLanguageTag(locale)); + } + } + + // searchParameters.setMlAnalaysisMode(getMLAnalysisMode()); + searchParameters.setNamespace(NamespaceService.CONTENT_MODEL_1_0_URI); + + return new Pair(searchParameters, isFilter); + } + + /** + * @param searchParameters SearchParameters + * @param detectedLocale Locale + * @return boolean + */ + private boolean localeIsNotIncluded(SearchParameters searchParameters, Locale detectedLocale) + { + for(Locale locale : searchParameters.getLocales()) + { + if(locale.getLanguage().equals(detectedLocale.getLanguage())) + { + return false; + } + } + return true; + } + + + /** + * @param authorityList ArrayList + * @return char + */ + private char getSeparator(ArrayList authorityList) + { + StringBuilder builder = new StringBuilder(); + for(String auth : authorityList) + { + builder.append(auth); + } + String test = builder.toString(); + + for(int i = 0; i < SEPARATORS.length; i++) + { + if(test.indexOf(SEPARATORS[i]) == -1) + { + return SEPARATORS[i]; + } + } + return 0; + } + + /* (non-Javadoc) + * @see org.apache.solr.search.QParser#getSort(boolean) + */ + @Override + public SortSpec getSortSpec(boolean useGlobalParams) throws SyntaxError + { + + getQuery(); // ensure query is parsed first + + String sortStr = null; + String startS = null; + String rowsS = null; + + if (localParams != null) { + sortStr = localParams.get(CommonParams.SORT); + startS = localParams.get(CommonParams.START); + rowsS = localParams.get(CommonParams.ROWS); + + // if any of these parameters are present, don't go back to the global params + if (sortStr != null || startS != null || rowsS != null) { + useGlobalParams = false; + } + } + + if (useGlobalParams) { + if (sortStr ==null) { + sortStr = params.get(CommonParams.SORT); + } + if (startS==null) { + startS = params.get(CommonParams.START); + } + if (rowsS==null) { + rowsS = params.get(CommonParams.ROWS); + } + } + + int start = startS != null ? Integer.parseInt(startS) : 0; + int rows = rowsS != null ? Integer.parseInt(rowsS) : 10; + + // Fix sort fields here + if(sortStr != null) + { + StringBuilder builder = new StringBuilder(); + StringBuilder propertyBuilder = null; + char c; + for(int i = 0; i < sortStr.length(); i++) + { + c = sortStr.charAt(i); + if(propertyBuilder == null) + { + if(!Character.isWhitespace(c) && (c != ',')) + { + propertyBuilder = new StringBuilder(); + propertyBuilder.append(c); + } + else + { + builder.append(c); + } + } + else + { + if(Character.isWhitespace(c) || (c == ',')) + { + String toAppend = AlfrescoSolrDataModel.getInstance().mapProperty(propertyBuilder.toString(), FieldUse.SORT, getReq()); + builder.append(toAppend); + builder.append(c); + propertyBuilder = null; + } + else + { + propertyBuilder.append(c); + } + } + } + if(propertyBuilder != null) + { + String toAppend = AlfrescoSolrDataModel.getInstance().mapProperty(propertyBuilder.toString(), FieldUse.SORT, getReq()); + builder.append(toAppend); + } + sortStr = builder.toString(); + } + + if(sortStr != null) + { + sortStr = sortStr.replaceAll("^ID(\\s)", "id$1"); + sortStr = sortStr.replaceAll("(\\s)ID(\\s)", "$1id$2"); + } + SortSpec sort = SortSpecParsing.parseSortSpec(sortStr, req); + + sort.setOffset(start); + sort.setCount(rows); + return sort; + } + + + + private List detectLanguage(String content) { + if (content.trim().length() == 0) { // to be consistent with the tika impl? + log.debug("No input text to detect language from, returning empty list"); + return Collections.emptyList(); + } + + try { + Detector detector = DetectorFactory.create(); + detector.append(content); + ArrayList langlist = detector.getProbabilities(); + ArrayList solrLangList = new ArrayList<>(); + for (Language l: langlist) + { + if((autoDetectQueryLocales.size() == 0) || (autoDetectQueryLocales.contains(l.lang))) + { + solrLangList.add(new DetectedLanguage(l.lang, l.prob)); + } + } + return solrLangList; + } catch (LangDetectException e) { + log.debug("Could not determine language, returning empty list: ", e); + return Collections.emptyList(); + } + } + + public class DetectedLanguage { + private final String langCode; + private final Double certainty; + + DetectedLanguage(String lang, Double certainty) { + this.langCode = lang; + this.certainty = certainty; + } + + /** + * Returns the detected language code + * @return language code as a string + */ + public String getLangCode() { + return langCode; + } + + /** + * Returns the detected certainty for this language + * @return certainty as a value between 0.0 and 1.0 where 1.0 is 100% certain + */ + public Double getCertainty() { + return certainty; + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractSolrCachingScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractSolrCachingScorer.java index 24a4af4e3..fa3704536 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractSolrCachingScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractSolrCachingScorer.java @@ -1,198 +1,198 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.stream.LongStream; - -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - - -/** - * @author Andy - * - */ -public abstract class AbstractSolrCachingScorer extends Scorer -{ - protected static class LongCache - { - private static int CACHE_SIZE = 1000000; - - private LongCache(){} - - static final long cache[] = LongStream.range(0, CACHE_SIZE).toArray(); - } - - protected static Long getLong(long l) { - if(l > LongCache.CACHE_SIZE) - { - return Long.valueOf(l); - } - else - { - return LongCache.cache[(int)l]; - } - } - - - - SolrCachingScorerDoIdSetIterator iterator; - - AbstractSolrCachingScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight); - iterator = new SolrCachingScorerDoIdSetIterator(in, context, searcher); - } - - @Override - public float score() throws IOException - { - return 1.0f; - } - - // TODO: implement - @Override - public int freq() throws IOException - { - return 1; - } - - @Override - public int docID() - { - return iterator.docID(); - } - - @Override - public DocIdSetIterator iterator() - { - return iterator; - } - - - - private static class SolrCachingScorerDoIdSetIterator extends DocIdSetIterator - { - BitDocSet matches; - - int doc = -1; - - FixedBitSet bitSet; - - LeafReaderContext context; - - SolrCachingScorerDoIdSetIterator(DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - this.context = context; - - if (in instanceof BitDocSet) - { - matches = (BitDocSet) in; - } - else - { - this.matches = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - for (DocIterator it = in.iterator(); it.hasNext(); /* */) - { - matches.addUnique(it.nextDoc()); - } - } - bitSet = matches.getBits(); - - doc = getBase() - 1; - } - - private boolean next() - { - if(doc+1 < bitSet.length()) - { - doc = bitSet.nextSetBit(doc+1); - return (doc != NO_MORE_DOCS) && (doc < (getBase() + context.reader().maxDoc())); - } - else - { - return false; - } - } - - private int getBase() - { - return context.docBase; - } - - @Override - public int nextDoc() throws IOException - { - while (next()) - { - return docID(); - } - return NO_MORE_DOCS; - } - - - @Override - public int docID() - { - // TODO: check this expression as for next() - if ((doc > -1) && (doc != NO_MORE_DOCS)) - { - return doc - getBase(); - } - return doc; - } - - @Override - public int advance(int target) throws IOException - { - while (next()) - { - final int current = docID(); - if (current >= target) - { - return current; - } - } - return NO_MORE_DOCS; - } - - // TODO: implement - @Override - public long cost() - { - return 1; - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.stream.LongStream; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + + +/** + * @author Andy + * + */ +public abstract class AbstractSolrCachingScorer extends Scorer +{ + protected static class LongCache + { + private static int CACHE_SIZE = 1000000; + + private LongCache(){} + + static final long cache[] = LongStream.range(0, CACHE_SIZE).toArray(); + } + + protected static Long getLong(long l) { + if(l > LongCache.CACHE_SIZE) + { + return Long.valueOf(l); + } + else + { + return LongCache.cache[(int)l]; + } + } + + + + SolrCachingScorerDoIdSetIterator iterator; + + AbstractSolrCachingScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight); + iterator = new SolrCachingScorerDoIdSetIterator(in, context, searcher); + } + + @Override + public float score() throws IOException + { + return 1.0f; + } + + // TODO: implement + @Override + public int freq() throws IOException + { + return 1; + } + + @Override + public int docID() + { + return iterator.docID(); + } + + @Override + public DocIdSetIterator iterator() + { + return iterator; + } + + + + private static class SolrCachingScorerDoIdSetIterator extends DocIdSetIterator + { + BitDocSet matches; + + int doc = -1; + + FixedBitSet bitSet; + + LeafReaderContext context; + + SolrCachingScorerDoIdSetIterator(DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + this.context = context; + + if (in instanceof BitDocSet) + { + matches = (BitDocSet) in; + } + else + { + this.matches = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + for (DocIterator it = in.iterator(); it.hasNext(); /* */) + { + matches.addUnique(it.nextDoc()); + } + } + bitSet = matches.getBits(); + + doc = getBase() - 1; + } + + private boolean next() + { + if(doc+1 < bitSet.length()) + { + doc = bitSet.nextSetBit(doc+1); + return (doc != NO_MORE_DOCS) && (doc < (getBase() + context.reader().maxDoc())); + } + else + { + return false; + } + } + + private int getBase() + { + return context.docBase; + } + + @Override + public int nextDoc() throws IOException + { + while (next()) + { + return docID(); + } + return NO_MORE_DOCS; + } + + + @Override + public int docID() + { + // TODO: check this expression as for next() + if ((doc > -1) && (doc != NO_MORE_DOCS)) + { + return doc - getBase(); + } + return doc; + } + + @Override + public int advance(int target) throws IOException + { + while (next()) + { + final int current = docID(); + if (current >= target) + { + return current; + } + } + return NO_MORE_DOCS; + } + + // TODO: implement + @Override + public long cost() + { + return 1; + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractStructuredFieldPosition.java index 6b266f3ef..ce05a219a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AbstractStructuredFieldPosition.java @@ -1,169 +1,169 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -public abstract class AbstractStructuredFieldPosition implements StructuredFieldPosition -{ - private String termText; - - private boolean isTerminal; - - private boolean isAbsolute; - - private CachingTermPositions tps; - - public AbstractStructuredFieldPosition(String termText, boolean isTerminal, boolean isAbsolute) - { - super(); - this.termText = termText; - this.isTerminal = isTerminal; - this.isAbsolute = isAbsolute; - } - - public boolean isTerminal() - { - return isTerminal; - } - - protected void setTerminal(boolean isTerminal) - { - this.isTerminal = isTerminal; - } - - public boolean isAbsolute() - { - return isAbsolute; - } - - public boolean isRelative() - { - return !isAbsolute; - } - - public String getTermText() - { - return termText; - } - - public int getPosition() - { - return -1; - } - - public void setCachingTermPositions(CachingTermPositions tps) - { - this.tps = tps; - } - - public CachingTermPositions getCachingTermPositions() - { - return this.tps; - } - - - - public boolean allowsLinkingBySelf() - { - return false; - } - - public boolean allowslinkingByParent() - { - return true; - } - - public boolean linkParent() - { - return true; - } - - public boolean linkSelf() - { - return false; - } - - public String toString() - { - StringBuffer buffer = new StringBuffer(256); - buffer.append(getDescription()); - buffer.append("<"+getTermText()+"> at "+getPosition()); - buffer.append(" Terminal = "+isTerminal()); - buffer.append(" Absolute = "+isAbsolute()); - return buffer.toString(); - } - - public abstract String getDescription(); - - public boolean isDescendant() - { - return false; - } - - public boolean matchesAll() - { - return getCachingTermPositions() == null; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + (isAbsolute ? 1231 : 1237); - result = prime * result + (isTerminal ? 1231 : 1237); - result = prime * result + ((termText == null) ? 0 : termText.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - AbstractStructuredFieldPosition other = (AbstractStructuredFieldPosition) obj; - if (isAbsolute != other.isAbsolute) - return false; - if (isTerminal != other.isTerminal) - return false; - if (termText == null) - { - if (other.termText != null) - return false; - } - else if (!termText.equals(other.termText)) - return false; - return true; - } - - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +public abstract class AbstractStructuredFieldPosition implements StructuredFieldPosition +{ + private String termText; + + private boolean isTerminal; + + private boolean isAbsolute; + + private CachingTermPositions tps; + + public AbstractStructuredFieldPosition(String termText, boolean isTerminal, boolean isAbsolute) + { + super(); + this.termText = termText; + this.isTerminal = isTerminal; + this.isAbsolute = isAbsolute; + } + + public boolean isTerminal() + { + return isTerminal; + } + + protected void setTerminal(boolean isTerminal) + { + this.isTerminal = isTerminal; + } + + public boolean isAbsolute() + { + return isAbsolute; + } + + public boolean isRelative() + { + return !isAbsolute; + } + + public String getTermText() + { + return termText; + } + + public int getPosition() + { + return -1; + } + + public void setCachingTermPositions(CachingTermPositions tps) + { + this.tps = tps; + } + + public CachingTermPositions getCachingTermPositions() + { + return this.tps; + } + + + + public boolean allowsLinkingBySelf() + { + return false; + } + + public boolean allowslinkingByParent() + { + return true; + } + + public boolean linkParent() + { + return true; + } + + public boolean linkSelf() + { + return false; + } + + public String toString() + { + StringBuffer buffer = new StringBuffer(256); + buffer.append(getDescription()); + buffer.append("<"+getTermText()+"> at "+getPosition()); + buffer.append(" Terminal = "+isTerminal()); + buffer.append(" Absolute = "+isAbsolute()); + return buffer.toString(); + } + + public abstract String getDescription(); + + public boolean isDescendant() + { + return false; + } + + public boolean matchesAll() + { + return getCachingTermPositions() == null; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (isAbsolute ? 1231 : 1237); + result = prime * result + (isTerminal ? 1231 : 1237); + result = prime * result + ((termText == null) ? 0 : termText.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AbstractStructuredFieldPosition other = (AbstractStructuredFieldPosition) obj; + if (isAbsolute != other.isAbsolute) + return false; + if (isTerminal != other.isTerminal) + return false; + if (termText == null) + { + if (other.termText != null) + return false; + } + else if (!termText.equals(other.termText)) + return false; + return true; + } + + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoDefaultTextFields.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoDefaultTextFields.java index 805271cf8..914cedd86 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoDefaultTextFields.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoDefaultTextFields.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoFTSQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoFTSQParserPlugin.java index 979f6e134..b8355fde2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoFTSQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoFTSQParserPlugin.java @@ -1,153 +1,153 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import org.alfresco.repo.search.impl.parsers.FTSQueryParser.RerankPhase; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.util.Pair; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class AlfrescoFTSQParserPlugin extends QParserPlugin -{ - protected final static Logger log = LoggerFactory.getLogger(AlfrescoFTSQParserPlugin.class); - - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new AlfrescoFTSQParser(qstr, localParams, params, req, args); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - @Override - public void init(NamedList args) - { - this.args = args; - } - - public static class AlfrescoFTSQParser extends AbstractQParser - { - private Log logger = LogFactory.getLog(AlfrescoFTSQParser.class); - private RerankPhase rerankPhase = RerankPhase.SINGLE_PASS_WITH_AUTO_PHRASE; - private boolean postfilter; - - public AlfrescoFTSQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) - { - super(qstr, localParams, params, req, args); - Object arg = args.get("rerankPhase"); - if(arg != null) - { - rerankPhase = RerankPhase.valueOf(arg.toString()); - } - - /* - * This turns on the postFilter - */ - - postfilter = Boolean.parseBoolean(System.getProperty("alfresco.postfilter", - req.getCore().getCoreDescriptor().getCoreProperty("alfresco.postfilter", - "true"))); - logger.debug("Post filter value: " + postfilter); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - try - { - Pair searchParametersAndFilter = getSearchParameters(); - - Query query = AlfrescoSolrDataModel.getInstance().getFTSQuery(searchParametersAndFilter, req, rerankPhase); - if(log.isDebugEnabled()) - { - log.debug("AFTS QP query as lucene:\t "+query); - } - - if(authset && postfilter) - { - /* - * The cost of 200 turns on the postfilter inside Solr - * The postfilter query pulls out all the post filters in the - * query and applies them. - */ - return new PostFilterQuery(200, query); - } - - /* - * This assertion is designed to ensure that if the System property alfresco.postfilter - * is set to true then the PostFilter was actually used. - * - * If authset is false the assertion will short circuit before it tests the - * alfresco.postfilter property because the PostFilter is only applied for authset queries. - * - * If authset is true, the assertion checks to make sure the alfresco.postfilter property is either - * false or unset. If the alfresco.postfilter property is true it should not have gotten to this point - * of the code. - */ - - assert (authset == false || - Boolean.parseBoolean(System.getProperty("alfresco.postfilter","false")) == false); - - return query; - } - catch(ParseException e) - { - throw new SyntaxError(e); - } - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import org.alfresco.repo.search.impl.parsers.FTSQueryParser.RerankPhase; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class AlfrescoFTSQParserPlugin extends QParserPlugin +{ + protected final static Logger log = LoggerFactory.getLogger(AlfrescoFTSQParserPlugin.class); + + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new AlfrescoFTSQParser(qstr, localParams, params, req, args); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + @Override + public void init(NamedList args) + { + this.args = args; + } + + public static class AlfrescoFTSQParser extends AbstractQParser + { + private Log logger = LogFactory.getLog(AlfrescoFTSQParser.class); + private RerankPhase rerankPhase = RerankPhase.SINGLE_PASS_WITH_AUTO_PHRASE; + private boolean postfilter; + + public AlfrescoFTSQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) + { + super(qstr, localParams, params, req, args); + Object arg = args.get("rerankPhase"); + if(arg != null) + { + rerankPhase = RerankPhase.valueOf(arg.toString()); + } + + /* + * This turns on the postFilter + */ + + postfilter = Boolean.parseBoolean(System.getProperty("alfresco.postfilter", + req.getCore().getCoreDescriptor().getCoreProperty("alfresco.postfilter", + "true"))); + logger.debug("Post filter value: " + postfilter); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + try + { + Pair searchParametersAndFilter = getSearchParameters(); + + Query query = AlfrescoSolrDataModel.getInstance().getFTSQuery(searchParametersAndFilter, req, rerankPhase); + if(log.isDebugEnabled()) + { + log.debug("AFTS QP query as lucene:\t "+query); + } + + if(authset && postfilter) + { + /* + * The cost of 200 turns on the postfilter inside Solr + * The postfilter query pulls out all the post filters in the + * query and applies them. + */ + return new PostFilterQuery(200, query); + } + + /* + * This assertion is designed to ensure that if the System property alfresco.postfilter + * is set to true then the PostFilter was actually used. + * + * If authset is false the assertion will short circuit before it tests the + * alfresco.postfilter property because the PostFilter is only applied for authset queries. + * + * If authset is true, the assertion checks to make sure the alfresco.postfilter property is either + * false or unset. If the alfresco.postfilter property is true it should not have gotten to this point + * of the code. + */ + + assert (authset == false || + Boolean.parseBoolean(System.getProperty("alfresco.postfilter","false")) == false); + + return query; + } + catch(ParseException e) + { + throw new SyntaxError(e); + } + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoLuceneQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoLuceneQParserPlugin.java index b7756ea76..513466758 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoLuceneQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoLuceneQParserPlugin.java @@ -1,149 +1,149 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import org.alfresco.repo.search.impl.parsers.FTSQueryParser; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.ContextAwareQuery; -import org.alfresco.util.Pair; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class AlfrescoLuceneQParserPlugin extends QParserPlugin -{ - protected final static Logger log = LoggerFactory.getLogger(AlfrescoLuceneQParserPlugin.class); - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new AlfrescoLuceneQParser(qstr, localParams, params, req, args); - - } - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - public void init(NamedList args) - { - this.args = args; - } - - public static class AlfrescoLuceneQParser extends AbstractQParser - { - public AlfrescoLuceneQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) - { - super(qstr, localParams, params, req, args); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - Pair searchParametersAndFilter = getSearchParameters(); - SearchParameters searchParameters = searchParametersAndFilter.getFirst(); - Boolean isFilter = searchParametersAndFilter.getSecond(); - - Solr4QueryParser qp = AlfrescoSolrDataModel.getInstance().getLuceneQueryParser(searchParameters, req, FTSQueryParser.RerankPhase.SINGLE_PASS_WITH_AUTO_PHRASE); - Query query; - try - { - // escape / not in a string and not already escaped - String escapedQ = escape(searchParameters.getQuery()); - query = qp.parse(escapedQ); - } - catch (ParseException pe) - { - throw new SyntaxError(pe); - } - ContextAwareQuery contextAwareQuery = new ContextAwareQuery(query, Boolean.TRUE.equals(isFilter) ? null : searchParameters); - if(log.isDebugEnabled()) - { - log.debug("Lucene QP query as lucene:\t "+contextAwareQuery); - } - return contextAwareQuery; - } - - protected String escape(String in) - { - if (in.indexOf('/') == -1) - { - // If we have no forward slashes, there is nothing to do. - return in; - } - - StringBuilder sb = new StringBuilder(in); - boolean quoted = false; - boolean escaped = false; - for (int i = 0; i < sb.length(); i++) - { - char c = sb.charAt(i); - if (c == '"') - { - quoted = !quoted; - continue; - } - else if (c == '\\') - { - escaped = true; - continue; - } - if (c == '/' && !quoted && !escaped) - { - sb.insert(i, "\\"); - i++; // jump over modification, avoid infinite loop. - } - escaped = false; - } - String out = sb.toString(); - return out; - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.ContextAwareQuery; +import org.alfresco.util.Pair; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class AlfrescoLuceneQParserPlugin extends QParserPlugin +{ + protected final static Logger log = LoggerFactory.getLogger(AlfrescoLuceneQParserPlugin.class); + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new AlfrescoLuceneQParser(qstr, localParams, params, req, args); + + } + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + public void init(NamedList args) + { + this.args = args; + } + + public static class AlfrescoLuceneQParser extends AbstractQParser + { + public AlfrescoLuceneQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) + { + super(qstr, localParams, params, req, args); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + Pair searchParametersAndFilter = getSearchParameters(); + SearchParameters searchParameters = searchParametersAndFilter.getFirst(); + Boolean isFilter = searchParametersAndFilter.getSecond(); + + Solr4QueryParser qp = AlfrescoSolrDataModel.getInstance().getLuceneQueryParser(searchParameters, req, FTSQueryParser.RerankPhase.SINGLE_PASS_WITH_AUTO_PHRASE); + Query query; + try + { + // escape / not in a string and not already escaped + String escapedQ = escape(searchParameters.getQuery()); + query = qp.parse(escapedQ); + } + catch (ParseException pe) + { + throw new SyntaxError(pe); + } + ContextAwareQuery contextAwareQuery = new ContextAwareQuery(query, Boolean.TRUE.equals(isFilter) ? null : searchParameters); + if(log.isDebugEnabled()) + { + log.debug("Lucene QP query as lucene:\t "+contextAwareQuery); + } + return contextAwareQuery; + } + + protected String escape(String in) + { + if (in.indexOf('/') == -1) + { + // If we have no forward slashes, there is nothing to do. + return in; + } + + StringBuilder sb = new StringBuilder(in); + boolean quoted = false; + boolean escaped = false; + for (int i = 0; i < sb.length(); i++) + { + char c = sb.charAt(i); + if (c == '"') + { + quoted = !quoted; + continue; + } + else if (c == '\\') + { + escaped = true; + continue; + } + if (c == '/' && !quoted && !escaped) + { + sb.insert(i, "\\"); + i++; // jump over modification, avoid infinite loop. + } + escaped = false; + } + String out = sb.toString(); + return out; + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoReRankQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoReRankQParserPlugin.java index d15ba04b0..aa45b708c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoReRankQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AlfrescoReRankQParserPlugin.java @@ -1,522 +1,522 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.LeafCollector; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.QueryRescorer; -import org.apache.lucene.search.ScoreDoc; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.search.TopDocsCollector; -import org.apache.lucene.search.TopFieldCollector; -import org.apache.lucene.search.TopScoreDocCollector; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.BytesRef; -import org.apache.solr.common.SolrException; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.handler.component.MergeStrategy; -import org.apache.solr.handler.component.QueryElevationComponent; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.request.SolrRequestInfo; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.RankQuery; -import org.apache.solr.search.QueryCommand; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.SyntaxError; - -import com.carrotsearch.hppc.IntFloatHashMap; -import com.carrotsearch.hppc.IntIntHashMap; - -/* -* -* Syntax: q=*:*&rq={!rerank reRankQuery=$rqq reRankDocs=300 reRankWeight=3} -* -*/ - -public class AlfrescoReRankQParserPlugin extends QParserPlugin { - - public static final String NAME = "rerank"; - private static Query defaultQuery = new MatchAllDocsQuery(); - - public void init(NamedList args) { - } - - public QParser createParser(String query, SolrParams localParams, SolrParams params, SolrQueryRequest req) { - return new ReRankQParser(query, localParams, params, req); - } - - private class ReRankQParser extends QParser { - - public ReRankQParser(String query, SolrParams localParams, SolrParams params, SolrQueryRequest req) { - super(query, localParams, params, req); - } - - public Query parse() throws SyntaxError { - String reRankQueryString = localParams.get("reRankQuery"); - boolean scale = localParams.getBool("scale", false); - QParser reRankParser = QParser.getParser(reRankQueryString, null, req); - Query reRankQuery = reRankParser.parse(); - - int reRankDocs = localParams.getInt("reRankDocs", 200); - reRankDocs = Math.max(1, reRankDocs); // - - double reRankWeight = localParams.getDouble("reRankWeight",2.0d); - - int start = params.getInt(CommonParams.START,0); - int rows = params.getInt(CommonParams.ROWS,10); - int length = start+rows; - return new ReRankQuery(reRankQuery, reRankDocs, reRankWeight, length, scale); - } - } - - private class ReRankQuery extends RankQuery { - private Query mainQuery = defaultQuery; - private Query reRankQuery; - private int reRankDocs; - private int length; - private double reRankWeight; - private boolean scale; - private Map boostedPriority; - - public int hashCode() { - return mainQuery.hashCode()+reRankQuery.hashCode()+(int)reRankWeight+reRankDocs+(scale ? 1 : 0); - } - - public boolean equals(Object o) { - if(o instanceof ReRankQuery) { - ReRankQuery rrq = (ReRankQuery)o; - return (mainQuery.equals(rrq.mainQuery) && - reRankQuery.equals(rrq.reRankQuery) && - reRankWeight == rrq.reRankWeight && - reRankDocs == rrq.reRankDocs && - scale == rrq.scale); - } - return false; - } - - public ReRankQuery(Query reRankQuery, int reRankDocs, double reRankWeight, int length, boolean scale) { - this.reRankQuery = reRankQuery; - this.reRankDocs = reRankDocs; - this.reRankWeight = reRankWeight; - this.length = length; - this.scale = scale; - } - - public RankQuery wrap(Query _mainQuery) { - if(_mainQuery != null){ - this.mainQuery = _mainQuery; - } - return this; - } - - public MergeStrategy getMergeStrategy() { - return null; - } - - public TopDocsCollector getTopDocsCollector(int len, QueryCommand cmd, IndexSearcher searcher) throws IOException { - - if(this.boostedPriority == null) { - SolrRequestInfo info = SolrRequestInfo.getRequestInfo(); - if(info != null) { - Map context = info.getReq().getContext(); - this.boostedPriority = (Map)context.get(QueryElevationComponent.BOOSTED_PRIORITY); - } - } - - return new ReRankCollector(reRankDocs, length, reRankQuery, reRankWeight, cmd, searcher, boostedPriority, scale); - } - - public String toString(String s) { - return "{!rerank mainQuery='"+mainQuery.toString()+ - "' reRankQuery='"+reRankQuery.toString()+ - "' reRankDocs="+reRankDocs+ - " reRankWeigh="+reRankWeight+"}"; - } - - - public Query rewrite(IndexReader reader) throws IOException { - Query q = mainQuery.rewrite(reader); - if(q == mainQuery) { - return this; - } else { - return clone().wrap(q); - } - } - - public ReRankQuery clone() { - ReRankQuery clonedQuery = new ReRankQuery(reRankQuery, reRankDocs, reRankWeight, length, scale); - return clonedQuery; - } - - - public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException{ - return new ReRankWeight(mainQuery, reRankQuery, reRankWeight, searcher); - } - } - - private class ReRankWeight extends Weight{ - private Query reRankQuery; - private IndexSearcher searcher; - private Weight mainWeight; - private double reRankWeight; - - public ReRankWeight(Query mainQuery, Query reRankQuery, double reRankWeight, IndexSearcher searcher) throws IOException { - super(reRankQuery); - this.reRankQuery = reRankQuery; - this.searcher = searcher; - this.reRankWeight = reRankWeight; - this.mainWeight = mainQuery.createWeight(searcher, true); - } - - @Override - public void extractTerms(Set terms) { - this.mainWeight.extractTerms(terms); - - } - - public float getValueForNormalization() throws IOException { - return mainWeight.getValueForNormalization(); - } - - public Scorer scorer(LeafReaderContext context) throws IOException { - return mainWeight.scorer(context); - } - - public void normalize(float norm, float topLevelBoost) { - mainWeight.normalize(norm, topLevelBoost); - } - - public Explanation explain(LeafReaderContext context, int doc) throws IOException { - Explanation mainExplain = mainWeight.explain(context, doc); - return new QueryRescorer(getQuery()) { - @Override - protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { - float score = firstPassScore; - if (secondPassMatches) { - score += reRankWeight * secondPassScore; - } - return score; - } - }.explain(searcher, mainExplain, context.docBase+doc); - } - - } - - private class ReRankCollector extends TopDocsCollector { - - private Query reRankQuery; - private TopDocsCollector mainCollector; - private IndexSearcher searcher; - private int reRankDocs; - private int length; - private double reRankWeight; - private Map boostedPriority; - private float minScore = Float.MAX_VALUE; - private float maxScore = -Float.MAX_VALUE; - private Scorer localScorer; - private boolean scale; - - public ReRankCollector(int reRankDocs, - int length, - Query reRankQuery, - double reRankWeight, - QueryCommand cmd, - IndexSearcher searcher, - Map boostedPriority, - boolean scale) throws IOException { - super(null); - this.reRankQuery = reRankQuery; - this.reRankDocs = reRankDocs; - this.length = length; - this.boostedPriority = boostedPriority; - this.scale = scale; - Sort sort = cmd.getSort(); - if(sort == null) { - this.mainCollector = TopScoreDocCollector.create(Math.max(this.reRankDocs, length), null); - } else { - sort = sort.rewrite(searcher); - this.mainCollector = TopFieldCollector.create(sort, Math.max(this.reRankDocs, length), null, false, true, true); - } - this.searcher = searcher; - this.reRankWeight = reRankWeight; - } - - public int getTotalHits() { - return mainCollector.getTotalHits(); - } - - public TopDocs topDocs(int start, int howMany) { - - try { - - TopDocs mainDocs = mainCollector.topDocs(0, Math.max(reRankDocs, length)); - - if(mainDocs.totalHits == 0 || mainDocs.scoreDocs.length == 0) { - return mainDocs; - } - - if(reRankDocs == 0) { - scaleScores(mainDocs, new HashMap()); - return mainDocs; - } - - if(boostedPriority != null) { - SolrRequestInfo info = SolrRequestInfo.getRequestInfo(); - Map requestContext = null; - if(info != null) { - requestContext = info.getReq().getContext(); - } - - IntIntHashMap boostedDocs = QueryElevationComponent.getBoostDocs((SolrIndexSearcher)searcher, boostedPriority, requestContext); - - ScoreDoc[] mainScoreDocs = mainDocs.scoreDocs; - ScoreDoc[] reRankScoreDocs = new ScoreDoc[Math.min(mainScoreDocs.length, reRankDocs)]; - System.arraycopy(mainScoreDocs,0,reRankScoreDocs,0,reRankScoreDocs.length); - - mainDocs.scoreDocs = reRankScoreDocs; - - Map scoreMap = getScoreMap(mainDocs.scoreDocs, mainDocs.scoreDocs.length); - - TopDocs rescoredDocs = new QueryRescorer(reRankQuery) { - @Override - protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { - float score = firstPassScore; - if (secondPassMatches) { - score += reRankWeight * secondPassScore; - } - return score; - } - }.rescore(searcher, mainDocs, mainDocs.scoreDocs.length); - - Arrays.sort(rescoredDocs.scoreDocs, new BoostedComp(boostedDocs, mainDocs.scoreDocs, rescoredDocs.getMaxScore())); - - //Lower howMany if we've collected fewer documents. - howMany = Math.min(howMany, mainScoreDocs.length); - - if(howMany == rescoredDocs.scoreDocs.length) { - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; // Just return the rescoredDocs - } else if(howMany > rescoredDocs.scoreDocs.length) { - //We need to return more then we've reRanked, so create the combined page. - ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; - System.arraycopy(mainScoreDocs, 0, scoreDocs, 0, scoreDocs.length); //lay down the initial docs - System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, rescoredDocs.scoreDocs.length);//overlay the re-ranked docs. - rescoredDocs.scoreDocs = scoreDocs; - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; - } else { - //We've rescored more then we need to return. - ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; - System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, howMany); - rescoredDocs.scoreDocs = scoreDocs; - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; - } - - } else { - - ScoreDoc[] mainScoreDocs = mainDocs.scoreDocs; - - /* - * Create the array for the reRankScoreDocs. - */ - ScoreDoc[] reRankScoreDocs = new ScoreDoc[Math.min(mainScoreDocs.length, reRankDocs)]; - - /* - * Copy the initial results into the reRankScoreDocs array. - */ - System.arraycopy(mainScoreDocs, 0, reRankScoreDocs, 0, reRankScoreDocs.length); - - mainDocs.scoreDocs = reRankScoreDocs; - - Map scoreMap = getScoreMap(mainDocs.scoreDocs, mainDocs.scoreDocs.length); - - TopDocs rescoredDocs = new QueryRescorer(reRankQuery) { - @Override - protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { - float score = firstPassScore; - if (secondPassMatches) { - score += reRankWeight * secondPassScore; - } - return score; - } - }.rescore(searcher, mainDocs, mainDocs.scoreDocs.length); - - //Lower howMany to return if we've collected fewer documents. - howMany = Math.min(howMany, mainScoreDocs.length); - - if(howMany == rescoredDocs.scoreDocs.length) { - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; // Just return the rescoredDocs - } else if(howMany > rescoredDocs.scoreDocs.length) { - - //We need to return more then we've reRanked, so create the combined page. - ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; - //lay down the initial docs - System.arraycopy(mainScoreDocs, 0, scoreDocs, 0, scoreDocs.length); - //overlay the rescoreds docs - System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, rescoredDocs.scoreDocs.length); - rescoredDocs.scoreDocs = scoreDocs; - if(scale) { - assert(scoreMap != null); - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; - } else { - //We've rescored more then we need to return. - ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; - System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, howMany); - rescoredDocs.scoreDocs = scoreDocs; - if(scale) { - scaleScores(rescoredDocs, scoreMap); - } - return rescoredDocs; - } - } - } catch (Exception e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); - } - } - - @Override - public LeafCollector getLeafCollector(LeafReaderContext context) - throws IOException { - return mainCollector.getLeafCollector(context); - } - - @Override - public boolean needsScores() { - return true; - } - } - - private void scaleScores(TopDocs topDocs, Map scoreMap) - { - float maxScore = topDocs.getMaxScore(); - float newMax = -Float.MAX_VALUE; - - for(ScoreDoc scoreDoc : topDocs.scoreDocs) { - float score = scoreDoc.score; - - Float oldScore = scoreMap.get(scoreDoc.doc); - - // check if the score has been changed after rescoring - boolean rescored = oldScore != null && score != oldScore; - - // If maxScore is different from 0, the score is divided by maxscore - scoreDoc.score = score / (maxScore != 0? maxScore : 1); - - // If the document has been rescored, the score is increased by 1. - // This results in having all the rescored element scores in (1,2] range. - if (rescored) - { - scoreDoc.score += 1; - } - - if(scoreDoc.score > newMax) - { - newMax = scoreDoc.score; - } - } - - assert(newMax <= 2); - topDocs.setMaxScore(newMax); - } - - private Map getScoreMap(ScoreDoc[] scoreDocs, int num) { - Map scoreMap = new HashMap(); - for(int i=0; i score2) { - return -1; - } else if(score1 < score2) { - return 1; - } else { - return 0; - } - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.LeafCollector; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryRescorer; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.TopDocsCollector; +import org.apache.lucene.search.TopFieldCollector; +import org.apache.lucene.search.TopScoreDocCollector; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.component.MergeStrategy; +import org.apache.solr.handler.component.QueryElevationComponent; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestInfo; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.RankQuery; +import org.apache.solr.search.QueryCommand; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SyntaxError; + +import com.carrotsearch.hppc.IntFloatHashMap; +import com.carrotsearch.hppc.IntIntHashMap; + +/* +* +* Syntax: q=*:*&rq={!rerank reRankQuery=$rqq reRankDocs=300 reRankWeight=3} +* +*/ + +public class AlfrescoReRankQParserPlugin extends QParserPlugin { + + public static final String NAME = "rerank"; + private static Query defaultQuery = new MatchAllDocsQuery(); + + public void init(NamedList args) { + } + + public QParser createParser(String query, SolrParams localParams, SolrParams params, SolrQueryRequest req) { + return new ReRankQParser(query, localParams, params, req); + } + + private class ReRankQParser extends QParser { + + public ReRankQParser(String query, SolrParams localParams, SolrParams params, SolrQueryRequest req) { + super(query, localParams, params, req); + } + + public Query parse() throws SyntaxError { + String reRankQueryString = localParams.get("reRankQuery"); + boolean scale = localParams.getBool("scale", false); + QParser reRankParser = QParser.getParser(reRankQueryString, null, req); + Query reRankQuery = reRankParser.parse(); + + int reRankDocs = localParams.getInt("reRankDocs", 200); + reRankDocs = Math.max(1, reRankDocs); // + + double reRankWeight = localParams.getDouble("reRankWeight",2.0d); + + int start = params.getInt(CommonParams.START,0); + int rows = params.getInt(CommonParams.ROWS,10); + int length = start+rows; + return new ReRankQuery(reRankQuery, reRankDocs, reRankWeight, length, scale); + } + } + + private class ReRankQuery extends RankQuery { + private Query mainQuery = defaultQuery; + private Query reRankQuery; + private int reRankDocs; + private int length; + private double reRankWeight; + private boolean scale; + private Map boostedPriority; + + public int hashCode() { + return mainQuery.hashCode()+reRankQuery.hashCode()+(int)reRankWeight+reRankDocs+(scale ? 1 : 0); + } + + public boolean equals(Object o) { + if(o instanceof ReRankQuery) { + ReRankQuery rrq = (ReRankQuery)o; + return (mainQuery.equals(rrq.mainQuery) && + reRankQuery.equals(rrq.reRankQuery) && + reRankWeight == rrq.reRankWeight && + reRankDocs == rrq.reRankDocs && + scale == rrq.scale); + } + return false; + } + + public ReRankQuery(Query reRankQuery, int reRankDocs, double reRankWeight, int length, boolean scale) { + this.reRankQuery = reRankQuery; + this.reRankDocs = reRankDocs; + this.reRankWeight = reRankWeight; + this.length = length; + this.scale = scale; + } + + public RankQuery wrap(Query _mainQuery) { + if(_mainQuery != null){ + this.mainQuery = _mainQuery; + } + return this; + } + + public MergeStrategy getMergeStrategy() { + return null; + } + + public TopDocsCollector getTopDocsCollector(int len, QueryCommand cmd, IndexSearcher searcher) throws IOException { + + if(this.boostedPriority == null) { + SolrRequestInfo info = SolrRequestInfo.getRequestInfo(); + if(info != null) { + Map context = info.getReq().getContext(); + this.boostedPriority = (Map)context.get(QueryElevationComponent.BOOSTED_PRIORITY); + } + } + + return new ReRankCollector(reRankDocs, length, reRankQuery, reRankWeight, cmd, searcher, boostedPriority, scale); + } + + public String toString(String s) { + return "{!rerank mainQuery='"+mainQuery.toString()+ + "' reRankQuery='"+reRankQuery.toString()+ + "' reRankDocs="+reRankDocs+ + " reRankWeigh="+reRankWeight+"}"; + } + + + public Query rewrite(IndexReader reader) throws IOException { + Query q = mainQuery.rewrite(reader); + if(q == mainQuery) { + return this; + } else { + return clone().wrap(q); + } + } + + public ReRankQuery clone() { + ReRankQuery clonedQuery = new ReRankQuery(reRankQuery, reRankDocs, reRankWeight, length, scale); + return clonedQuery; + } + + + public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException{ + return new ReRankWeight(mainQuery, reRankQuery, reRankWeight, searcher); + } + } + + private class ReRankWeight extends Weight{ + private Query reRankQuery; + private IndexSearcher searcher; + private Weight mainWeight; + private double reRankWeight; + + public ReRankWeight(Query mainQuery, Query reRankQuery, double reRankWeight, IndexSearcher searcher) throws IOException { + super(reRankQuery); + this.reRankQuery = reRankQuery; + this.searcher = searcher; + this.reRankWeight = reRankWeight; + this.mainWeight = mainQuery.createWeight(searcher, true); + } + + @Override + public void extractTerms(Set terms) { + this.mainWeight.extractTerms(terms); + + } + + public float getValueForNormalization() throws IOException { + return mainWeight.getValueForNormalization(); + } + + public Scorer scorer(LeafReaderContext context) throws IOException { + return mainWeight.scorer(context); + } + + public void normalize(float norm, float topLevelBoost) { + mainWeight.normalize(norm, topLevelBoost); + } + + public Explanation explain(LeafReaderContext context, int doc) throws IOException { + Explanation mainExplain = mainWeight.explain(context, doc); + return new QueryRescorer(getQuery()) { + @Override + protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { + float score = firstPassScore; + if (secondPassMatches) { + score += reRankWeight * secondPassScore; + } + return score; + } + }.explain(searcher, mainExplain, context.docBase+doc); + } + + } + + private class ReRankCollector extends TopDocsCollector { + + private Query reRankQuery; + private TopDocsCollector mainCollector; + private IndexSearcher searcher; + private int reRankDocs; + private int length; + private double reRankWeight; + private Map boostedPriority; + private float minScore = Float.MAX_VALUE; + private float maxScore = -Float.MAX_VALUE; + private Scorer localScorer; + private boolean scale; + + public ReRankCollector(int reRankDocs, + int length, + Query reRankQuery, + double reRankWeight, + QueryCommand cmd, + IndexSearcher searcher, + Map boostedPriority, + boolean scale) throws IOException { + super(null); + this.reRankQuery = reRankQuery; + this.reRankDocs = reRankDocs; + this.length = length; + this.boostedPriority = boostedPriority; + this.scale = scale; + Sort sort = cmd.getSort(); + if(sort == null) { + this.mainCollector = TopScoreDocCollector.create(Math.max(this.reRankDocs, length), null); + } else { + sort = sort.rewrite(searcher); + this.mainCollector = TopFieldCollector.create(sort, Math.max(this.reRankDocs, length), null, false, true, true); + } + this.searcher = searcher; + this.reRankWeight = reRankWeight; + } + + public int getTotalHits() { + return mainCollector.getTotalHits(); + } + + public TopDocs topDocs(int start, int howMany) { + + try { + + TopDocs mainDocs = mainCollector.topDocs(0, Math.max(reRankDocs, length)); + + if(mainDocs.totalHits == 0 || mainDocs.scoreDocs.length == 0) { + return mainDocs; + } + + if(reRankDocs == 0) { + scaleScores(mainDocs, new HashMap()); + return mainDocs; + } + + if(boostedPriority != null) { + SolrRequestInfo info = SolrRequestInfo.getRequestInfo(); + Map requestContext = null; + if(info != null) { + requestContext = info.getReq().getContext(); + } + + IntIntHashMap boostedDocs = QueryElevationComponent.getBoostDocs((SolrIndexSearcher)searcher, boostedPriority, requestContext); + + ScoreDoc[] mainScoreDocs = mainDocs.scoreDocs; + ScoreDoc[] reRankScoreDocs = new ScoreDoc[Math.min(mainScoreDocs.length, reRankDocs)]; + System.arraycopy(mainScoreDocs,0,reRankScoreDocs,0,reRankScoreDocs.length); + + mainDocs.scoreDocs = reRankScoreDocs; + + Map scoreMap = getScoreMap(mainDocs.scoreDocs, mainDocs.scoreDocs.length); + + TopDocs rescoredDocs = new QueryRescorer(reRankQuery) { + @Override + protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { + float score = firstPassScore; + if (secondPassMatches) { + score += reRankWeight * secondPassScore; + } + return score; + } + }.rescore(searcher, mainDocs, mainDocs.scoreDocs.length); + + Arrays.sort(rescoredDocs.scoreDocs, new BoostedComp(boostedDocs, mainDocs.scoreDocs, rescoredDocs.getMaxScore())); + + //Lower howMany if we've collected fewer documents. + howMany = Math.min(howMany, mainScoreDocs.length); + + if(howMany == rescoredDocs.scoreDocs.length) { + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; // Just return the rescoredDocs + } else if(howMany > rescoredDocs.scoreDocs.length) { + //We need to return more then we've reRanked, so create the combined page. + ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; + System.arraycopy(mainScoreDocs, 0, scoreDocs, 0, scoreDocs.length); //lay down the initial docs + System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, rescoredDocs.scoreDocs.length);//overlay the re-ranked docs. + rescoredDocs.scoreDocs = scoreDocs; + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; + } else { + //We've rescored more then we need to return. + ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; + System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, howMany); + rescoredDocs.scoreDocs = scoreDocs; + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; + } + + } else { + + ScoreDoc[] mainScoreDocs = mainDocs.scoreDocs; + + /* + * Create the array for the reRankScoreDocs. + */ + ScoreDoc[] reRankScoreDocs = new ScoreDoc[Math.min(mainScoreDocs.length, reRankDocs)]; + + /* + * Copy the initial results into the reRankScoreDocs array. + */ + System.arraycopy(mainScoreDocs, 0, reRankScoreDocs, 0, reRankScoreDocs.length); + + mainDocs.scoreDocs = reRankScoreDocs; + + Map scoreMap = getScoreMap(mainDocs.scoreDocs, mainDocs.scoreDocs.length); + + TopDocs rescoredDocs = new QueryRescorer(reRankQuery) { + @Override + protected float combine(float firstPassScore, boolean secondPassMatches, float secondPassScore) { + float score = firstPassScore; + if (secondPassMatches) { + score += reRankWeight * secondPassScore; + } + return score; + } + }.rescore(searcher, mainDocs, mainDocs.scoreDocs.length); + + //Lower howMany to return if we've collected fewer documents. + howMany = Math.min(howMany, mainScoreDocs.length); + + if(howMany == rescoredDocs.scoreDocs.length) { + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; // Just return the rescoredDocs + } else if(howMany > rescoredDocs.scoreDocs.length) { + + //We need to return more then we've reRanked, so create the combined page. + ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; + //lay down the initial docs + System.arraycopy(mainScoreDocs, 0, scoreDocs, 0, scoreDocs.length); + //overlay the rescoreds docs + System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, rescoredDocs.scoreDocs.length); + rescoredDocs.scoreDocs = scoreDocs; + if(scale) { + assert(scoreMap != null); + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; + } else { + //We've rescored more then we need to return. + ScoreDoc[] scoreDocs = new ScoreDoc[howMany]; + System.arraycopy(rescoredDocs.scoreDocs, 0, scoreDocs, 0, howMany); + rescoredDocs.scoreDocs = scoreDocs; + if(scale) { + scaleScores(rescoredDocs, scoreMap); + } + return rescoredDocs; + } + } + } catch (Exception e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); + } + } + + @Override + public LeafCollector getLeafCollector(LeafReaderContext context) + throws IOException { + return mainCollector.getLeafCollector(context); + } + + @Override + public boolean needsScores() { + return true; + } + } + + private void scaleScores(TopDocs topDocs, Map scoreMap) + { + float maxScore = topDocs.getMaxScore(); + float newMax = -Float.MAX_VALUE; + + for(ScoreDoc scoreDoc : topDocs.scoreDocs) { + float score = scoreDoc.score; + + Float oldScore = scoreMap.get(scoreDoc.doc); + + // check if the score has been changed after rescoring + boolean rescored = oldScore != null && score != oldScore; + + // If maxScore is different from 0, the score is divided by maxscore + scoreDoc.score = score / (maxScore != 0? maxScore : 1); + + // If the document has been rescored, the score is increased by 1. + // This results in having all the rescored element scores in (1,2] range. + if (rescored) + { + scoreDoc.score += 1; + } + + if(scoreDoc.score > newMax) + { + newMax = scoreDoc.score; + } + } + + assert(newMax <= 2); + topDocs.setMaxScore(newMax); + } + + private Map getScoreMap(ScoreDoc[] scoreDocs, int num) { + Map scoreMap = new HashMap(); + for(int i=0; i score2) { + return -1; + } else if(score1 < score2) { + return 1; + } else { + return 0; + } + } + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AllAccessCollector.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AllAccessCollector.java index 70a2eeb5a..ecc986759 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AllAccessCollector.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AllAccessCollector.java @@ -1,37 +1,37 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import org.apache.solr.search.DelegatingCollector; - -public class AllAccessCollector extends DelegatingCollector -{ - public boolean acceptsDocsOutOfOrder() - { - return false; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import org.apache.solr.search.DelegatingCollector; + +public class AllAccessCollector extends DelegatingCollector +{ + public boolean acceptsDocsOutOfOrder() + { + return false; + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AnyStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AnyStructuredFieldPosition.java index 103af5af5..1f4841c2e 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AnyStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/AnyStructuredFieldPosition.java @@ -1,103 +1,103 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -/** - * @author andyh - * - * TODO To change the template for this generated type comment go to Window - - * Preferences - Java - Code Style - Code Templates - */ -public class AnyStructuredFieldPosition extends AbstractStructuredFieldPosition -{ - - /** - * - */ - public AnyStructuredFieldPosition(String termText) - { - super(termText, true, false); - if (termText == null) - { - setTerminal(false); - } - } - - public AnyStructuredFieldPosition() - { - super(null, false, false); - } - - /* - * (non-Javadoc) - * - * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, - * int, org.apache.lucene.index.TermPositions) - */ - public int matches(int start, int end, int offset) throws IOException - { - // we are doing //name - if (getCachingTermPositions() != null) - { - setTerminal(true); - int realPosition = 0; - int adjustedPosition = 0; - getCachingTermPositions().reset(); - int count = getCachingTermPositions().freq(); - for (int i = 0; i < count; i++) - { - realPosition = getCachingTermPositions().nextPosition(); - adjustedPosition = realPosition - start; - if ((end != -1) && (realPosition > end)) - { - return -1; - } - if (adjustedPosition > offset) - { - return adjustedPosition; - } - } - } - else - { - // we are doing // - setTerminal(false); - return offset; - } - return -1; - } - - public String getDescription() - { - return "Any"; - } - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +/** + * @author andyh + * + * TODO To change the template for this generated type comment go to Window - + * Preferences - Java - Code Style - Code Templates + */ +public class AnyStructuredFieldPosition extends AbstractStructuredFieldPosition +{ + + /** + * + */ + public AnyStructuredFieldPosition(String termText) + { + super(termText, true, false); + if (termText == null) + { + setTerminal(false); + } + } + + public AnyStructuredFieldPosition() + { + super(null, false, false); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, + * int, org.apache.lucene.index.TermPositions) + */ + public int matches(int start, int end, int offset) throws IOException + { + // we are doing //name + if (getCachingTermPositions() != null) + { + setTerminal(true); + int realPosition = 0; + int adjustedPosition = 0; + getCachingTermPositions().reset(); + int count = getCachingTermPositions().freq(); + for (int i = 0; i < count; i++) + { + realPosition = getCachingTermPositions().nextPosition(); + adjustedPosition = realPosition - start; + if ((end != -1) && (realPosition > end)) + { + return -1; + } + if (adjustedPosition > offset) + { + return adjustedPosition; + } + } + } + else + { + // we are doing // + setTerminal(false); + return offset; + } + return -1; + } + + public String getDescription() + { + return "Any"; + } + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/BitsFilter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/BitsFilter.java index 90d26d959..86729c908 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/BitsFilter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/BitsFilter.java @@ -1,105 +1,105 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.util.List; - -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.DocIdSet; -import org.apache.solr.search.BitsFilteredDocIdSet; -import org.apache.solr.search.Filter; -import org.apache.lucene.util.BitDocIdSet; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.FixedBitSet; - - -/* -* A segment level Lucene Filter where each segment is backed by a FixedBitSet. -*/ - -public class BitsFilter extends Filter { - - private final List bitSets; - - public BitsFilter(List bitSets) - { - if (bitSets == null) throw new IllegalStateException("bitSets cannot be null"); - this.bitSets = bitSets; - } - - public void or(BitsFilter bitsFilter) - { - List andSets = bitsFilter.bitSets; - for(int i=0; i andSets = bitsFilter.bitSets; - for(int i=0; i getBitSets() - { - return this.bitSets; - } - - public String toString(String s) { - return s; - } - - public DocIdSet getDocIdSet(LeafReaderContext context, Bits bits) { - return BitsFilteredDocIdSet.wrap(new BitDocIdSet(bitSets.get(context.ord)), bits); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof BitsFilter)) return false; - - BitsFilter that = (BitsFilter) o; - - if (!bitSets.equals(that.bitSets)) return false; - - return true; - } - - @Override - public int hashCode() { - return bitSets.hashCode(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.util.List; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.DocIdSet; +import org.apache.solr.search.BitsFilteredDocIdSet; +import org.apache.solr.search.Filter; +import org.apache.lucene.util.BitDocIdSet; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.FixedBitSet; + + +/* +* A segment level Lucene Filter where each segment is backed by a FixedBitSet. +*/ + +public class BitsFilter extends Filter { + + private final List bitSets; + + public BitsFilter(List bitSets) + { + if (bitSets == null) throw new IllegalStateException("bitSets cannot be null"); + this.bitSets = bitSets; + } + + public void or(BitsFilter bitsFilter) + { + List andSets = bitsFilter.bitSets; + for(int i=0; i andSets = bitsFilter.bitSets; + for(int i=0; i getBitSets() + { + return this.bitSets; + } + + public String toString(String s) { + return s; + } + + public DocIdSet getDocIdSet(LeafReaderContext context, Bits bits) { + return BitsFilteredDocIdSet.wrap(new BitDocIdSet(bitSets.get(context.ord)), bits); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BitsFilter)) return false; + + BitsFilter that = (BitsFilter) o; + + if (!bitSets.equals(that.bitSets)) return false; + + return true; + } + + @Override + public int hashCode() { + return bitSets.hashCode(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CachingTermPositions.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CachingTermPositions.java index 06949911b..f17ca745a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CachingTermPositions.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CachingTermPositions.java @@ -1,210 +1,210 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.util.AttributeSource; -import org.apache.lucene.util.BytesRef; - - -/** - * @author andyh - * - * TODO To change the template for this generated type comment go to Window - - * Preferences - Java - Code Style - Code Templates - */ -public class CachingTermPositions extends PostingsEnum -{ - int[] results; - - int position = -1; - - int last = -1; - - PostingsEnum delegate; - - public CachingTermPositions(PostingsEnum delegate) - { - this.delegate = delegate; - } - - /* - * (non-Javadoc) - * - * @see org.apache.lucene.index.TermPositions#nextPosition() - */ - public int nextPosition() throws IOException - { - if (results == null) - { - results = new int[freq()]; - } - position++; - if (last < position) - { - results[position] = delegate.nextPosition(); - last = position; - } - return results[position]; - - } - - public void reset() - { - position = -1; - } - - private void clear() - { - position = -1; - last = -1; - results = null; - } - - /** - * @return int - * @see java.lang.Object#hashCode() - */ - public int hashCode() - { - return delegate.hashCode(); - } - - /** - * @return int - * @throws IOException - * @see org.apache.lucene.index.DocsEnum#freq() - */ - public int freq() throws IOException - { - return delegate.freq(); - } - - /** - * @return int - * @throws IOException - * @see org.apache.lucene.index.DocsAndPositionsEnum#startOffset() - */ - public int startOffset() throws IOException - { - return delegate.startOffset(); - } - - /** - * @return int - * @throws IOException - * @see org.apache.lucene.index.DocsAndPositionsEnum#endOffset() - */ - public int endOffset() throws IOException - { - return delegate.endOffset(); - } - - /** - * @return int - * @see org.apache.lucene.search.DocIdSetIterator#docID() - */ - public int docID() - { - return delegate.docID(); - } - - /** - * @return BytesRef - * @throws IOException - * @see org.apache.lucene.index.DocsAndPositionsEnum#getPayload() - */ - public BytesRef getPayload() throws IOException - { - return delegate.getPayload(); - } - - /** - * @return AttributeSource - * @see org.apache.lucene.index.DocsEnum#attributes() - */ - public AttributeSource attributes() - { - return delegate.attributes(); - } - - /** - * @return int - * @throws IOException - * @see org.apache.lucene.search.DocIdSetIterator#nextDoc() - */ - public int nextDoc() throws IOException - { - int nextDoc = delegate.nextDoc(); - clear(); - return nextDoc; - } - - /** - * @param target int - * @return int - * @throws IOException - * @see org.apache.lucene.search.DocIdSetIterator#advance(int) - */ - public int advance(int target) throws IOException - { - int nextDoc = delegate.advance(target); - clear(); - return nextDoc; - } - - /** - * @param obj Object - * @return boolean - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object obj) - { - return delegate.equals(obj); - } - - /** - * @return long - * @see org.apache.lucene.search.DocIdSetIterator#cost() - */ - public long cost() - { - return delegate.cost(); - } - - /** - * @return String - * @see java.lang.Object#toString() - */ - public String toString() - { - return delegate.toString(); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.apache.lucene.index.PostingsEnum; +import org.apache.lucene.util.AttributeSource; +import org.apache.lucene.util.BytesRef; + + +/** + * @author andyh + * + * TODO To change the template for this generated type comment go to Window - + * Preferences - Java - Code Style - Code Templates + */ +public class CachingTermPositions extends PostingsEnum +{ + int[] results; + + int position = -1; + + int last = -1; + + PostingsEnum delegate; + + public CachingTermPositions(PostingsEnum delegate) + { + this.delegate = delegate; + } + + /* + * (non-Javadoc) + * + * @see org.apache.lucene.index.TermPositions#nextPosition() + */ + public int nextPosition() throws IOException + { + if (results == null) + { + results = new int[freq()]; + } + position++; + if (last < position) + { + results[position] = delegate.nextPosition(); + last = position; + } + return results[position]; + + } + + public void reset() + { + position = -1; + } + + private void clear() + { + position = -1; + last = -1; + results = null; + } + + /** + * @return int + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return delegate.hashCode(); + } + + /** + * @return int + * @throws IOException + * @see org.apache.lucene.index.DocsEnum#freq() + */ + public int freq() throws IOException + { + return delegate.freq(); + } + + /** + * @return int + * @throws IOException + * @see org.apache.lucene.index.DocsAndPositionsEnum#startOffset() + */ + public int startOffset() throws IOException + { + return delegate.startOffset(); + } + + /** + * @return int + * @throws IOException + * @see org.apache.lucene.index.DocsAndPositionsEnum#endOffset() + */ + public int endOffset() throws IOException + { + return delegate.endOffset(); + } + + /** + * @return int + * @see org.apache.lucene.search.DocIdSetIterator#docID() + */ + public int docID() + { + return delegate.docID(); + } + + /** + * @return BytesRef + * @throws IOException + * @see org.apache.lucene.index.DocsAndPositionsEnum#getPayload() + */ + public BytesRef getPayload() throws IOException + { + return delegate.getPayload(); + } + + /** + * @return AttributeSource + * @see org.apache.lucene.index.DocsEnum#attributes() + */ + public AttributeSource attributes() + { + return delegate.attributes(); + } + + /** + * @return int + * @throws IOException + * @see org.apache.lucene.search.DocIdSetIterator#nextDoc() + */ + public int nextDoc() throws IOException + { + int nextDoc = delegate.nextDoc(); + clear(); + return nextDoc; + } + + /** + * @param target int + * @return int + * @throws IOException + * @see org.apache.lucene.search.DocIdSetIterator#advance(int) + */ + public int advance(int target) throws IOException + { + int nextDoc = delegate.advance(target); + clear(); + return nextDoc; + } + + /** + * @param obj Object + * @return boolean + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) + { + return delegate.equals(obj); + } + + /** + * @return long + * @see org.apache.lucene.search.DocIdSetIterator#cost() + */ + public long cost() + { + return delegate.cost(); + } + + /** + * @return String + * @see java.lang.Object#toString() + */ + public String toString() + { + return delegate.toString(); + } + +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CmisQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CmisQParserPlugin.java index 898c703ba..eb08bb0ba 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CmisQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/CmisQParserPlugin.java @@ -1,207 +1,207 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; -import org.alfresco.repo.search.impl.querymodel.Order; -import org.alfresco.repo.search.impl.querymodel.Ordering; -import org.alfresco.repo.search.impl.querymodel.PropertyArgument; -import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; -import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.util.Pair; -import org.apache.chemistry.opencmis.commons.enums.CmisVersion; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class CmisQParserPlugin extends QParserPlugin -{ - protected final static Logger log = LoggerFactory.getLogger(CmisQParserPlugin.class); - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new CmisQParser(qstr, localParams, params, req, args); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - public void init(NamedList args) - { - this.args = args; - } - - public static class CmisQParser extends AbstractQParser - { - public CmisQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) - { - super(qstr, localParams, params, req, args); - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - try - { - Pair searchParametersAndFilter = getSearchParameters(); - SearchParameters searchParameters = searchParametersAndFilter.getFirst(); - // these could either be checked & set here, or in the SolrQueryParser constructor - - String cmisVersionString = this.params.get("cmisVersion"); - CmisVersion cmisVersion = (cmisVersionString == null ? CmisVersion.CMIS_1_0 : CmisVersion.valueOf(cmisVersionString)); - - String altDic = this.params.get(SearchParameters.ALTERNATIVE_DICTIONARY); - org.alfresco.repo.search.impl.querymodel.Query queryModelQuery - = AlfrescoSolrDataModel.getInstance().parseCMISQueryToAlfrescoAbstractQuery(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS, searchParameters, req, altDic, cmisVersion); - - // build the sort param and update the params on the request if required ..... - - if ((queryModelQuery.getOrderings() != null) && (queryModelQuery.getOrderings().size() > 0)) - { - StringBuilder sortParameter = new StringBuilder(); - - for (Ordering ordering : queryModelQuery.getOrderings()) - { - if (ordering.getColumn().getFunction().getName().equals(PropertyAccessor.NAME)) - { - PropertyArgument property = (PropertyArgument) ordering.getColumn().getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY); - - if (property == null) - { - throw new IllegalStateException(); - } - - String propertyName = property.getPropertyName(); - - String luceneField = AlfrescoSolrDataModel.getInstance().getCMISFunctionEvaluationContext(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS,cmisVersion,altDic).getLuceneFieldName(propertyName); - -// Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(luceneField); -// PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), AlfrescoSolrDataModel.getInstance().getNamespaceDAO(), AlfrescoSolrDataModel.getInstance().getDictionaryService(altDic), fieldNameAndEnding.getFirst()); -// -// String solrSortField = null; -// if(propertyDef != null) -// { -// -// IndexedField fields = AlfrescoSolrDataModel.getInstance().getQueryableFields(propertyDef.getName(), AlfrescoSolrDataModel.getInstance().getTextField(fieldNameAndEnding.getSecond()), FieldUse.SORT); -// if(fields.getFields().size() > 0) -// { -// solrSortField = fields.getFields().get(0).getField(); -// } -// } -// else -// { -// solrSortField = AlfrescoSolrDataModel.getInstance().mapNonPropertyFields(luceneField); -// } - if(sortParameter.length() > 0) - { - sortParameter.append(", "); - } - sortParameter.append(luceneField).append(" "); - if(ordering.getOrder() == Order.DESCENDING) - { - sortParameter.append("desc"); - } - else - { - sortParameter.append("asc"); - } - - } - else if (ordering.getColumn().getFunction().getName().equals(Score.NAME)) - { - if(sortParameter.length() > 0) - { - sortParameter.append(", "); - } - sortParameter.append("SCORE "); - if(ordering.getOrder() == Order.DESCENDING) - { - sortParameter.append("desc"); - } - else - { - sortParameter.append("asc"); - } - } - else - { - throw new IllegalStateException(); - } - - } - - // update request params - - ModifiableSolrParams newParams = new ModifiableSolrParams(req.getParams()); - newParams.set("sort", sortParameter.toString()); - req.setParams(newParams); - this.params = newParams; - } - - Query query = AlfrescoSolrDataModel.getInstance().getCMISQuery(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS, searchParametersAndFilter, req, queryModelQuery, cmisVersion, altDic); - if(log.isDebugEnabled()) - { - log.debug("AFTS QP query as lucene:\t "+query); - } - return query; - } - catch(ParseException e) - { - throw new SyntaxError(e); - } - } - - - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; +import org.alfresco.repo.search.impl.querymodel.Order; +import org.alfresco.repo.search.impl.querymodel.Ordering; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class CmisQParserPlugin extends QParserPlugin +{ + protected final static Logger log = LoggerFactory.getLogger(CmisQParserPlugin.class); + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new CmisQParser(qstr, localParams, params, req, args); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + public void init(NamedList args) + { + this.args = args; + } + + public static class CmisQParser extends AbstractQParser + { + public CmisQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args) + { + super(qstr, localParams, params, req, args); + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + try + { + Pair searchParametersAndFilter = getSearchParameters(); + SearchParameters searchParameters = searchParametersAndFilter.getFirst(); + // these could either be checked & set here, or in the SolrQueryParser constructor + + String cmisVersionString = this.params.get("cmisVersion"); + CmisVersion cmisVersion = (cmisVersionString == null ? CmisVersion.CMIS_1_0 : CmisVersion.valueOf(cmisVersionString)); + + String altDic = this.params.get(SearchParameters.ALTERNATIVE_DICTIONARY); + org.alfresco.repo.search.impl.querymodel.Query queryModelQuery + = AlfrescoSolrDataModel.getInstance().parseCMISQueryToAlfrescoAbstractQuery(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS, searchParameters, req, altDic, cmisVersion); + + // build the sort param and update the params on the request if required ..... + + if ((queryModelQuery.getOrderings() != null) && (queryModelQuery.getOrderings().size() > 0)) + { + StringBuilder sortParameter = new StringBuilder(); + + for (Ordering ordering : queryModelQuery.getOrderings()) + { + if (ordering.getColumn().getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument property = (PropertyArgument) ordering.getColumn().getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY); + + if (property == null) + { + throw new IllegalStateException(); + } + + String propertyName = property.getPropertyName(); + + String luceneField = AlfrescoSolrDataModel.getInstance().getCMISFunctionEvaluationContext(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS,cmisVersion,altDic).getLuceneFieldName(propertyName); + +// Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(luceneField); +// PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), AlfrescoSolrDataModel.getInstance().getNamespaceDAO(), AlfrescoSolrDataModel.getInstance().getDictionaryService(altDic), fieldNameAndEnding.getFirst()); +// +// String solrSortField = null; +// if(propertyDef != null) +// { +// +// IndexedField fields = AlfrescoSolrDataModel.getInstance().getQueryableFields(propertyDef.getName(), AlfrescoSolrDataModel.getInstance().getTextField(fieldNameAndEnding.getSecond()), FieldUse.SORT); +// if(fields.getFields().size() > 0) +// { +// solrSortField = fields.getFields().get(0).getField(); +// } +// } +// else +// { +// solrSortField = AlfrescoSolrDataModel.getInstance().mapNonPropertyFields(luceneField); +// } + if(sortParameter.length() > 0) + { + sortParameter.append(", "); + } + sortParameter.append(luceneField).append(" "); + if(ordering.getOrder() == Order.DESCENDING) + { + sortParameter.append("desc"); + } + else + { + sortParameter.append("asc"); + } + + } + else if (ordering.getColumn().getFunction().getName().equals(Score.NAME)) + { + if(sortParameter.length() > 0) + { + sortParameter.append(", "); + } + sortParameter.append("SCORE "); + if(ordering.getOrder() == Order.DESCENDING) + { + sortParameter.append("desc"); + } + else + { + sortParameter.append("asc"); + } + } + else + { + throw new IllegalStateException(); + } + + } + + // update request params + + ModifiableSolrParams newParams = new ModifiableSolrParams(req.getParams()); + newParams.set("sort", sortParameter.toString()); + req.setParams(newParams); + this.params = newParams; + } + + Query query = AlfrescoSolrDataModel.getInstance().getCMISQuery(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS, searchParametersAndFilter, req, queryModelQuery, cmisVersion, altDic); + if(log.isDebugEnabled()) + { + log.debug("AFTS QP query as lucene:\t "+query); + } + return query; + } + catch(ParseException e) + { + throw new SyntaxError(e); + } + } + + + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingAnalyticsQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingAnalyticsQuery.java index 1d6dbf3a8..efeca2487 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingAnalyticsQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingAnalyticsQuery.java @@ -1,63 +1,63 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import org.apache.lucene.search.IndexSearcher; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.search.AnalyticsQuery; -import org.apache.solr.search.DelegatingCollector; - -/** - * @author Andy - * - */ -public class ContentSizeGroupingAnalyticsQuery extends AnalyticsQuery -{ - private int buckets; - private int scale; - - /** - * @param buckets - * @param mappings - */ - public ContentSizeGroupingAnalyticsQuery(int scale, int buckets) - { - this.scale = scale; - this.buckets = buckets; - - } - - /* (non-Javadoc) - * @see org.apache.solr.search.AnalyticsQuery#getAnalyticsCollector(org.apache.solr.handler.component.ResponseBuilder, org.apache.lucene.search.IndexSearcher) - */ - @Override - public DelegatingCollector getAnalyticsCollector(ResponseBuilder rb, IndexSearcher searcher) - { - return new ContentSizeGroupingCollector(rb, scale, buckets); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import org.apache.lucene.search.IndexSearcher; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.search.AnalyticsQuery; +import org.apache.solr.search.DelegatingCollector; + +/** + * @author Andy + * + */ +public class ContentSizeGroupingAnalyticsQuery extends AnalyticsQuery +{ + private int buckets; + private int scale; + + /** + * @param buckets + * @param mappings + */ + public ContentSizeGroupingAnalyticsQuery(int scale, int buckets) + { + this.scale = scale; + this.buckets = buckets; + + } + + /* (non-Javadoc) + * @see org.apache.solr.search.AnalyticsQuery#getAnalyticsCollector(org.apache.solr.handler.component.ResponseBuilder, org.apache.lucene.search.IndexSearcher) + */ + @Override + public DelegatingCollector getAnalyticsCollector(ResponseBuilder rb, IndexSearcher searcher) + { + return new ContentSizeGroupingCollector(rb, scale, buckets); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingCollector.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingCollector.java index e1afb7242..b0d345a70 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingCollector.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingCollector.java @@ -1,131 +1,131 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.math.BigDecimal; -import java.math.MathContext; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.alfresco.solr.tracker.TrackerStats.Bucket; -import org.alfresco.solr.tracker.TrackerStats.IncrementalStats; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.DelegatingCollector; - -/** - * @author Andy - * - */ -public class ContentSizeGroupingCollector extends DelegatingCollector -{ - ResponseBuilder rb; - IncrementalStats stats; - String schemaFieldName; - SchemaField schemaField; - NumericDocValues numericDocValues; - /** - * @param rb - * @param buckets - */ - public ContentSizeGroupingCollector(ResponseBuilder rb, int scale, int buckets) - { - this.rb = rb; - stats = new IncrementalStats(scale, buckets, null); - schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty("content.size", FieldUse.FACET, rb.req); - schemaField = rb.req.getSchema().getFieldOrNull(schemaFieldName); - } - - - - /* (non-Javadoc) - * @see org.apache.solr.search.DelegatingCollector#setNextReader(org.apache.lucene.index.AtomicReaderContext) - */ - @Override - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - if(schemaField != null) - { - if(schemaField.getType().getNumericType() != null) - { - try - { - numericDocValues = context.reader().getNumericDocValues(schemaFieldName); - } - catch (IOException e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - } - - - - public void collect(int doc) throws IOException - { - - if(numericDocValues != null) - { - long value = numericDocValues.get(doc); - stats.add(value); - } - leafDelegate.collect(doc); - } - - public void finish() throws IOException - { - NamedList analytics = new NamedList<>(); - rb.rsp.add("analytics", analytics); - NamedList fieldCounts = new NamedList<>(); - analytics.add("contentSize()", fieldCounts); - - for(Bucket bucket :stats.getHistogram()) - { - fieldCounts.add("["+(long)Math.ceil(bucket.leftBoundary)+ " TO "+(long)Math.ceil(bucket.rightBoundary)+">", (long)roundEven(bucket.countLeft + bucket.countRight)); - } - - - if(this.delegate instanceof DelegatingCollector) { - ((DelegatingCollector)this.delegate).finish(); - } - } - - - private long roundEven(double value) - { - BigDecimal bd = new BigDecimal(value); - bd = bd.round(MathContext.DECIMAL64); - return bd.longValue(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.MathContext; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.alfresco.solr.tracker.TrackerStats.Bucket; +import org.alfresco.solr.tracker.TrackerStats.IncrementalStats; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DelegatingCollector; + +/** + * @author Andy + * + */ +public class ContentSizeGroupingCollector extends DelegatingCollector +{ + ResponseBuilder rb; + IncrementalStats stats; + String schemaFieldName; + SchemaField schemaField; + NumericDocValues numericDocValues; + /** + * @param rb + * @param buckets + */ + public ContentSizeGroupingCollector(ResponseBuilder rb, int scale, int buckets) + { + this.rb = rb; + stats = new IncrementalStats(scale, buckets, null); + schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty("content.size", FieldUse.FACET, rb.req); + schemaField = rb.req.getSchema().getFieldOrNull(schemaFieldName); + } + + + + /* (non-Javadoc) + * @see org.apache.solr.search.DelegatingCollector#setNextReader(org.apache.lucene.index.AtomicReaderContext) + */ + @Override + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + if(schemaField != null) + { + if(schemaField.getType().getNumericType() != null) + { + try + { + numericDocValues = context.reader().getNumericDocValues(schemaFieldName); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + + + + public void collect(int doc) throws IOException + { + + if(numericDocValues != null) + { + long value = numericDocValues.get(doc); + stats.add(value); + } + leafDelegate.collect(doc); + } + + public void finish() throws IOException + { + NamedList analytics = new NamedList<>(); + rb.rsp.add("analytics", analytics); + NamedList fieldCounts = new NamedList<>(); + analytics.add("contentSize()", fieldCounts); + + for(Bucket bucket :stats.getHistogram()) + { + fieldCounts.add("["+(long)Math.ceil(bucket.leftBoundary)+ " TO "+(long)Math.ceil(bucket.rightBoundary)+">", (long)roundEven(bucket.countLeft + bucket.countRight)); + } + + + if(this.delegate instanceof DelegatingCollector) { + ((DelegatingCollector)this.delegate).finish(); + } + } + + + private long roundEven(double value) + { + BigDecimal bd = new BigDecimal(value); + bd = bd.round(MathContext.DECIMAL64); + return bd.longValue(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingQParserPlugin.java index fc3186884..ecdf069f9 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/ContentSizeGroupingQParserPlugin.java @@ -1,110 +1,110 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; - -/** - * @author Andy - */ -public class ContentSizeGroupingQParserPlugin extends QParserPlugin -{ - private int scale = 1; - - private int buckets = 10; - - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - @Override - public void init(NamedList args) - { - this.args = args; - if (args != null) - { - Object val = args.get("buckets"); - if (val != null) - { - buckets = Integer.parseInt(val.toString()); - } - - val = args.get("scale"); - if (val != null) - { - scale = Integer.parseInt(val.toString()); - } - } - - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new ContentSizeGroupingQParser(qstr, localParams, params, req, args, scale, buckets); - } - - public static class ContentSizeGroupingQParser extends AbstractQParser - { - - private int buckets; - - private int scale; - - public ContentSizeGroupingQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args, int scale, int buckets) - { - super(qstr, localParams, params, req, args); - this.scale = scale; - this.buckets = buckets; - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - return new ContentSizeGroupingAnalyticsQuery(scale, buckets); - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; + +/** + * @author Andy + */ +public class ContentSizeGroupingQParserPlugin extends QParserPlugin +{ + private int scale = 1; + + private int buckets = 10; + + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + @Override + public void init(NamedList args) + { + this.args = args; + if (args != null) + { + Object val = args.get("buckets"); + if (val != null) + { + buckets = Integer.parseInt(val.toString()); + } + + val = args.get("scale"); + if (val != null) + { + scale = Integer.parseInt(val.toString()); + } + } + + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new ContentSizeGroupingQParser(qstr, localParams, params, req, args, scale, buckets); + } + + public static class ContentSizeGroupingQParser extends AbstractQParser + { + + private int buckets; + + private int scale; + + public ContentSizeGroupingQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args, int scale, int buckets) + { + super(qstr, localParams, params, req, args); + this.scale = scale; + this.buckets = buckets; + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + return new ContentSizeGroupingAnalyticsQuery(scale, buckets); + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DescendantAndSelfStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DescendantAndSelfStructuredFieldPosition.java index 42cdf4177..dc72e3814 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DescendantAndSelfStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DescendantAndSelfStructuredFieldPosition.java @@ -1,53 +1,53 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -public class DescendantAndSelfStructuredFieldPosition extends AnyStructuredFieldPosition -{ - public DescendantAndSelfStructuredFieldPosition() - { - super(); - } - - public String getDescription() - { - return "Descendant and Self Axis"; - } - - public boolean allowsLinkingBySelf() - { - return true; - } - - public boolean isDescendant() - { - return true; - } - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +public class DescendantAndSelfStructuredFieldPosition extends AnyStructuredFieldPosition +{ + public DescendantAndSelfStructuredFieldPosition() + { + super(); + } + + public String getDescription() + { + return "Descendant and Self Axis"; + } + + public boolean allowsLinkingBySelf() + { + return true; + } + + public boolean isDescendant() + { + return true; + } + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DocValuesCache.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DocValuesCache.java index 013d6af2a..75d1a7b0e 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DocValuesCache.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/DocValuesCache.java @@ -1,143 +1,143 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.WeakHashMap; - -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.NumericDocValues; - - -/** - * The DocValuesCache is an in-memory uncompressed numeric DocValues cache. It is designed to provide the fastest - * possible access to numeric docValues. The DocValuesCache can be used instead of the Direct DocValues format which also - * provides uncompressed in-memory docValues. The DocValuesCache can be used in situations when it is not - * practical to re-index to use Direct docValues. - **/ - -public class DocValuesCache -{ - private static Map> cache = new HashMap>(); - - public static synchronized NumericDocValues getNumericDocValues(String field, LeafReader reader) throws IOException - { - WeakHashMap fieldCache = cache.get(field); - - if(fieldCache == null) - { - fieldCache = new WeakHashMap(); - cache.put(field, fieldCache); - } - - Object cacheKey = reader.getCoreCacheKey(); - NumericDocValues cachedValues = fieldCache.get(cacheKey); - - if(cachedValues == null) - { - NumericDocValues fieldValues = reader.getNumericDocValues(field); - if(fieldValues == null) - { - return null; - } - else - { - int maxDoc = reader.maxDoc(); - boolean longs = false; - int[] intValues = new int[maxDoc]; //Always start off with an int array. - SettableDocValues settableValues = new IntValues(intValues); - - for(int i=0; i Integer.MAX_VALUE && !longs) - { - longs = true; - settableValues = new LongValues(intValues); - } - - settableValues.set(i, value); - } - fieldCache.put(cacheKey, settableValues); - return settableValues; - } - } - else - { - return cachedValues; - } - } - - private static abstract class SettableDocValues extends NumericDocValues - { - public abstract void set(int index, long value); - } - - private static class IntValues extends SettableDocValues - { - private int[] values; - - public IntValues(int[] values) - { - this.values = values; - } - - public void set(int index, long value) - { - this.values[index] = (int)value; - } - - public long get(int index) { - return values[index]; - } - } - - private static class LongValues extends SettableDocValues - { - private long[] values; - - public LongValues(int[] ivalues) - { - values = new long[ivalues.length]; - for(int i=0; i< ivalues.length; i++) { - values[i] = ivalues[i]; - } - } - - public void set(int index, long value) - { - this.values[index] = value; - } - - public long get(int index) - { - return values[index]; - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.NumericDocValues; + + +/** + * The DocValuesCache is an in-memory uncompressed numeric DocValues cache. It is designed to provide the fastest + * possible access to numeric docValues. The DocValuesCache can be used instead of the Direct DocValues format which also + * provides uncompressed in-memory docValues. The DocValuesCache can be used in situations when it is not + * practical to re-index to use Direct docValues. + **/ + +public class DocValuesCache +{ + private static Map> cache = new HashMap>(); + + public static synchronized NumericDocValues getNumericDocValues(String field, LeafReader reader) throws IOException + { + WeakHashMap fieldCache = cache.get(field); + + if(fieldCache == null) + { + fieldCache = new WeakHashMap(); + cache.put(field, fieldCache); + } + + Object cacheKey = reader.getCoreCacheKey(); + NumericDocValues cachedValues = fieldCache.get(cacheKey); + + if(cachedValues == null) + { + NumericDocValues fieldValues = reader.getNumericDocValues(field); + if(fieldValues == null) + { + return null; + } + else + { + int maxDoc = reader.maxDoc(); + boolean longs = false; + int[] intValues = new int[maxDoc]; //Always start off with an int array. + SettableDocValues settableValues = new IntValues(intValues); + + for(int i=0; i Integer.MAX_VALUE && !longs) + { + longs = true; + settableValues = new LongValues(intValues); + } + + settableValues.set(i, value); + } + fieldCache.put(cacheKey, settableValues); + return settableValues; + } + } + else + { + return cachedValues; + } + } + + private static abstract class SettableDocValues extends NumericDocValues + { + public abstract void set(int index, long value); + } + + private static class IntValues extends SettableDocValues + { + private int[] values; + + public IntValues(int[] values) + { + this.values = values; + } + + public void set(int index, long value) + { + this.values[index] = (int)value; + } + + public long get(int index) { + return values[index]; + } + } + + private static class LongValues extends SettableDocValues + { + private long[] values; + + public LongValues(int[] ivalues) + { + values = new long[ivalues.length]; + for(int i=0; i< ivalues.length; i++) { + values[i] = ivalues[i]; + } + } + + public void set(int index, long value) + { + this.values[index] = value; + } + + public long get(int index) + { + return values[index]; + } + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/EmptyHybridBitSet.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/EmptyHybridBitSet.java index 8925bf0b5..d7ad4e196 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/EmptyHybridBitSet.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/EmptyHybridBitSet.java @@ -1,40 +1,40 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -public class EmptyHybridBitSet extends HybridBitSet -{ - public void set(long bit) - { - - } - - public boolean get(long bit) - { - return false; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +public class EmptyHybridBitSet extends HybridBitSet +{ + public void set(long bit) + { + + } + + public boolean get(long bit) + { + return false; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/HybridBitSet.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/HybridBitSet.java index e33328c70..5c9170cbb 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/HybridBitSet.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/HybridBitSet.java @@ -1,80 +1,80 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.util.HashSet; -import java.util.Set; - -import org.apache.lucene.util.FixedBitSet; - -/** -* The HybridBitSet is a random access (doesn't support iteration) BitSet implementation that uses a FixedBitSet for the first N bits -* and a HashSet for higher bits. This is designed to provide a balance between the high performance of FixedBitSet and -* the efficient sparse behavior of a HashSet. -**/ - -public class HybridBitSet -{ - private FixedBitSet bits; - private Set set = new HashSet(); - private int maxBit; - - public HybridBitSet() - { - - } - - HybridBitSet(int maxBit) - { - this.bits = new FixedBitSet(maxBit); - this.maxBit = maxBit; - } - - public void set(long bit) - { - if(bit < maxBit) - { - bits.set((int)bit); - } - else - { - set.add((long)bit); - } - } - - public boolean get(long bit) - { - if(bit < maxBit) - { - return bits.get((int)bit); - } - else - { - return set.contains((long)bit); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.lucene.util.FixedBitSet; + +/** +* The HybridBitSet is a random access (doesn't support iteration) BitSet implementation that uses a FixedBitSet for the first N bits +* and a HashSet for higher bits. This is designed to provide a balance between the high performance of FixedBitSet and +* the efficient sparse behavior of a HashSet. +**/ + +public class HybridBitSet +{ + private FixedBitSet bits; + private Set set = new HashSet(); + private int maxBit; + + public HybridBitSet() + { + + } + + HybridBitSet(int maxBit) + { + this.bits = new FixedBitSet(maxBit); + this.maxBit = maxBit; + } + + public void set(long bit) + { + if(bit < maxBit) + { + bits.set((int)bit); + } + else + { + set.add((long)bit); + } + } + + public boolean get(long bit) + { + if(bit < maxBit) + { + return bits.get((int)bit); + } + else + { + return set.contains((long)bit); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryBuilderContextSolrImpl.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryBuilderContextSolrImpl.java index c0cc36444..f67c0a111 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryBuilderContextSolrImpl.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryBuilderContextSolrImpl.java @@ -1,106 +1,106 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.util.Properties; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.adaptor.QueryParserAdaptor; -import org.alfresco.repo.search.impl.parsers.FTSQueryParser; -import org.alfresco.repo.search.impl.querymodel.impl.lucene.QueryBuilderContext; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Sort; -import org.apache.lucene.util.Version; -import org.apache.solr.core.CoreDescriptorDecorator; -import org.apache.solr.request.SolrQueryRequest; - -/** - * @author andyh - */ -public class Lucene4QueryBuilderContextSolrImpl implements QueryBuilderContext -{ - private Solr4QueryParser lqp; - - private NamespacePrefixResolver namespacePrefixResolver; - - private QueryParserAdaptor lqpa; - - /** - * Context for building lucene queries - * - * @param dictionaryService - * @param namespacePrefixResolver - * @param tenantService - * @param searchParameters - * @param defaultSearchMLAnalysisMode - * @param req - * @param model - */ - public Lucene4QueryBuilderContextSolrImpl(DictionaryService dictionaryService, NamespacePrefixResolver namespacePrefixResolver, TenantService tenantService, - SearchParameters searchParameters, MLAnalysisMode defaultSearchMLAnalysisMode, SolrQueryRequest req, AlfrescoSolrDataModel model, FTSQueryParser.RerankPhase rerankPhase) - { - lqp = new Solr4QueryParser(req, Version.LATEST, searchParameters.getDefaultFieldName(), req.getSchema().getQueryAnalyzer(), rerankPhase); -// lqp.setDefaultOperator(AbstractLuceneQueryParser.OR_OPERATOR); - lqp.setDictionaryService(dictionaryService); - lqp.setNamespacePrefixResolver(namespacePrefixResolver); - lqp.setTenantService(tenantService); - lqp.setSearchParameters(searchParameters); -// lqp.setDefaultSearchMLAnalysisMode(defaultSearchMLAnalysisMode); -// lqp.setIndexReader(indexReader); -// lqp.setAllowLeadingWildcard(true); -// this.namespacePrefixResolver = namespacePrefixResolver; - - Properties props = new CoreDescriptorDecorator(req.getCore().getCoreDescriptor()).getProperties(); - int topTermSpanRewriteLimit = Integer.parseInt(props.getProperty("alfresco.topTermSpanRewriteLimit", "1000")); - lqp.setTopTermSpanRewriteLimit(topTermSpanRewriteLimit); - - lqpa = new Lucene4QueryParserAdaptor(lqp); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderContext#getLuceneQueryParser() - */ - public QueryParserAdaptor getLuceneQueryParserAdaptor() - { - return lqpa; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderContext#getNamespacePrefixResolver() - */ - public NamespacePrefixResolver getNamespacePrefixResolver() - { - return namespacePrefixResolver; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.util.Properties; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.adaptor.QueryParserAdaptor; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.repo.search.impl.querymodel.impl.lucene.QueryBuilderContext; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.util.Version; +import org.apache.solr.core.CoreDescriptorDecorator; +import org.apache.solr.request.SolrQueryRequest; + +/** + * @author andyh + */ +public class Lucene4QueryBuilderContextSolrImpl implements QueryBuilderContext +{ + private Solr4QueryParser lqp; + + private NamespacePrefixResolver namespacePrefixResolver; + + private QueryParserAdaptor lqpa; + + /** + * Context for building lucene queries + * + * @param dictionaryService + * @param namespacePrefixResolver + * @param tenantService + * @param searchParameters + * @param defaultSearchMLAnalysisMode + * @param req + * @param model + */ + public Lucene4QueryBuilderContextSolrImpl(DictionaryService dictionaryService, NamespacePrefixResolver namespacePrefixResolver, TenantService tenantService, + SearchParameters searchParameters, MLAnalysisMode defaultSearchMLAnalysisMode, SolrQueryRequest req, AlfrescoSolrDataModel model, FTSQueryParser.RerankPhase rerankPhase) + { + lqp = new Solr4QueryParser(req, Version.LATEST, searchParameters.getDefaultFieldName(), req.getSchema().getQueryAnalyzer(), rerankPhase); +// lqp.setDefaultOperator(AbstractLuceneQueryParser.OR_OPERATOR); + lqp.setDictionaryService(dictionaryService); + lqp.setNamespacePrefixResolver(namespacePrefixResolver); + lqp.setTenantService(tenantService); + lqp.setSearchParameters(searchParameters); +// lqp.setDefaultSearchMLAnalysisMode(defaultSearchMLAnalysisMode); +// lqp.setIndexReader(indexReader); +// lqp.setAllowLeadingWildcard(true); +// this.namespacePrefixResolver = namespacePrefixResolver; + + Properties props = new CoreDescriptorDecorator(req.getCore().getCoreDescriptor()).getProperties(); + int topTermSpanRewriteLimit = Integer.parseInt(props.getProperty("alfresco.topTermSpanRewriteLimit", "1000")); + lqp.setTopTermSpanRewriteLimit(topTermSpanRewriteLimit); + + lqpa = new Lucene4QueryParserAdaptor(lqp); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderContext#getLuceneQueryParser() + */ + public QueryParserAdaptor getLuceneQueryParserAdaptor() + { + return lqpa; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderContext#getNamespacePrefixResolver() + */ + public NamespacePrefixResolver getNamespacePrefixResolver() + { + return namespacePrefixResolver; + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryParserAdaptor.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryParserAdaptor.java index 3184232fc..932ba20f8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryParserAdaptor.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Lucene4QueryParserAdaptor.java @@ -1,427 +1,427 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.util.List; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.search.adaptor.QueryParserAdaptor; -import org.alfresco.repo.search.adaptor.QueryParserExpressionAdaptor; -import org.alfresco.repo.search.adaptor.AnalysisMode; -import org.alfresco.repo.search.adaptor.LuceneFunction; -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; -import org.alfresco.repo.search.impl.querymodel.Ordering; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.cmr.search.SearchParameters; -import org.apache.lucene.index.Term; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BoostQuery; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.TermQuery; - -/** - * @author Andy - * - */ -public class Lucene4QueryParserAdaptor implements QueryParserAdaptor -{ - - private Solr4QueryParser lqp; - - /** - * @param lqp - */ - public Lucene4QueryParserAdaptor(Solr4QueryParser lqp) - { - this.lqp = lqp; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) - */ - @Override - public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException - { - return lqp.getFieldQuery(field, queryText, analysisMode, luceneFunction); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getRangeQuery(java.lang.String, java.lang.String, java.lang.String, boolean, boolean, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) - */ - @Override - public Query getRangeQuery(String field, String lower, String upper, boolean includeLower, boolean includeUpper, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return lqp.getRangeQuery(field, lower, upper, includeLower, includeUpper, analysisMode, luceneFunction); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchAllQuery() - */ - @Override - public Query getMatchAllQuery() throws ParseException - { - return new MatchAllDocsQuery(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchNoneQuery() - */ - @Override - public Query getMatchNoneQuery() throws ParseException - { - return new TermQuery(new Term("NO_TOKENS", "__")); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getLikeQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) - */ - @Override - public Query getLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException - { - return lqp.getLikeQuery(field, sqlLikeClause, analysisMode); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSearchParameters() - */ - @Override - public SearchParameters getSearchParameters() - { - return lqp.getSearchParameters(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSortField(java.lang.String) - */ - @Override - public String getSortField(String field) throws ParseException - { - // TODO - //return field; - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getIdentifierQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) - */ - @Override - public Query getIdentifierQuery(String field, String stringValue, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException - { - String[] split = stringValue.split(";"); - if(split.length == 1) - { - return lqp.getFieldQuery(field, stringValue, AnalysisMode.IDENTIFIER, luceneFunction); - } - else - { - if(split[1].equalsIgnoreCase("PWC")) - { - return getMatchNoneQuery(); - } - - BooleanQuery.Builder query = new BooleanQuery.Builder(); - BooleanQuery.Builder part1 = new BooleanQuery.Builder(); - part1.add(lqp.getFieldQuery(field, split[0], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); - part1.add(lqp.getFieldQuery("@"+ContentModel.PROP_VERSION_LABEL.toString(), split[1], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); - query.add(part1.build(), Occur.SHOULD); - - if(split[1].equals("1.0")) - { - BooleanQuery.Builder part2 = new BooleanQuery.Builder(); - part2.add(lqp.getFieldQuery(field, split[0], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); - part2.add(lqp.getFieldQuery(QueryConstants.FIELD_ASPECT, ContentModel.ASPECT_VERSIONABLE.toString(), AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST_NOT); - query.add(part2.build(), Occur.SHOULD); - } - return query.build(); - } - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getIdentifieLikeQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) - */ - @Override - public Query getIdentifieLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException - { - return getLikeQuery(field, sqlLikeClause, analysisMode); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#sortFieldExists(java.lang.String) - */ - @Override - public boolean sortFieldExists(String noLocalField) - { - // TODO Auto-generated method stub - //return false; - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String) - */ - @Override - public Query getFieldQuery(String field, String queryText) throws ParseException - { - return lqp.getFieldQuery(field, queryText); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#buildSort(java.util.List, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) - */ - @Override - public Sort buildSort(List list, FunctionEvaluationContext functionContext) throws ParseException - { - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFuzzyQuery(java.lang.String, java.lang.String, java.lang.Float) - */ - @Override - public Query getFuzzyQuery(String luceneFieldName, String term, Float minSimilarity) throws ParseException - { - return lqp.getFuzzyQuery(luceneFieldName, term, minSimilarity); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getField() - */ - @Override - public String getField() - { - return lqp.getField(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getPhraseSlop() - */ - @Override - public int getPhraseSlop() - { - return lqp.getPhraseSlop(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, java.lang.Integer, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) - */ - @Override - public Query getFieldQuery(String luceneFieldName, String term, AnalysisMode analysisMode, Integer slop, LuceneFunction luceneFunction) throws ParseException - { - return lqp.getFieldQuery(luceneFieldName, term, analysisMode, slop, luceneFunction); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getPrefixQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) - */ - @Override - public Query getPrefixQuery(String luceneFieldName, String term, AnalysisMode analysisMode) throws ParseException - { - return lqp.getPrefixQuery(luceneFieldName, term, analysisMode); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSpanQuery(java.lang.String, java.lang.String, java.lang.String, int, boolean) - */ - @Override - public Query getSpanQuery(String field, String first, String last, int slop, boolean inOrder) throws ParseException - { - return lqp.getSpanQuery(field, first, last, slop, inOrder); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getWildcardQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) - */ - @Override - public Query getWildcardQuery(String luceneFieldName, String term, AnalysisMode analysisMode) throws ParseException - { - return lqp.getWildcardQuery(luceneFieldName, term, analysisMode); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getNegatedQuery(java.lang.Object) - */ - @Override - public Query getNegatedQuery(Query query) throws ParseException - { - QueryParserExpressionAdaptor expressionAdaptor = getExpressionAdaptor(); - expressionAdaptor.addRequired(getMatchAllQuery()); - expressionAdaptor.addExcluded(query); - return expressionAdaptor.getQuery(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getExpressionAdaptor() - */ - @Override - public QueryParserExpressionAdaptor getExpressionAdaptor() - { - return new Lucene4QueryParserExpressionAdaptor(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchAllNodesQuery() - */ - @Override - public Query getMatchAllNodesQuery() - { - return new TermQuery(new Term("ISNODE", "T")); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getDatetimeSortField(java.lang.String, org.alfresco.service.cmr.dictionary.PropertyDefinition) - */ - @Override - public String getDatetimeSortField(String field, PropertyDefinition propertyDef) - { - throw new UnsupportedOperationException(); - } - - private class Lucene4QueryParserExpressionAdaptor implements QueryParserExpressionAdaptor - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addRequired(java.lang.Object) - */ - @Override - public void addRequired(Query q) - { - booleanQuery.add(q, Occur.MUST); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addExcluded(java.lang.Object) - */ - @Override - public void addExcluded(Query q) - { - booleanQuery.add(q, Occur.MUST_NOT); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addOptoinal(java.lang.Object) - */ - @Override - public void addOptional(Query q) - { - booleanQuery.add(q, Occur.SHOULD); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#getQuery() - */ - @Override - public Query getQuery() throws ParseException - { - BooleanQuery query = booleanQuery.build(); - if(query.clauses().size() == 0) - { - return getMatchNoneQuery(); - } - else if (query.clauses().size() == 1) - { - BooleanClause clause = query.clauses().get(0); - if(clause.isProhibited()) - { - booleanQuery.add(getMatchAllQuery(), Occur.MUST); - return booleanQuery.build(); - } - else - { - return clause.getQuery(); - } - } - else - { - return query; - } - } - - public Query getNegatedQuery() throws ParseException - { - BooleanQuery query = booleanQuery.build(); - if(query.clauses().size() == 0) - { - return getMatchAllQuery(); - } - else if (query.clauses().size() == 1) - { - BooleanClause clause = query.clauses().get(0); - if(clause.isProhibited()) - { - return clause.getQuery(); - } - else - { - return Lucene4QueryParserAdaptor.this.getNegatedQuery(getQuery()); - } - } - else - { - return Lucene4QueryParserAdaptor.this.getNegatedQuery(getQuery()); - } - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addRequired(java.lang.Object, float) - */ - @Override - public void addRequired(Query q, float boost) throws ParseException - { - addRequired(new BoostQuery(q, boost)); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addExcluded(java.lang.Object, float) - */ - @Override - public void addExcluded(Query q, float boost) throws ParseException - { - addExcluded(new BoostQuery(q, boost)); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addOptional(java.lang.Object, float) - */ - @Override - public void addOptional(Query q, float boost) throws ParseException - { - addOptional(new BoostQuery(q, boost)); - - } - - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.search.adaptor.QueryParserAdaptor; +import org.alfresco.repo.search.adaptor.QueryParserExpressionAdaptor; +import org.alfresco.repo.search.adaptor.AnalysisMode; +import org.alfresco.repo.search.adaptor.LuceneFunction; +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.Ordering; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.search.SearchParameters; +import org.apache.lucene.index.Term; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BoostQuery; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.TermQuery; + +/** + * @author Andy + * + */ +public class Lucene4QueryParserAdaptor implements QueryParserAdaptor +{ + + private Solr4QueryParser lqp; + + /** + * @param lqp + */ + public Lucene4QueryParserAdaptor(Solr4QueryParser lqp) + { + this.lqp = lqp; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) + */ + @Override + public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException + { + return lqp.getFieldQuery(field, queryText, analysisMode, luceneFunction); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getRangeQuery(java.lang.String, java.lang.String, java.lang.String, boolean, boolean, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) + */ + @Override + public Query getRangeQuery(String field, String lower, String upper, boolean includeLower, boolean includeUpper, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return lqp.getRangeQuery(field, lower, upper, includeLower, includeUpper, analysisMode, luceneFunction); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchAllQuery() + */ + @Override + public Query getMatchAllQuery() throws ParseException + { + return new MatchAllDocsQuery(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchNoneQuery() + */ + @Override + public Query getMatchNoneQuery() throws ParseException + { + return new TermQuery(new Term("NO_TOKENS", "__")); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getLikeQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) + */ + @Override + public Query getLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException + { + return lqp.getLikeQuery(field, sqlLikeClause, analysisMode); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSearchParameters() + */ + @Override + public SearchParameters getSearchParameters() + { + return lqp.getSearchParameters(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSortField(java.lang.String) + */ + @Override + public String getSortField(String field) throws ParseException + { + // TODO + //return field; + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getIdentifierQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) + */ + @Override + public Query getIdentifierQuery(String field, String stringValue, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException + { + String[] split = stringValue.split(";"); + if(split.length == 1) + { + return lqp.getFieldQuery(field, stringValue, AnalysisMode.IDENTIFIER, luceneFunction); + } + else + { + if(split[1].equalsIgnoreCase("PWC")) + { + return getMatchNoneQuery(); + } + + BooleanQuery.Builder query = new BooleanQuery.Builder(); + BooleanQuery.Builder part1 = new BooleanQuery.Builder(); + part1.add(lqp.getFieldQuery(field, split[0], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); + part1.add(lqp.getFieldQuery("@"+ContentModel.PROP_VERSION_LABEL.toString(), split[1], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); + query.add(part1.build(), Occur.SHOULD); + + if(split[1].equals("1.0")) + { + BooleanQuery.Builder part2 = new BooleanQuery.Builder(); + part2.add(lqp.getFieldQuery(field, split[0], AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST); + part2.add(lqp.getFieldQuery(QueryConstants.FIELD_ASPECT, ContentModel.ASPECT_VERSIONABLE.toString(), AnalysisMode.IDENTIFIER, luceneFunction), Occur.MUST_NOT); + query.add(part2.build(), Occur.SHOULD); + } + return query.build(); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getIdentifieLikeQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) + */ + @Override + public Query getIdentifieLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException + { + return getLikeQuery(field, sqlLikeClause, analysisMode); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#sortFieldExists(java.lang.String) + */ + @Override + public boolean sortFieldExists(String noLocalField) + { + // TODO Auto-generated method stub + //return false; + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String) + */ + @Override + public Query getFieldQuery(String field, String queryText) throws ParseException + { + return lqp.getFieldQuery(field, queryText); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#buildSort(java.util.List, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public Sort buildSort(List list, FunctionEvaluationContext functionContext) throws ParseException + { + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFuzzyQuery(java.lang.String, java.lang.String, java.lang.Float) + */ + @Override + public Query getFuzzyQuery(String luceneFieldName, String term, Float minSimilarity) throws ParseException + { + return lqp.getFuzzyQuery(luceneFieldName, term, minSimilarity); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getField() + */ + @Override + public String getField() + { + return lqp.getField(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getPhraseSlop() + */ + @Override + public int getPhraseSlop() + { + return lqp.getPhraseSlop(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getFieldQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode, java.lang.Integer, org.alfresco.repo.search.adaptor.lucene.LuceneFunction) + */ + @Override + public Query getFieldQuery(String luceneFieldName, String term, AnalysisMode analysisMode, Integer slop, LuceneFunction luceneFunction) throws ParseException + { + return lqp.getFieldQuery(luceneFieldName, term, analysisMode, slop, luceneFunction); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getPrefixQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) + */ + @Override + public Query getPrefixQuery(String luceneFieldName, String term, AnalysisMode analysisMode) throws ParseException + { + return lqp.getPrefixQuery(luceneFieldName, term, analysisMode); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getSpanQuery(java.lang.String, java.lang.String, java.lang.String, int, boolean) + */ + @Override + public Query getSpanQuery(String field, String first, String last, int slop, boolean inOrder) throws ParseException + { + return lqp.getSpanQuery(field, first, last, slop, inOrder); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getWildcardQuery(java.lang.String, java.lang.String, org.alfresco.repo.search.adaptor.lucene.AnalysisMode) + */ + @Override + public Query getWildcardQuery(String luceneFieldName, String term, AnalysisMode analysisMode) throws ParseException + { + return lqp.getWildcardQuery(luceneFieldName, term, analysisMode); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getNegatedQuery(java.lang.Object) + */ + @Override + public Query getNegatedQuery(Query query) throws ParseException + { + QueryParserExpressionAdaptor expressionAdaptor = getExpressionAdaptor(); + expressionAdaptor.addRequired(getMatchAllQuery()); + expressionAdaptor.addExcluded(query); + return expressionAdaptor.getQuery(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getExpressionAdaptor() + */ + @Override + public QueryParserExpressionAdaptor getExpressionAdaptor() + { + return new Lucene4QueryParserExpressionAdaptor(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getMatchAllNodesQuery() + */ + @Override + public Query getMatchAllNodesQuery() + { + return new TermQuery(new Term("ISNODE", "T")); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor#getDatetimeSortField(java.lang.String, org.alfresco.service.cmr.dictionary.PropertyDefinition) + */ + @Override + public String getDatetimeSortField(String field, PropertyDefinition propertyDef) + { + throw new UnsupportedOperationException(); + } + + private class Lucene4QueryParserExpressionAdaptor implements QueryParserExpressionAdaptor + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addRequired(java.lang.Object) + */ + @Override + public void addRequired(Query q) + { + booleanQuery.add(q, Occur.MUST); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addExcluded(java.lang.Object) + */ + @Override + public void addExcluded(Query q) + { + booleanQuery.add(q, Occur.MUST_NOT); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addOptoinal(java.lang.Object) + */ + @Override + public void addOptional(Query q) + { + booleanQuery.add(q, Occur.SHOULD); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#getQuery() + */ + @Override + public Query getQuery() throws ParseException + { + BooleanQuery query = booleanQuery.build(); + if(query.clauses().size() == 0) + { + return getMatchNoneQuery(); + } + else if (query.clauses().size() == 1) + { + BooleanClause clause = query.clauses().get(0); + if(clause.isProhibited()) + { + booleanQuery.add(getMatchAllQuery(), Occur.MUST); + return booleanQuery.build(); + } + else + { + return clause.getQuery(); + } + } + else + { + return query; + } + } + + public Query getNegatedQuery() throws ParseException + { + BooleanQuery query = booleanQuery.build(); + if(query.clauses().size() == 0) + { + return getMatchAllQuery(); + } + else if (query.clauses().size() == 1) + { + BooleanClause clause = query.clauses().get(0); + if(clause.isProhibited()) + { + return clause.getQuery(); + } + else + { + return Lucene4QueryParserAdaptor.this.getNegatedQuery(getQuery()); + } + } + else + { + return Lucene4QueryParserAdaptor.this.getNegatedQuery(getQuery()); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addRequired(java.lang.Object, float) + */ + @Override + public void addRequired(Query q, float boost) throws ParseException + { + addRequired(new BoostQuery(q, boost)); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addExcluded(java.lang.Object, float) + */ + @Override + public void addExcluded(Query q, float boost) throws ParseException + { + addExcluded(new BoostQuery(q, boost)); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryParserExpressionAdaptor#addOptional(java.lang.Object, float) + */ + @Override + public void addOptional(Query q, float boost) throws ParseException + { + addOptional(new BoostQuery(q, boost)); + + } + + } + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingAnalyticsQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingAnalyticsQuery.java index 7b3a8116d..6373e6f28 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingAnalyticsQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingAnalyticsQuery.java @@ -1,65 +1,65 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.util.HashMap; - -import org.apache.lucene.search.IndexSearcher; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.search.AnalyticsQuery; -import org.apache.solr.search.DelegatingCollector; - -/** - * @author Andy - * - */ -public class MimetypeGroupingAnalyticsQuery extends AnalyticsQuery -{ - private HashMap mappings; - private boolean group; - - /** - * @param mappings - * @param group - */ - public MimetypeGroupingAnalyticsQuery(HashMap mappings, boolean group) - { - this.mappings = mappings; - this.group = group; - - } - - /* (non-Javadoc) - * @see org.apache.solr.search.AnalyticsQuery#getAnalyticsCollector(org.apache.solr.handler.component.ResponseBuilder, org.apache.lucene.search.IndexSearcher) - */ - @Override - public DelegatingCollector getAnalyticsCollector(ResponseBuilder rb, IndexSearcher searcher) - { - return new MimetypeGroupingCollector(rb, mappings, group); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.util.HashMap; + +import org.apache.lucene.search.IndexSearcher; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.search.AnalyticsQuery; +import org.apache.solr.search.DelegatingCollector; + +/** + * @author Andy + * + */ +public class MimetypeGroupingAnalyticsQuery extends AnalyticsQuery +{ + private HashMap mappings; + private boolean group; + + /** + * @param mappings + * @param group + */ + public MimetypeGroupingAnalyticsQuery(HashMap mappings, boolean group) + { + this.mappings = mappings; + this.group = group; + + } + + /* (non-Javadoc) + * @see org.apache.solr.search.AnalyticsQuery#getAnalyticsCollector(org.apache.solr.handler.component.ResponseBuilder, org.apache.lucene.search.IndexSearcher) + */ + @Override + public DelegatingCollector getAnalyticsCollector(ResponseBuilder rb, IndexSearcher searcher) + { + return new MimetypeGroupingCollector(rb, mappings, group); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingCollector.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingCollector.java index 704bc31c0..e2495756b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingCollector.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingCollector.java @@ -1,132 +1,132 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map.Entry; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.SortedDocValues; -import org.apache.lucene.util.Counter; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.handler.component.ResponseBuilder; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.DelegatingCollector; - -/** - * @author Andy - * - */ -public class MimetypeGroupingCollector extends DelegatingCollector -{ - HashMap counters = new HashMap(); - ResponseBuilder rb; - private HashMap mappings; - private boolean doGroup; - String schemaFieldName; - SchemaField schemaField; - SortedDocValues sortedDocValues; - /** - * @param rb - * @param mappings - * @param group - */ - public MimetypeGroupingCollector(ResponseBuilder rb, HashMap mappings, boolean doGroup) - { - this.rb = rb; - this.mappings = mappings; - this.doGroup = doGroup; - schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty("content.mimetype", FieldUse.FACET, rb.req); - schemaField = rb.req.getSchema().getFieldOrNull(schemaFieldName); - } - - - @Override - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - if(schemaField != null) - { - try - { - sortedDocValues = context.reader().getSortedDocValues(schemaFieldName); - } - catch (IOException e) - { - - } - } - } - - @Override - public void collect(int doc) throws IOException - { - if(sortedDocValues != null) - { - int ordinal = sortedDocValues.getOrd(doc); - if(ordinal > -1) - { - String value = (String)schemaField.getType().toObject(schemaField, sortedDocValues.lookupOrd(ordinal)); - String group = doGroup ? mappings.get(value) : value; - if(group == null) - { - group = value; - } - - Counter counter = counters.get(group); - if(counter == null) - { - counter = Counter.newCounter(); - counters.put(group, counter); - } - counter.addAndGet(1); - } - } - - - leafDelegate.collect(doc); - } - - public void finish() throws IOException - { - NamedList analytics = new NamedList<>(); - rb.rsp.add("analytics", analytics); - NamedList fieldCounts = new NamedList<>(); - analytics.add("mimetype()", fieldCounts); - for (Entry counter : counters.entrySet()) - { - fieldCounts.add(counter.getKey(), counter.getValue().get()); - } - - if(this.delegate instanceof DelegatingCollector) { - ((DelegatingCollector)this.delegate).finish(); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map.Entry; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.util.Counter; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DelegatingCollector; + +/** + * @author Andy + * + */ +public class MimetypeGroupingCollector extends DelegatingCollector +{ + HashMap counters = new HashMap(); + ResponseBuilder rb; + private HashMap mappings; + private boolean doGroup; + String schemaFieldName; + SchemaField schemaField; + SortedDocValues sortedDocValues; + /** + * @param rb + * @param mappings + * @param group + */ + public MimetypeGroupingCollector(ResponseBuilder rb, HashMap mappings, boolean doGroup) + { + this.rb = rb; + this.mappings = mappings; + this.doGroup = doGroup; + schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty("content.mimetype", FieldUse.FACET, rb.req); + schemaField = rb.req.getSchema().getFieldOrNull(schemaFieldName); + } + + + @Override + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + if(schemaField != null) + { + try + { + sortedDocValues = context.reader().getSortedDocValues(schemaFieldName); + } + catch (IOException e) + { + + } + } + } + + @Override + public void collect(int doc) throws IOException + { + if(sortedDocValues != null) + { + int ordinal = sortedDocValues.getOrd(doc); + if(ordinal > -1) + { + String value = (String)schemaField.getType().toObject(schemaField, sortedDocValues.lookupOrd(ordinal)); + String group = doGroup ? mappings.get(value) : value; + if(group == null) + { + group = value; + } + + Counter counter = counters.get(group); + if(counter == null) + { + counter = Counter.newCounter(); + counters.put(group, counter); + } + counter.addAndGet(1); + } + } + + + leafDelegate.collect(doc); + } + + public void finish() throws IOException + { + NamedList analytics = new NamedList<>(); + rb.rsp.add("analytics", analytics); + NamedList fieldCounts = new NamedList<>(); + analytics.add("mimetype()", fieldCounts); + for (Entry counter : counters.entrySet()) + { + fieldCounts.add(counter.getKey(), counter.getValue().get()); + } + + if(this.delegate instanceof DelegatingCollector) { + ((DelegatingCollector)this.delegate).finish(); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingQParserPlugin.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingQParserPlugin.java index e875aeaf8..3557525e3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingQParserPlugin.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/MimetypeGroupingQParserPlugin.java @@ -1,163 +1,163 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; - -import org.apache.lucene.search.Query; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.core.SolrResourceLoader; -import org.apache.solr.internal.csv.CSVParser; -import org.apache.solr.internal.csv.CSVStrategy; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.SyntaxError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class MimetypeGroupingQParserPlugin extends QParserPlugin -{ - protected static final Logger LOGGER = LoggerFactory.getLogger(MimetypeGroupingQParserPlugin.class); - - private static HashMap mappings = new HashMap<>(); - - private static HashMap> reverseMappings = new HashMap<>(); - - public static synchronized HashMap> getReverseMappings() - { - return reverseMappings; - } - - private static synchronized void initMap(String mappingFile) - { - String solrHome = SolrResourceLoader.locateSolrHome().toString(); - File file = new File(solrHome, mappingFile); - - CSVParser parser; - try - { - parser = new CSVParser(new FileReader(file), CSVStrategy.DEFAULT_STRATEGY); - // parse the fieldnames from the header of the file - parser.getLine(); - - // read the rest of the CSV file - for (;;) - { - String[] vals = null; - - vals = parser.getLine(); - - if (vals == null) - break; - - mappings.put(vals[1], vals[2]); - - ArrayList grouped = reverseMappings.get(vals[2]); - if(grouped == null) - { - grouped = new ArrayList<>(); - reverseMappings.put(vals[2], grouped); - } - if (!grouped.contains(vals[1])) - { - grouped.add(vals[1]); - } - } - } - catch (Exception exception) - { - LOGGER.error("Exception during the MimetypeGroupingQParserPlugin. See the stacktrace below for further details.", exception); - } - } - - private NamedList args; - - /* - * (non-Javadoc) - * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) - */ - @Override - public void init(NamedList args) - { - this.args = args; - String mappingFile = (String) args.get("mapping"); - initMap(mappingFile); - - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, - * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, - * org.apache.solr.request.SolrQueryRequest) - */ - @Override - public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) - { - return new MimetypeGroupingQParser(qstr, localParams, params, req, args, mappings); - } - - public static class MimetypeGroupingQParser extends AbstractQParser - { - private HashMap mappings; - - private boolean group = true; - - public MimetypeGroupingQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args, HashMap mappings) - { - super(qstr, localParams, params, req, args); - Boolean doGroup = localParams.getBool("group"); - if(doGroup != null) - { - group = doGroup.booleanValue(); - } - - this.mappings = mappings; - } - - /* - * (non-Javadoc) - * @see org.apache.solr.search.QParser#parse() - */ - @Override - public Query parse() throws SyntaxError - { - return new MimetypeGroupingAnalyticsQuery(mappings, group); - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.lucene.search.Query; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.core.SolrResourceLoader; +import org.apache.solr.internal.csv.CSVParser; +import org.apache.solr.internal.csv.CSVStrategy; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.QParser; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.SyntaxError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class MimetypeGroupingQParserPlugin extends QParserPlugin +{ + protected static final Logger LOGGER = LoggerFactory.getLogger(MimetypeGroupingQParserPlugin.class); + + private static HashMap mappings = new HashMap<>(); + + private static HashMap> reverseMappings = new HashMap<>(); + + public static synchronized HashMap> getReverseMappings() + { + return reverseMappings; + } + + private static synchronized void initMap(String mappingFile) + { + String solrHome = SolrResourceLoader.locateSolrHome().toString(); + File file = new File(solrHome, mappingFile); + + CSVParser parser; + try + { + parser = new CSVParser(new FileReader(file), CSVStrategy.DEFAULT_STRATEGY); + // parse the fieldnames from the header of the file + parser.getLine(); + + // read the rest of the CSV file + for (;;) + { + String[] vals = null; + + vals = parser.getLine(); + + if (vals == null) + break; + + mappings.put(vals[1], vals[2]); + + ArrayList grouped = reverseMappings.get(vals[2]); + if(grouped == null) + { + grouped = new ArrayList<>(); + reverseMappings.put(vals[2], grouped); + } + if (!grouped.contains(vals[1])) + { + grouped.add(vals[1]); + } + } + } + catch (Exception exception) + { + LOGGER.error("Exception during the MimetypeGroupingQParserPlugin. See the stacktrace below for further details.", exception); + } + } + + private NamedList args; + + /* + * (non-Javadoc) + * @see org.apache.solr.util.plugin.NamedListInitializedPlugin#init(org.apache.solr.common.util.NamedList) + */ + @Override + public void init(NamedList args) + { + this.args = args; + String mappingFile = (String) args.get("mapping"); + initMap(mappingFile); + + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParserPlugin#createParser(java.lang.String, + * org.apache.solr.common.params.SolrParams, org.apache.solr.common.params.SolrParams, + * org.apache.solr.request.SolrQueryRequest) + */ + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + return new MimetypeGroupingQParser(qstr, localParams, params, req, args, mappings); + } + + public static class MimetypeGroupingQParser extends AbstractQParser + { + private HashMap mappings; + + private boolean group = true; + + public MimetypeGroupingQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req, NamedList args, HashMap mappings) + { + super(qstr, localParams, params, req, args); + Boolean doGroup = localParams.getBool("group"); + if(doGroup != null) + { + group = doGroup.booleanValue(); + } + + this.mappings = mappings; + } + + /* + * (non-Javadoc) + * @see org.apache.solr.search.QParser#parse() + */ + @Override + public Query parse() throws SyntaxError + { + return new MimetypeGroupingAnalyticsQuery(mappings, group); + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/PostFilterQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/PostFilterQuery.java index 386598b2d..ae3888b9e 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/PostFilterQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/PostFilterQuery.java @@ -1,257 +1,257 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import org.alfresco.solr.ContextAwareQuery; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BoostQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.solr.search.DelegatingCollector; -import org.apache.solr.search.PostFilter; - -public class PostFilterQuery extends Query implements PostFilter -{ - private int cost; - private final Query query; - - public PostFilterQuery(int cost, Query query) - { - this.cost = cost; - if (query == null) throw new IllegalStateException("query cannot be null"); - this.query = query; - } - - @Override - public int hashCode() { - //DON'T INCLUDE COST?? - int result = cost; - result = 31 * result + query.hashCode(); - return result; - } - - //THIS WAS WRONG public int hashcode() - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof PostFilterQuery)) return false; - - PostFilterQuery that = (PostFilterQuery) o; - - //DON'T INCLUDE COST?? if (cost != that.cost) return false; - return query.equals(that.query); - } - - public int getCost() - { - return cost; - } - - @Override - public void setCost(int cost) - { - this.cost = cost; - } - - public boolean getCache() - { - return false; - } - - @Override - public void setCache(boolean cache) - { - - } - - public boolean getCacheSep() - { - return false; - } - - @Override - public void setCacheSep(boolean cacheSep) - { - - } - - public String toString(String s) - { - return s; - } - - public DelegatingCollector getFilterCollector(IndexSearcher searcher) - { - List postFilters = new ArrayList(); - getPostFilters(query, postFilters); - - Collections.sort(postFilters, new PostFilterComp()); - - List delegatingCollectors = new ArrayList(); - for(PostFilter postFilter : postFilters) - { - DelegatingCollector delegatingCollector = postFilter.getFilterCollector(searcher); - if(!(delegatingCollector instanceof AllAccessCollector)) { - delegatingCollectors.add(delegatingCollector); - } - } - - if(delegatingCollectors.size() == 0) - { - return new AllAccessCollector(); - } - else if(delegatingCollectors.size() == 1) - { - return delegatingCollectors.get(0); - } - else - { - return new WrapperCollector(delegatingCollectors); - } - } - - private static class WrapperCollector extends DelegatingCollector - { - private DelegatingCollector innerDelegate; - private CollectorSink sink; - - public WrapperCollector(List delegatingCollectors) - { - for(DelegatingCollector delegatingCollector : delegatingCollectors) - { - if(innerDelegate == null) - { - innerDelegate = delegatingCollector; - } - else - { - innerDelegate.setLastDelegate(delegatingCollector); - } - } - - this.sink = new CollectorSink(); - innerDelegate.setLastDelegate(this.sink); - } - - public void setScorer(Scorer scorer) throws IOException - { - super.setScorer(scorer); - innerDelegate.setScorer(scorer); - } - - protected void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - innerDelegate.getLeafCollector(context); - } - - - public void collect(int doc) throws IOException - { - innerDelegate.collect(doc); - if(sink.doc == doc) { - sink.doc = -1; - super.collect(doc); - } - } - } - - private static class CollectorSink extends DelegatingCollector - { - public int doc = -1; - - public void collect(int doc) throws IOException - { - this.doc = doc; - } - - public void doSetNextReader(LeafReaderContext context) - { - - } - } - - private void getPostFilters(Query q, List postFilters) - { - if(q instanceof BooleanQuery) - { - BooleanQuery bq = (BooleanQuery) q; - List clauses = bq.clauses(); - for (BooleanClause clause : clauses) - { - Query q1 = clause.getQuery(); - getPostFilters(q1, postFilters); - } - } - else if(q instanceof ContextAwareQuery) - { - ContextAwareQuery cq = (ContextAwareQuery)q; - getPostFilters(cq.getLuceneQuery(), postFilters); - } - else if(q instanceof PostFilter) - { - postFilters.add((PostFilter)q); - } - else if(q instanceof BoostQuery) - { - BoostQuery bq = (BoostQuery)q; - getPostFilters(bq.getQuery(), postFilters); - } - } - - private class PostFilterComp implements Comparator - { - public int compare(PostFilter a, PostFilter b) - { - int costa = a.getCost(); - int costb = b.getCost(); - if(costa == costb) - { - return 0; - } - else if(costa < costb) - { - return -1; - } - else - { - return 1; - } - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.alfresco.solr.ContextAwareQuery; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BoostQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.solr.search.DelegatingCollector; +import org.apache.solr.search.PostFilter; + +public class PostFilterQuery extends Query implements PostFilter +{ + private int cost; + private final Query query; + + public PostFilterQuery(int cost, Query query) + { + this.cost = cost; + if (query == null) throw new IllegalStateException("query cannot be null"); + this.query = query; + } + + @Override + public int hashCode() { + //DON'T INCLUDE COST?? + int result = cost; + result = 31 * result + query.hashCode(); + return result; + } + + //THIS WAS WRONG public int hashcode() + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PostFilterQuery)) return false; + + PostFilterQuery that = (PostFilterQuery) o; + + //DON'T INCLUDE COST?? if (cost != that.cost) return false; + return query.equals(that.query); + } + + public int getCost() + { + return cost; + } + + @Override + public void setCost(int cost) + { + this.cost = cost; + } + + public boolean getCache() + { + return false; + } + + @Override + public void setCache(boolean cache) + { + + } + + public boolean getCacheSep() + { + return false; + } + + @Override + public void setCacheSep(boolean cacheSep) + { + + } + + public String toString(String s) + { + return s; + } + + public DelegatingCollector getFilterCollector(IndexSearcher searcher) + { + List postFilters = new ArrayList(); + getPostFilters(query, postFilters); + + Collections.sort(postFilters, new PostFilterComp()); + + List delegatingCollectors = new ArrayList(); + for(PostFilter postFilter : postFilters) + { + DelegatingCollector delegatingCollector = postFilter.getFilterCollector(searcher); + if(!(delegatingCollector instanceof AllAccessCollector)) { + delegatingCollectors.add(delegatingCollector); + } + } + + if(delegatingCollectors.size() == 0) + { + return new AllAccessCollector(); + } + else if(delegatingCollectors.size() == 1) + { + return delegatingCollectors.get(0); + } + else + { + return new WrapperCollector(delegatingCollectors); + } + } + + private static class WrapperCollector extends DelegatingCollector + { + private DelegatingCollector innerDelegate; + private CollectorSink sink; + + public WrapperCollector(List delegatingCollectors) + { + for(DelegatingCollector delegatingCollector : delegatingCollectors) + { + if(innerDelegate == null) + { + innerDelegate = delegatingCollector; + } + else + { + innerDelegate.setLastDelegate(delegatingCollector); + } + } + + this.sink = new CollectorSink(); + innerDelegate.setLastDelegate(this.sink); + } + + public void setScorer(Scorer scorer) throws IOException + { + super.setScorer(scorer); + innerDelegate.setScorer(scorer); + } + + protected void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + innerDelegate.getLeafCollector(context); + } + + + public void collect(int doc) throws IOException + { + innerDelegate.collect(doc); + if(sink.doc == doc) { + sink.doc = -1; + super.collect(doc); + } + } + } + + private static class CollectorSink extends DelegatingCollector + { + public int doc = -1; + + public void collect(int doc) throws IOException + { + this.doc = doc; + } + + public void doSetNextReader(LeafReaderContext context) + { + + } + } + + private void getPostFilters(Query q, List postFilters) + { + if(q instanceof BooleanQuery) + { + BooleanQuery bq = (BooleanQuery) q; + List clauses = bq.clauses(); + for (BooleanClause clause : clauses) + { + Query q1 = clause.getQuery(); + getPostFilters(q1, postFilters); + } + } + else if(q instanceof ContextAwareQuery) + { + ContextAwareQuery cq = (ContextAwareQuery)q; + getPostFilters(cq.getLuceneQuery(), postFilters); + } + else if(q instanceof PostFilter) + { + postFilters.add((PostFilter)q); + } + else if(q instanceof BoostQuery) + { + BoostQuery bq = (BoostQuery)q; + getPostFilters(bq.getQuery(), postFilters); + } + } + + private class PostFilterComp implements Comparator + { + public int compare(PostFilter a, PostFilter b) + { + int costa = a.getCost(); + int costb = b.getCost(); + if(costa == costb) + { + return 0; + } + else if(costa < costb) + { + return -1; + } + else + { + return 1; + } + } + } + +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/RelativeStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/RelativeStructuredFieldPosition.java index 77b481a9f..a530329d7 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/RelativeStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/RelativeStructuredFieldPosition.java @@ -1,142 +1,142 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -/** - * Search for a term relative to the last one found. - * - * @author andyh - */ -public class RelativeStructuredFieldPosition extends AbstractStructuredFieldPosition -{ - - int relativePosition; - - /** - * - */ - public RelativeStructuredFieldPosition(String termText) - { - super(getTermText(termText), true, false); - relativePosition = 1; - - } - - private static String getTermText(String termText) - { - if((termText != null) && (termText.equals("*"))) - { - return null; - } - else - { - return termText; - } - } - - public RelativeStructuredFieldPosition() - { - super(null, false, false); - relativePosition = 1; - } - - /* - * (non-Javadoc) - * - * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, - * int, org.apache.lucene.index.TermPositions) - */ - public int matches(int start, int end, int offset) throws IOException - { - - if (getCachingTermPositions() != null) - { - // Doing "termText" - getCachingTermPositions().reset(); - int count = getCachingTermPositions().freq(); - int requiredPosition = offset + relativePosition; - int realPosition = 0; - int adjustedPosition = 0; - for (int i = 0; i < count; i++) - { - realPosition = getCachingTermPositions().nextPosition(); - adjustedPosition = realPosition - start; - if ((end != -1) && (realPosition > end)) - { - return -1; - } - if (adjustedPosition == requiredPosition) - { - return adjustedPosition; - } - if (adjustedPosition > requiredPosition) - { - return -1; - } - } - } - else - { - // Doing "*"; - return offset + 1; - } - return -1; - } - - public String getDescription() - { - return "Relative Named child"; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + relativePosition; - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - RelativeStructuredFieldPosition other = (RelativeStructuredFieldPosition) obj; - if (relativePosition != other.relativePosition) - return false; - return true; - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +/** + * Search for a term relative to the last one found. + * + * @author andyh + */ +public class RelativeStructuredFieldPosition extends AbstractStructuredFieldPosition +{ + + int relativePosition; + + /** + * + */ + public RelativeStructuredFieldPosition(String termText) + { + super(getTermText(termText), true, false); + relativePosition = 1; + + } + + private static String getTermText(String termText) + { + if((termText != null) && (termText.equals("*"))) + { + return null; + } + else + { + return termText; + } + } + + public RelativeStructuredFieldPosition() + { + super(null, false, false); + relativePosition = 1; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int, + * int, org.apache.lucene.index.TermPositions) + */ + public int matches(int start, int end, int offset) throws IOException + { + + if (getCachingTermPositions() != null) + { + // Doing "termText" + getCachingTermPositions().reset(); + int count = getCachingTermPositions().freq(); + int requiredPosition = offset + relativePosition; + int realPosition = 0; + int adjustedPosition = 0; + for (int i = 0; i < count; i++) + { + realPosition = getCachingTermPositions().nextPosition(); + adjustedPosition = realPosition - start; + if ((end != -1) && (realPosition > end)) + { + return -1; + } + if (adjustedPosition == requiredPosition) + { + return adjustedPosition; + } + if (adjustedPosition > requiredPosition) + { + return -1; + } + } + } + else + { + // Doing "*"; + return offset + 1; + } + return -1; + } + + public String getDescription() + { + return "Relative Named child"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + relativePosition; + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + RelativeStructuredFieldPosition other = (RelativeStructuredFieldPosition) obj; + if (relativePosition != other.relativePosition) + return false; + return true; + } + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SelfAxisStructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SelfAxisStructuredFieldPosition.java index 7465f2dce..da6a32acd 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SelfAxisStructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SelfAxisStructuredFieldPosition.java @@ -1,62 +1,62 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -public class SelfAxisStructuredFieldPosition extends AbstractStructuredFieldPosition -{ - - public SelfAxisStructuredFieldPosition() - { - super(null, true, false); - } - - public int matches(int start, int end, int offset) throws IOException - { - return offset; - } - - public String getDescription() - { - return "Self Axis"; - } - - public boolean linkSelf() - { - return true; - } - - public boolean isTerminal() - { - return false; - } - - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +public class SelfAxisStructuredFieldPosition extends AbstractStructuredFieldPosition +{ + + public SelfAxisStructuredFieldPosition() + { + super(null, true, false); + } + + public int matches(int start, int end, int offset) throws IOException + { + return offset; + } + + public String getDescription() + { + return "Self Axis"; + } + + public boolean linkSelf() + { + return true; + } + + public boolean isTerminal() + { + return false; + } + + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Solr4QueryParser.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Solr4QueryParser.java index a0538163d..4dfa281c2 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Solr4QueryParser.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/Solr4QueryParser.java @@ -1,5563 +1,5563 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import edu.umd.cs.findbugs.annotations.SuppressWarnings; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.dictionary.IndexTokenisationMode; -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.repo.search.adaptor.AnalysisMode; -import org.alfresco.repo.search.adaptor.LuceneFunction; -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.repo.search.impl.QueryParserUtils; -import org.alfresco.repo.search.impl.lucene.analysis.MLTokenDuplicator; -import org.alfresco.repo.search.impl.parsers.FTSQueryException; -import org.alfresco.repo.search.impl.parsers.FTSQueryParser; -import org.alfresco.repo.search.impl.parsers.FTSQueryParser.RerankPhase; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.cmr.dictionary.AspectDefinition; -import org.alfresco.service.cmr.dictionary.ClassDefinition; -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.dictionary.TypeDefinition; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.AlfrescoAnalyzerWrapper; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.SpecializedFieldType; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldInstance; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.alfresco.solr.AlfrescoSolrDataModel.IndexedField; -import org.alfresco.solr.component.FingerPrintComponent; -import org.alfresco.solr.utils.ThrowingFunction; -import org.alfresco.util.CachingDateFormat; -import org.alfresco.util.Pair; -import org.alfresco.util.SearchLanguageConversion; -import org.antlr.misc.OrderedHashSet; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.commongrams.CommonGramsFilter; -import org.apache.lucene.analysis.shingle.ShingleFilterFactory; -import org.apache.lucene.analysis.synonym.SynonymFilter; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; -import org.apache.lucene.analysis.util.TokenFilterFactory; -import org.apache.lucene.document.Document; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.index.Term; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.queryparser.classic.QueryParser; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BooleanQuery.Builder; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.LegacyNumericRangeQuery; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.MultiTermQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.RegexpQuery; -import org.apache.lucene.search.ScoreDoc; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.TermRangeQuery; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper; -import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper.TopTermsSpanBooleanQueryRewrite; -import org.apache.lucene.search.spans.SpanNearQuery; -import org.apache.lucene.search.spans.SpanOrQuery; -import org.apache.lucene.search.spans.SpanQuery; -import org.apache.lucene.search.spans.SpanTermQuery; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.Version; -import org.apache.solr.analysis.TokenizerChain; -import org.apache.solr.client.solrj.SolrRequest; -import org.apache.solr.client.solrj.impl.HttpClientUtil; -import org.apache.solr.client.solrj.impl.HttpSolrClient; -import org.apache.solr.client.solrj.request.QueryRequest; -import org.apache.solr.client.solrj.response.QueryResponse; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.core.SolrCore; -import org.apache.solr.handler.component.HttpShardHandlerFactory; -import org.apache.solr.handler.component.ShardHandlerFactory; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.DocsStreamer; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.update.UpdateShardHandlerConfig; -import org.jaxen.saxpath.SAXPathException; -import org.jaxen.saxpath.base.XPathReader; -import org.json.JSONObject; -import org.springframework.extensions.surf.util.I18NUtil; - -import java.io.IOException; -import java.io.StringReader; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.apache.solr.search.QueryParsing.DEFTYPE; - -/** - * @author Andy - * - */ -public class Solr4QueryParser extends QueryParser implements QueryConstants -{ - - private static final String CMIS_REQUEST_HANDLER = "cmis"; - - public Solr4QueryParser(SolrQueryRequest req, Version matchVersion, String f, Analyzer a, - FTSQueryParser.RerankPhase rerankPhase) - { - super(f, a); - setAllowLeadingWildcard(true); - setAnalyzeRangeTerms(true); - this.rerankPhase = rerankPhase; - this.schema = req.getSchema(); - this.solrParams = req.getParams(); - SolrCore core = req.getCore(); - if(core != null) { - this.shardHandlerFactory = core.getCoreContainer().getShardHandlerFactory(); - } - this.request = req; - - } - - private RerankPhase rerankPhase; - private SolrParams solrParams; - private ShardHandlerFactory shardHandlerFactory; - private SolrQueryRequest request; - - - IndexSchema schema; - - @SuppressWarnings("unused") - private static Log logger = LogFactory.getLog(Solr4QueryParser.class); - - protected NamespacePrefixResolver namespacePrefixResolver; - - protected DictionaryService dictionaryService; - - private TenantService tenantService; - - private SearchParameters searchParameters; - - private MLAnalysisMode mlAnalysisMode = MLAnalysisMode.EXACT_LANGUAGE_AND_ALL; - - private int internalSlop = 0; - - int topTermSpanRewriteLimit = 1000; - - /** - * @param topTermSpanRewriteLimit - * the topTermSpanRewriteLimit to set - */ - public void setTopTermSpanRewriteLimit(int topTermSpanRewriteLimit) - { - this.topTermSpanRewriteLimit = topTermSpanRewriteLimit; - } - - /** - * @param searchParameters - */ - public void setSearchParameters(SearchParameters searchParameters) - { - this.searchParameters = searchParameters; - } - - /** - * @param namespacePrefixResolver - */ - public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) - { - this.namespacePrefixResolver = namespacePrefixResolver; - } - - /** - * @param tenantService - */ - public void setTenantService(TenantService tenantService) - { - this.tenantService = tenantService; - } - - public SearchParameters getSearchParameters() - { - return searchParameters; - } - - @Override - protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException - { - try - { - internalSlop = slop; - Query query; - query = getFieldQuery(field, queryText); - return query; - } - finally - { - internalSlop = 0; - } - - } - - /** - * @param field - * @param queryText - * @param analysisMode - * @param slop - * @param luceneFunction - * @return the query - * @throws ParseException - * @throws IOException - */ - public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, int slop, - LuceneFunction luceneFunction) throws ParseException - { - try - { - internalSlop = slop; - Query query = getFieldQuery(field, queryText, analysisMode, luceneFunction); - return query; - } - finally - { - internalSlop = 0; - } - - } - - /** - * @param field - * @param sqlLikeClause - * @param analysisMode - * @return the query - * @throws ParseException - */ - public Query getLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException - { - String luceneWildCardExpression = translate(sqlLikeClause); - return getWildcardQuery(field, luceneWildCardExpression, AnalysisMode.LIKE); - } - - private String translate(String string) - { - StringBuilder builder = new StringBuilder(string.length()); - - boolean lastWasEscape = false; - - for (int i = 0; i < string.length(); i++) - { - char c = string.charAt(i); - if (lastWasEscape) - { - builder.append(c); - lastWasEscape = false; - } else - { - if (c == '\\') - { - lastWasEscape = true; - } else if (c == '%') - { - builder.append('*'); - } else if (c == '_') - { - builder.append('?'); - } else if (c == '*') - { - builder.append('\\'); - builder.append(c); - } else if (c == '?') - { - builder.append('\\'); - builder.append(c); - } else - { - builder.append(c); - } - } - } - if (lastWasEscape) - { - throw new FTSQueryException("Escape character at end of string " + string); - } - - return builder.toString(); - } - - /** - * @param field - * @param queryText - * @param analysisMode - * @param luceneFunction - * @return the query - * @throws ParseException - * @throws IOException - */ - public Query getDoesNotMatchFieldQuery(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - Query allQuery = new MatchAllDocsQuery(); - Query matchQuery = getFieldQuery(field, queryText, analysisMode, luceneFunction); - if ((matchQuery != null)) - { - query.add(allQuery, Occur.MUST); - query.add(matchQuery, Occur.MUST_NOT); - } else - { - throw new UnsupportedOperationException(); - } - return query.build(); - } - - public Query getFieldQuery(String field, String queryText) throws ParseException - { - return getFieldQuery(field, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); - } - - /** - * @param field - * @param first - * @param last - * @param slop - * @param inOrder - * @return the query - * @throws ParseException - */ - public Query getSpanQuery(String field, String first, String last, int slop, boolean inOrder) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATH); - } - else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - return createDefaultTextQuery(textField -> getSpanQuery(textField, first, last, slop, inOrder)); - } else if (field.equals(FIELD_CLASS)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_CLASS); - } else if (field.equals(FIELD_TYPE)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_TYPE); - } else if (field.equals(FIELD_EXACTTYPE)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTTYPE); - } else if (field.equals(FIELD_ASPECT)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_ASPECT); - } else if (field.equals(FIELD_EXACTASPECT)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTASPECT); - } else if (isPropertyField(field)) - { - return spanQueryBuilder(field, first, last, slop, inOrder); - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); - query.add(part, Occur.SHOULD); - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getSpanQuery(fieldName, first, last, slop, inOrder); - query.add(part, Occur.SHOULD); - } - return query.build(); - } - - } else if (field.equals(FIELD_ISUNSET)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISUNSET); - } else if (field.equals(FIELD_ISNULL)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNULL); - } else if (field.equals(FIELD_ISNOTNULL)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNOTNULL); - } else if (field.equals(FIELD_EXISTS)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXISTS); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); - query.add(part, Occur.SHOULD); - } - return query.build(); - } else if (field.equals(FIELD_FTSSTATUS)) - { - throw new UnsupportedOperationException("Span is not supported for " + FIELD_FTSSTATUS); - } else if (field.equals(FIELD_TAG)) - { - return null; - } else if (isPropertyField(field)) - { - return spanQueryBuilder(field, first, last, slop, inOrder); - } else - { - BytesRef firstBytes = analyzeMultitermTerm(field, first); - BytesRef lastBytes = analyzeMultitermTerm(field, last); - SpanQuery firstTerm = new SpanTermQuery(new Term(field, firstBytes)); - SpanQuery lastTerm = new SpanTermQuery(new Term(field, lastBytes)); - return new SpanNearQuery(new SpanQuery[] - { firstTerm, lastTerm }, slop, inOrder); - } - } - - protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException - { - return getFieldQuery(field, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); - } - - /** - * @param field - * @param queryText - * @param analysisMode - * @param luceneFunction - * @return the query - * @throws ParseException - * @throws IOException - */ - @SuppressWarnings("deprecation") - public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - try - { - if (field.equals(FIELD_PATH)) - { - return createPathQuery(queryText, false); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - return createPathQuery(queryText, true); - } else if (field.equals(FIELD_TEXT)) - { - return createTextQuery(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_ID)) - { - return createIdQuery(queryText); - } else if (field.equals(FIELD_SOLR4_ID)) - { - return createSolr4IdQuery(queryText); - } else if (field.equals(FIELD_DBID)) - { - return createDbidQuery(queryText); - } else if (field.equals(FIELD_ACLID)) - { - return createAclIdQuery(queryText); - } else if (field.equals(FIELD_OWNER)) - { - return createOwnerQuery(queryText); - } else if (field.equals(FIELD_OWNERSET)) - { - return createOwnerSetQuery(queryText); - } else if (field.equals(FIELD_READER)) - { - return createReaderQuery(queryText); - } else if (field.equals(FIELD_READERSET)) - { - return createReaderSetQuery(queryText); - } else if (field.equals(FIELD_AUTHORITY)) - { - /* - * ACL DOCUMENTATION STARTS HERE - * This creates the query that applies the ACL filter - */ - return createAuthorityQuery(queryText); - } else if (field.equals(FIELD_AUTHORITYSET)) - { - /* - * ACL DOCUMENTATION STARTS HERE - * This creates the query that applies the ACL filter - */ - return createAuthoritySetQuery(queryText); - } else if (field.equals(FIELD_DENIED)) - { - return createDeniedQuery(queryText); - } else if (field.equals(FIELD_DENYSET)) - { - return createDenySetQuery(queryText); - } else if (field.equals(FIELD_ISROOT)) - { - return createIsRootQuery(queryText); - } else if (field.equals(FIELD_ISCONTAINER)) - { - return createIsContainerQuery(queryText); - } else if (field.equals(FIELD_ISNODE)) - { - return createIsNodeQuery(queryText); - } else if (field.equals(FIELD_TX)) - { - return createTransactionQuery(queryText); - } else if (field.equals(FIELD_INTXID)) - { - return createInTxIdQuery(queryText); - } else if (field.equals(FIELD_INACLTXID)) - { - return createInAclTxIdQuery(queryText); - } else if (field.equals(FIELD_PARENT)) - { - return createParentQuery(queryText); - } else if (field.equals(FIELD_PRIMARYPARENT)) - { - return createPrimaryParentQuery(queryText); - } else if (field.equals(FIELD_QNAME)) - { - return createQNameQuery(queryText); - } else if (field.equals(FIELD_PRIMARYASSOCQNAME)) - { - return createPrimaryAssocQNameQuery(queryText); - } else if (field.equals(FIELD_PRIMARYASSOCTYPEQNAME)) - { - return createPrimaryAssocTypeQNameQuery(queryText); - } else if (field.equals(FIELD_ASSOCTYPEQNAME)) - { - return createAssocTypeQNameQuery(queryText); - } else if (field.equals(FIELD_CLASS)) - { - ClassDefinition target = QueryParserUtils.matchClassDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (target == null) - { - return new TermQuery(new Term(FIELD_TYPE, "_unknown_")); - } - return getFieldQuery(target.isAspect() ? FIELD_ASPECT : FIELD_TYPE, queryText, analysisMode, - luceneFunction); - } else if (field.equals(FIELD_TYPE)) - { - return createTypeQuery(queryText, false); - } else if (field.equals(FIELD_EXACTTYPE)) - { - return createTypeQuery(queryText, true); - } else if (field.equals(FIELD_ASPECT)) - { - return createAspectQuery(queryText, false); - } else if (field.equals(FIELD_EXACTASPECT)) - { - return createAspectQuery(queryText, true); - } else if (isPropertyField(field)) - { - Query query = attributeQueryBuilder(field, queryText, new FieldQuery(), analysisMode, luceneFunction); - return query; - } else if (field.equals(FIELD_ALL)) - { - return createAllQuery(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_ISUNSET)) - { - return createIsUnsetQuery(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_ISNULL)) - { - return createIsNullQuery(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_ISNOTNULL)) - { - return createIsNotNull(queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_EXISTS)) - { - return createExistsQuery(queryText, analysisMode, luceneFunction); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field) != null) - { - return createDataTypeDefinitionQuery(field, queryText, analysisMode, luceneFunction); - } else if (field.equals(FIELD_TXID)) - { - return createTxIdQuery(queryText); - } else if (field.equals(FIELD_ACLTXID)) - { - return createAclTxIdQuery(queryText); - } else if (field.equals(FIELD_TXCOMMITTIME)) - { - return createTxCommitTimeQuery(queryText); - } else if (field.equals(FIELD_ACLTXCOMMITTIME)) - { - return createAclTxCommitTimeQuery(queryText); - } else if (field.equals(FIELD_TAG)) - { - return createTagQuery(queryText); - } else if (field.equals(FIELD_SITE)) - { - return createSiteQuery(queryText); - } else if (field.equals(FIELD_PNAME)) - { - return createPNameQuery(queryText); - } else if (field.equals(FIELD_NPATH)) - { - return createNPathQuery(queryText); - } else if (field.equals(FIELD_TENANT)) - { - return createTenantQuery(queryText); - } else if (field.equals(FIELD_ANCESTOR)) - { - return createAncestorQuery(queryText); - } else if (field.equals(FIELD_FINGERPRINT)) - { - String[] parts = queryText.split("_"); - Collection values = null; - String nodeId = parts[0]; - - JSONObject json = (JSONObject)request.getContext().get(AbstractQParser.ALFRESCO_JSON); - String fingerPrint = null; - if(json != null) { - //Was the fingerprint passed in - String fingerPrintKey = "fingerprint." + nodeId; - if(json.has(fingerPrintKey)) { - fingerPrint = (String) json.get("fingerprint." + nodeId); - if (fingerPrint != null) { - List l = new ArrayList(); - String[] hashes = fingerPrint.split(" "); - for (String hash : hashes) { - l.add(hash); - } - values = l; - } - } - } else { - json = new JSONObject(); - } - - if (values == null) { - values = fetchMinHash(nodeId); - } - - String shards = this.solrParams.get("shards"); - if(values == null && shards != null) { - //we are in distributed mode - //Fetch the fingerPrint from the shards. - //The UUID and DBID will both work for method call. - values = fetchFingerPrint(shards, nodeId); - } - - //If we're in distributed mode then add the fingerprint to the json - if(values != null && shards != null && fingerPrint == null) { - ModifiableSolrParams newParams = new ModifiableSolrParams(); - newParams.add(solrParams); - solrParams = newParams; - json.put("fingerprint." + nodeId, join(values, " ")); - String jsonString = json.toString(); - newParams.add(AbstractQParser.ALFRESCO_JSON, jsonString); - request.getContext().put(AbstractQParser.ALFRESCO_JSON, json); - request.setParams(newParams); - } - - return createFingerPrintQuery(field, queryText, values, analysisMode, luceneFunction); - - } else - { - return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); - } - - } catch (SAXPathException e) - { - throw new ParseException("Failed to parse XPath...\n" + e.getMessage()); - } catch (IOException e) - { - throw new ParseException("IO: " + e.getMessage()); - } - - } - - private boolean isNumber(String s) { - for(int i=0; i 0) { - builder.append(" "); - } - builder.append(o.toString()); - } - return builder.toString(); - } - - - /** - * fetch MINHASH values from lucene index - * @param id argument of search. It is taken as DBID if is numberic, LID otherwise - * @return List of minhashes - * @throws IOException - */ - private Collection fetchMinHash(String id) throws IOException { - SolrIndexSearcher searcher = request.getSearcher(); - - - Query q; - if(isNumber(id)) - { - long dbid = Long.parseLong(id); - q = LegacyNumericRangeQuery.newLongRange("DBID", dbid, dbid + 1, true, false); - } - else - { - String query = id.startsWith("workspace") ? id : "workspace://SpacesStore/"+id; - q = new TermQuery(new Term(FIELD_LID, query)); - } - - TopDocs docs = searcher.search(q, 1); - Set fields = new HashSet(); - fields.add(FIELD_FINGERPRINT); - - if(docs.totalHits == 1) { - ScoreDoc scoreDoc = docs.scoreDocs[0]; - Document doc = searcher.doc(scoreDoc.doc, fields); - - List values = new ArrayList<>(); - - IndexableField[] minHashes = doc.getFields("MINHASH"); - for (IndexableField minHash : minHashes) - { - SchemaField sf = schema.getFieldOrNull(minHash.name()); - Object value = DocsStreamer.getValue(sf, minHash); - values.add(value); - } - - return values; - } - - return null; - } - - private Collection fetchFingerPrint(String shards, String nodeId) { - shards = shards.replace(",", "|"); - List urls = ((HttpShardHandlerFactory)shardHandlerFactory).buildURLList(shards); - ExecutorService executorService = null; - List futures = new ArrayList(); - Collection fingerPrint = null; - try { - executorService = Executors.newCachedThreadPool(); - for (String url : urls) { - futures.add(executorService.submit(new FingerPrintFetchTask(url, nodeId))); - } - - for (Future future : futures) { - Collection col = future.get(); - if(col != null) { - fingerPrint = col; - } - } - - }catch(Exception e) { - logger.error(e); - } finally { - if (executorService != null) - { - executorService.shutdown(); - } - } - return fingerPrint; - } - - - - - private class FingerPrintFetchTask implements Callable { - private String url; - private String id; - - public FingerPrintFetchTask(String url, String id) { - this.url = url; - this.id = id; - } - - public Collection call() throws Exception { - HttpSolrClient solrClient = null; - CloseableHttpClient closeableHttpClient = null; - try { - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add(FingerPrintComponent.COMPONENT_NAME, "true"); - params.add("id", id); - params.add("qt","/fingerprint"); - closeableHttpClient = HttpClientUtil.createClient(getClientParams()); - solrClient = new HttpSolrClient.Builder(url).withHttpClient(closeableHttpClient).build();; - QueryRequest request = new QueryRequest(params, SolrRequest.METHOD.POST); - QueryResponse response = request.process(solrClient); - NamedList dataResponse = response.getResponse(); - NamedList fingerprint = (NamedList) dataResponse.get("fingerprint"); - return (Collection)fingerprint.get("MINHASH"); - } finally { - if (closeableHttpClient != null) - { - closeableHttpClient.close(); - } - if (solrClient != null) - { - solrClient.close(); - } - } - } - - protected ModifiableSolrParams getClientParams() { - ModifiableSolrParams clientParams = new ModifiableSolrParams(); - clientParams.set(HttpClientUtil.PROP_SO_TIMEOUT, UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATESOTIMEOUT); - clientParams.set(HttpClientUtil.PROP_CONNECTION_TIMEOUT, UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATECONNTIMEOUT); - return clientParams; - } - } - - /** - * @param field - * @param queryText - * @param analysisMode - * - * @param luceneFunction - * @return - * @throws IOException - * @throws ParseException - */ - private Query createFingerPrintQuery(String field, String queryText, Collection values, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws IOException, ParseException - { - String[] parts = queryText.split("_"); - if (parts.length == 0) - { - return createIsNodeQuery("T"); - } - - if (values != null) - { - int bandSize = 1; - float fraction = -1; - float truePositive = 1; - if (parts.length > 1) - { - fraction = Float.parseFloat(parts[1]); - if (fraction > 1) - { - fraction /= 100; - } - } - if (parts.length > 2) - { - truePositive = Float.parseFloat(parts[2]); - if (truePositive > 1) - { - truePositive /= 100; - } - bandSize = computeBandSize(values.size(), fraction, truePositive); - } - BooleanQuery.Builder builder = new BooleanQuery.Builder(); - BooleanQuery.Builder childBuilder = new BooleanQuery.Builder(); - int rowInBand = 0; - for (Object token : values) - { - TermQuery tq = new TermQuery(new Term("MINHASH", token.toString())); - if (bandSize == 1) - { - builder.add(new ConstantScoreQuery(tq), Occur.SHOULD); - } else - { - childBuilder.add(new ConstantScoreQuery(tq), Occur.MUST); - rowInBand++; - if (rowInBand == bandSize) - { - builder.add(new ConstantScoreQuery(childBuilder.setDisableCoord(true).build()), - Occur.SHOULD); - childBuilder = new BooleanQuery.Builder(); - rowInBand = 0; - } - } - } - // Avoid a dubious narrow band .... wrap around and pad with the - // start - if (childBuilder.build().clauses().size() > 0) - { - for (Object token : values) - { - TermQuery tq = new TermQuery(new Term("MINHASH", token.toString())); - childBuilder.add(new ConstantScoreQuery(tq), Occur.MUST); - rowInBand++; - if (rowInBand == bandSize) - { - builder.add(new ConstantScoreQuery(childBuilder.setDisableCoord(true).build()), - Occur.SHOULD); - break; - } - } - } - builder.setDisableCoord(true); - if (parts.length == 2) - { - builder.setMinimumNumberShouldMatch((int) (Math.ceil(values.size() * fraction))); - } - Query q = builder.build(); - return q; - } else - { - return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); - } - } - - private int computeBandSize(int numHash, double sim, double expectedTruePositive) - { - for (int bands = 1; bands <= numHash; bands++) - { - int rowsInBand = numHash / bands; - double truePositive = 1 - Math.pow(1 - Math.pow(sim, rowsInBand), bands); - if (truePositive > expectedTruePositive) - { - return rowsInBand; - } - } - return 1; - } - - private ArrayList getTokens(IndexableField indexableField) throws IOException - { - ArrayList tokens = new ArrayList(); - - TokenStream ts = indexableField.tokenStream(schema.getIndexAnalyzer(), null); - CharTermAttribute termAttribute = ts.getAttribute(CharTermAttribute.class); - ts.reset(); - while (ts.incrementToken()) - { - String token = new String(termAttribute.buffer(), 0, termAttribute.length()); - tokens.add(token); - } - ts.end(); - ts.close(); - - return tokens; - } - - /** - * @param queryText - * @return - */ - private org.apache.lucene.search.Query createNPathQuery(String queryText) - { - return createTermQuery(FIELD_NPATH, queryText); - } - - /** - * @param queryText - * @return - */ - private Query createPNameQuery(String queryText) - { - return createTermQuery(FIELD_PNAME, queryText); - } - - /** - * @param queryText - * @return - */ - private Query createSiteQuery(String queryText) - { - if (queryText.equals("_EVERYTHING_")) - { - return createTermQuery(FIELD_ISNODE, "T"); - } - else if (queryText.equals("_ALL_SITES_")) - { - BooleanQuery.Builder invertedRepositoryQuery = new BooleanQuery.Builder(); - invertedRepositoryQuery.add(createTermQuery(FIELD_ISNODE, "T"), Occur.MUST); - invertedRepositoryQuery.add(createTermQuery(FIELD_SITE, "_REPOSITORY_"), Occur.MUST_NOT); - return invertedRepositoryQuery.build(); - } - else - { - return createTermQuery(FIELD_SITE, queryText); - } - } - - private boolean isPropertyField(String field) - { - if (field.startsWith(PROPERTY_FIELD_PREFIX)) - { - return true; - } - int index = field.lastIndexOf('@'); - if (index > -1) - { - PropertyDefinition pDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field.substring(index + 1)); - if (pDef != null) - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance() - .getIndexedFieldNamesForProperty(pDef.getName()); - for (FieldInstance instance : indexedField.getFields()) - { - if (instance.getField().equals(field)) - { - return true; - } - } - return false; - } else - { - return false; - } - } else - { - return false; - } - - } - - protected Query createTenantQuery(String queryText) throws ParseException - { - - if (queryText.length() > 0) - { - - return getFieldQueryImplWithIOExceptionWrapped(FIELD_TENANT, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - - } - else - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_TENANT, "_DEFAULT_", AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - } - - protected Query createAncestorQuery(String queryText) throws ParseException - { - return createNodeRefQuery(FIELD_ANCESTOR, queryText); - } - - /** - * @param tag - * (which will then be ISO9075 encoded) - * @return - * @throws ParseException - */ - protected Query createTagQuery(String tag) throws ParseException - { - return createTermQuery(FIELD_TAG, tag.toLowerCase()); - } - - private Query getFieldQueryImplWithIOExceptionWrapped(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException - { - try - { - return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); - } - catch (IOException e) - { - throw new ParseException("IO: " + e.getMessage()); - } - } - - /** - * @param queryText - * @return - */ - protected Query createDbidQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_DBID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createTxIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_TXID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createAclTxIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLTXID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createTxCommitTimeQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_TXCOMMITTIME, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createAclTxCommitTimeQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLTXCOMMITTIME, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createDataTypeDefinitionQuery(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc if - // required - Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), queryText, analysisMode, - luceneFunction); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } - - protected Query createIsNotNull(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (pd != null) - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - query.add(createTermQuery(FIELD_PROPERTIES, pd.getName().toString()), Occur.MUST); - query.add(createTermQuery(FIELD_NULLPROPERTIES, pd.getName().toString()), Occur.MUST_NOT); - ; - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - - Query presenceQuery = getWildcardQuery(queryText, "*"); - if (presenceQuery != null) - { - query.add(presenceQuery, Occur.MUST); - } - return query.build(); - } - } - - protected Query createIsNullQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (pd != null) - { - return createTermQuery(FIELD_NULLPROPERTIES, pd.getName().toString()); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - Query presenceQuery = getWildcardQuery(queryText, "*"); - if (presenceQuery != null) - { - query.add(createIsNodeQuery("T"), Occur.MUST); - query.add(presenceQuery, Occur.MUST_NOT); - } - return query.build(); - } - } - - protected Query createIsUnsetQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (pd != null) - { - ClassDefinition containerClass = pd.getContainerClass(); - QName container = containerClass.getName(); - String classType = containerClass.isAspect() ? FIELD_ASPECT : FIELD_TYPE; - Query typeQuery = getFieldQuery(classType, container.toString(), analysisMode, luceneFunction); - - BooleanQuery.Builder query = new BooleanQuery.Builder(); - Query presenceQuery = createTermQuery(FIELD_PROPERTIES, pd.getName().toString()); - if (presenceQuery != null) - { - query.add(typeQuery, Occur.MUST); - query.add(presenceQuery, Occur.MUST_NOT); - } - - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - - Query presenceQuery = getWildcardQuery(queryText, "*"); - if (presenceQuery != null) - { - query.add(presenceQuery, Occur.MUST_NOT); - } - return query.build(); - } - } - - protected Query createExistsQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (pd != null) - { - return createTermQuery(FIELD_PROPERTIES, pd.getName().toString()); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - - Query presenceQuery = getWildcardQuery(queryText, "*"); - if (presenceQuery != null) - { - query.add(createIsNodeQuery("T"), Occur.MUST); - query.add(presenceQuery, Occur.MUST_NOT); - } - return query.build(); - } - } - - protected Query createAllQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - // Set all = searchParameters.getAllAttributes(); - // if ((all == null) || (all.size() == 0)) - // { - // Collection contentAttributes = - // dictionaryService.getAllProperties(null); - // BooleanQuery query = new BooleanQuery(); - // for (QName qname : contentAttributes) - // { - // // The super implementation will create phrase queries etc if - // required - // Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), - // queryText, analysisMode, luceneFunction); - // if (part != null) - // { - // query.add(part, Occur.SHOULD); - // } - // else - // { - // query.add(createNoMatchQuery(), Occur.SHOULD); - // } - // } - // return query; - // } - // else - // { - // BooleanQuery query = new BooleanQuery(); - // for (String fieldName : all) - // { - // Query part = getFieldQuery(fieldName, queryText, analysisMode, - // luceneFunction); - // if (part != null) - // { - // query.add(part, Occur.SHOULD); - // } - // else - // { - // query.add(createNoMatchQuery(), Occur.SHOULD); - // } - // } - // return query; - // } - throw new UnsupportedOperationException(); - } - - protected Query createAspectQuery(String queryText, boolean exactOnly) - { - AspectDefinition target = QueryParserUtils.matchAspectDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (target == null) - { - return new TermQuery(new Term(FIELD_ASPECT, "_unknown_")); - } - - if (exactOnly) - { - QName targetQName = target.getName(); - TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, targetQName.toString())); - - return termQuery; - } - else - { - Collection subclasses = dictionaryService.getSubAspects(target.getName(), true); - - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - for (QName qname : subclasses) - { - AspectDefinition current = dictionaryService.getAspect(qname); - if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) - { - TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, qname.toString())); - if (termQuery != null) - { - booleanQuery.add(termQuery, Occur.SHOULD); - } - } - } - return booleanQuery.build(); - } - - } - - protected Query createTypeQuery(String queryText, boolean exactOnly) throws ParseException - { - TypeDefinition target = QueryParserUtils.matchTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, queryText); - if (target == null) - { - return new TermQuery(new Term(FIELD_TYPE, "_unknown_")); - } - if (exactOnly) - { - QName targetQName = target.getName(); - TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, targetQName.toString())); - return termQuery; - } - else - { - Collection subclasses = dictionaryService.getSubTypes(target.getName(), true); - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - for (QName qname : subclasses) - { - TypeDefinition current = dictionaryService.getType(qname); - if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) - { - TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, qname.toString())); - if (termQuery != null) - { - booleanQuery.add(termQuery, Occur.SHOULD); - } - } - } - return booleanQuery.build(); - } - } - - protected Query createInTxIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_INTXID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - protected Query createInAclTxIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_INACLTXID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - @SuppressWarnings("deprecation") - protected Query createTransactionQuery(String queryText) - { - return createTermQuery(FIELD_TX, queryText); - } - - protected Query createIsNodeQuery(String queryText) - { - return createTermQuery(FIELD_ISNODE, queryText); - } - - protected Query createIsContainerQuery(String queryText) - { - return createTermQuery(FIELD_ISCONTAINER, queryText); - } - - protected Query createIsRootQuery(String queryText) - { - return createTermQuery(FIELD_ISROOT, queryText); - } - - protected Query createTermQuery(String field, String queryText) - { - TermQuery termQuery = new TermQuery(new Term(field, queryText)); - return termQuery; - } - - protected Query createPrimaryParentQuery(String queryText) - { - return createNodeRefQuery(FIELD_PRIMARYPARENT, queryText); - } - - protected Query createParentQuery(String queryText) - { - return createNodeRefQuery(FIELD_PARENT, queryText); - } - - protected Query createNodeRefQuery(String field, String queryText) - { - if (tenantService.isTenantUser() && (queryText.contains(StoreRef.URI_FILLER))) - { - // assume NodeRef, since it contains StorRef URI filler - queryText = tenantService.getName(new NodeRef(queryText)).toString(); - } - return createTermQuery(field, queryText); - } - - - - /** - * - * Get generic text query - * - * - */ - protected Query createDefaultTextQuery(ThrowingFunction getQuery) throws ParseException - { - - Set text = searchParameters.getTextAttributes(); - if (text == null || text.isEmpty()) - { - text = Stream.of(AlfrescoDefaultTextFields.values()) - .map(t -> PROPERTY_FIELD_PREFIX + t.getFieldName()) - .collect(Collectors.toSet()); - } - - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : text) - { - Query part; - try { - part = getQuery.apply(fieldName); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } catch (UnsupportedOperationException e) { - logger.warn(e); - } - } - return query.build(); - - } - - - protected Query createTextQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return createDefaultTextQuery(textField -> getFieldQuery(textField, queryText, analysisMode, luceneFunction)); - } - - @SuppressWarnings("unchecked") - protected Query getFieldQueryImpl(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException, IOException - { - // make sure the field exists or return a dummy query so we have no - // error ....ACE-3231 - SchemaField schemaField = schema.getFieldOrNull(field); - boolean isNumeric = false; - if (schemaField == null) - { - return new TermQuery(new Term("_dummy_", "_miss_")); - } - else - { - isNumeric = (schemaField.getType().getNumericType() != null); - if (isNumeric) - { - //Check to see if queryText is numeric or else it will fail. - try - { - Double.valueOf(queryText); - } - catch (NumberFormatException e) - { - return new TermQuery(new Term("_dummy_", "_miss_")); - } - } - } - - // Use the analyzer to get all the tokens, and then build a TermQuery, - // PhraseQuery, or noth - - // TODO: Untokenised columns with functions require special handling - - if (luceneFunction != LuceneFunction.FIELD) - { - throw new UnsupportedOperationException( - "Field queries are not supported on lucene functions (UPPER, LOWER, etc)"); - } - - // if the incoming string already has a language identifier we strip it - // iff and addit back on again - - String localePrefix = ""; - - String toTokenise = queryText; - - if (queryText.startsWith("{")) - { - int position = queryText.indexOf("}"); - if (position > 0) - { - String language = queryText.substring(0, position + 1); - Locale locale = new Locale(queryText.substring(1, position)); - String token = queryText.substring(position + 1); - boolean found = false; - for (Locale current : Locale.getAvailableLocales()) - { - if (current.toString().equalsIgnoreCase(locale.toString())) - { - found = true; - break; - } - } - if (found) - { - localePrefix = language; - toTokenise = token; - } else - { - // toTokenise = token; - } - } - } - - String testText = toTokenise; - boolean requiresMLTokenDuplication = false; - String localeString = null; - if (isPropertyField(field) && (localePrefix.length() == 0)) - { - if ((queryText.length() > 0) && (queryText.charAt(0) == '\u0000')) - { - int position = queryText.indexOf("\u0000", 1); - testText = queryText.substring(position + 1); - requiresMLTokenDuplication = true; - localeString = queryText.substring(1, position); - - } - } - - // find the positions of any escaped * and ? and ignore them - - Set wildcardPoistions = getWildcardPositions(testText); - - TokenStream source = null; - ArrayList list = new ArrayList(); - boolean severalTokensAtSamePosition = false; - PackedTokenAttributeImpl nextToken; - int positionCount = 0; - - try - { - source = getAnalyzer().tokenStream(field, new StringReader(toTokenise)); - source.reset(); - while (source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if (source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if (source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - nextToken = new PackedTokenAttributeImpl(); - nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); - nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if (typeAtt != null) - { - nextToken.setType(typeAtt.type()); - } - if (posIncAtt != null) - { - nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - - list.add(nextToken); - if (nextToken.getPositionIncrement() != 0) - positionCount += nextToken.getPositionIncrement(); - else - severalTokensAtSamePosition = true; - } - } - finally - { - try - { - if (source != null) - { - source.close(); - } - } catch (IOException e) - { - // ignore - } - } - - // add any alpha numeric wildcards that have been missed - // Fixes most stop word and wild card issues - - for (int index = 0; index < testText.length(); index++) - { - char current = testText.charAt(index); - if (((current == '*') || (current == '?')) && wildcardPoistions.contains(index)) - { - StringBuilder pre = new StringBuilder(10); - if (index == 0) - { - // "*" and "?" at the start - - boolean found = false; - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl test = list.get(j); - if ((test.startOffset() <= 0) && (0 < test.endOffset())) - { - found = true; - break; - } - } - if (!found && (list.size() == 0)) - { - // Add new token followed by * not given by the - // tokeniser - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append("", 0, 0); - newToken.setType("ALPHANUM"); - if (requiresMLTokenDuplication) - { - Locale locale = I18NUtil.parseLocale(localeString); - @SuppressWarnings("resource") - MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, MLAnalysisMode.EXACT_LANGUAGE); - Iterator it = duplicator.buildIterator(newToken); - if (it != null) - { - int count = 0; - while (it.hasNext()) - { - list.add(it.next()); - count++; - if (count > 1) - { - severalTokensAtSamePosition = true; - } - } - } - } - // content - else - { - list.add(newToken); - } - } - } - else if (index > 0) - { - // Add * and ? back into any tokens from which it has been - // removed - - boolean tokenFound = false; - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl test = list.get(j); - if ((test.startOffset() <= index) && (index < test.endOffset())) - { - if (requiresMLTokenDuplication) - { - String termText = test.toString(); - int position = termText.indexOf("}"); - String language = termText.substring(0, position + 1); - String token = termText.substring(position + 1); - if (index >= test.startOffset() + token.length()) - { - test.setEmpty(); - test.append(language + token + current); - } - } - else - { - if (index >= test.startOffset() + test.length()) - { - test.setEmpty(); - test.append(test.toString() + current); - } - } - tokenFound = true; - break; - } - } - - if (!tokenFound) - { - for (int i = index - 1; i >= 0; i--) - { - char c = testText.charAt(i); - if (Character.isLetterOrDigit(c)) - { - boolean found = false; - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl test = list.get(j); - if ((test.startOffset() <= i) && (i < test.endOffset())) - { - found = true; - break; - } - } - if (found) - { - break; - } - else - { - pre.insert(0, c); - } - } - else - { - break; - } - } - if (pre.length() > 0) - { - // Add new token followed by * not given by the - // tokeniser - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(pre.toString()); - newToken.setOffset(index - pre.length(), index); - newToken.setType("ALPHANUM"); - if (requiresMLTokenDuplication) - { - Locale locale = I18NUtil.parseLocale(localeString); - @SuppressWarnings("resource") - MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, - MLAnalysisMode.EXACT_LANGUAGE); - Iterator it = duplicator.buildIterator(newToken); - if (it != null) - { - int count = 0; - while (it.hasNext()) - { - list.add(it.next()); - count++; - if (count > 1) - { - severalTokensAtSamePosition = true; - } - } - } - } - // content - else - { - list.add(newToken); - } - } - } - } - - StringBuilder post = new StringBuilder(10); - if (index > 0) - { - for (int i = index + 1; i < testText.length(); i++) - { - char c = testText.charAt(i); - if (Character.isLetterOrDigit(c)) - { - boolean found = false; - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl test = list.get(j); - if ((test.startOffset() <= i) && (i < test.endOffset())) - { - found = true; - break; - } - } - if (found) - { - break; - } - else - { - post.append(c); - } - } - else - { - break; - } - } - if (post.length() > 0) - { - // Add new token followed by * not given by the - // tokeniser - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(post.toString()); - newToken.setOffset(index + 1, index + 1 + post.length()); - newToken.setType("ALPHANUM"); - if (requiresMLTokenDuplication) - { - Locale locale = I18NUtil.parseLocale(localeString); - @SuppressWarnings("resource") - MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, MLAnalysisMode.EXACT_LANGUAGE); - Iterator it = duplicator.buildIterator(newToken); - if (it != null) - { - int count = 0; - while (it.hasNext()) - { - list.add(it.next()); - count++; - if (count > 1) - { - severalTokensAtSamePosition = true; - } - } - } - } - // content - else - { - list.add(newToken); - } - } - } - - } - } - - // Put in real position increments as we treat them correctly - - int curentIncrement = -1; - for (PackedTokenAttributeImpl c : list) - { - if (curentIncrement == -1) - { - curentIncrement = c.getPositionIncrement(); - } else if (c.getPositionIncrement() > 0) - { - curentIncrement = c.getPositionIncrement(); - } else - { - c.setPositionIncrement(curentIncrement); - } - } - - // Fix up position increments for in phrase isolated wildcards - - boolean lastWasWild = false; - for(int i = 0; i < list.size() -1; i++) - { - for(int j = list.get(i).endOffset() + 1; j < list.get(i + 1).startOffset() - 1; j++) - { - if(wildcardPoistions.contains(j)) - { - if(!lastWasWild) - { - list.get(i+1).setPositionIncrement(list.get(i+1).getPositionIncrement() + 1); - } - lastWasWild = true; - } - else - { - lastWasWild = false; - } - } - } - - Collections.sort(list, new Comparator() - { - - public int compare(PackedTokenAttributeImpl o1, PackedTokenAttributeImpl o2) - { - int dif = o1.startOffset() - o2.startOffset(); - return dif; - - } - }); - - // Combined * and ? based strings - should redo the tokeniser - - // Build tokens by position - - LinkedList> tokensByPosition = new LinkedList>(); - LinkedList currentList = null; - int lastStart = 0; - for (PackedTokenAttributeImpl c : list) - { - if (c.startOffset() == lastStart) - { - if (currentList == null) - { - currentList = new LinkedList(); - tokensByPosition.add(currentList); - } - currentList.add(c); - } else - { - currentList = new LinkedList(); - tokensByPosition.add(currentList); - currentList.add(c); - } - lastStart = c.startOffset(); - } - - // Build all the token sequences and see which ones get strung together - - OrderedHashSet> allTokenSequencesSet = new OrderedHashSet>(); - for (LinkedList tokensAtPosition : tokensByPosition) - { - OrderedHashSet> positionalSynonymSequencesSet = new OrderedHashSet>(); - - OrderedHashSet> newAllTokenSequencesSet = new OrderedHashSet>(); - - FOR_FIRST_TOKEN_AT_POSITION_ONLY: for (PackedTokenAttributeImpl t : tokensAtPosition) - { - PackedTokenAttributeImpl replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(t); - replace.setOffset(t.startOffset(), t.endOffset()); - replace.setType(t.type()); - replace.setPositionIncrement(t.getPositionIncrement()); - - boolean tokenFoundSequence = false; - for (LinkedList tokenSequence : allTokenSequencesSet) - { - LinkedList newEntry = new LinkedList(); - newEntry.addAll(tokenSequence); - if ((newEntry.getLast().endOffset() == replace.endOffset()) - && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) - { - if ((newEntry.getLast().startOffset() == replace.startOffset()) - && newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM)) - { - positionalSynonymSequencesSet.add(tokenSequence); - newEntry.add(replace); - tokenFoundSequence = true; - } - else if (newEntry.getLast().type().equals(CommonGramsFilter.GRAM_TYPE)) - { - if (newEntry.toString().endsWith(replace.toString())) - { - // already in the gram - positionalSynonymSequencesSet.add(tokenSequence); - tokenFoundSequence = true; - } - else - { - // need to replace the synonym in the current - // gram - tokenFoundSequence = true; - StringBuffer old = new StringBuffer(newEntry.getLast().toString()); - old.replace(replace.startOffset() - newEntry.getLast().startOffset(), - replace.endOffset() - newEntry.getLast().startOffset(), replace.toString()); - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(old.toString()); - newToken.setOffset(newEntry.getLast().startOffset(), newEntry.getLast().endOffset()); - newEntry.removeLast(); - newEntry.add(newToken); - } - } - } - else if ((newEntry.getLast().startOffset() < replace.startOffset()) - && (newEntry.getLast().endOffset() < replace.endOffset())) - { - if (newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM) - && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) - { - positionalSynonymSequencesSet.add(tokenSequence); - } - newEntry.add(replace); - tokenFoundSequence = true; - } - newAllTokenSequencesSet.add(newEntry); - } - if (false == tokenFoundSequence) - { - for (LinkedList tokenSequence : newAllTokenSequencesSet) - { - LinkedList newEntry = new LinkedList(); - newEntry.addAll(tokenSequence); - if ((newEntry.getLast().endOffset() == replace.endOffset()) - && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) - { - if ((newEntry.getLast().startOffset() == replace.startOffset()) - && newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM)) - { - positionalSynonymSequencesSet.add(tokenSequence); - newEntry.add(replace); - tokenFoundSequence = true; - } else if (newEntry.getLast().type().equals(CommonGramsFilter.GRAM_TYPE)) - { - if (newEntry.toString().endsWith(replace.toString())) - { - // already in the gram - positionalSynonymSequencesSet.add(tokenSequence); - tokenFoundSequence = true; - } else - { - // need to replace the synonym in the - // current gram - tokenFoundSequence = true; - StringBuffer old = new StringBuffer(newEntry.getLast().toString()); - old.replace(replace.startOffset() - newEntry.getLast().startOffset(), - replace.endOffset() - newEntry.getLast().startOffset(), replace.toString()); - PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); - newToken.setEmpty().append(old.toString()); - newToken.setOffset(newEntry.getLast().startOffset(), - newEntry.getLast().endOffset()); - newEntry.removeLast(); - newEntry.add(newToken); - positionalSynonymSequencesSet.add(newEntry); - } - } - } else if ((newEntry.getLast().startOffset() < replace.startOffset()) - && (newEntry.getLast().endOffset() < replace.endOffset())) - { - if (newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM) - && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) - { - positionalSynonymSequencesSet.add(tokenSequence); - newEntry.add(replace); - tokenFoundSequence = true; - } - } - } - } - if (false == tokenFoundSequence) - { - LinkedList newEntry = new LinkedList(); - newEntry.add(replace); - newAllTokenSequencesSet.add(newEntry); - } - // Limit the max number of permutations we consider - if (newAllTokenSequencesSet.size() > 64) - { - break FOR_FIRST_TOKEN_AT_POSITION_ONLY; - } - } - allTokenSequencesSet = newAllTokenSequencesSet; - allTokenSequencesSet.addAll(positionalSynonymSequencesSet); - - } - - LinkedList> allTokenSequences = new LinkedList>( - allTokenSequencesSet); - - // build the unique - - LinkedList> fixedTokenSequences = new LinkedList>(); - for (LinkedList tokenSequence : allTokenSequences) - { - LinkedList fixedTokenSequence = new LinkedList(); - fixedTokenSequences.add(fixedTokenSequence); - PackedTokenAttributeImpl replace = null; - for (PackedTokenAttributeImpl c : tokenSequence) - { - if (replace == null) - { - StringBuilder prefix = new StringBuilder(); - for (int i = c.startOffset() - 1; i >= 0; i--) - { - char test = testText.charAt(i); - if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) - { - prefix.insert(0, test); - } else - { - break; - } - } - String pre = prefix.toString(); - if (requiresMLTokenDuplication) - { - String termText = c.toString(); - int position = termText.indexOf("}"); - String language = termText.substring(0, position + 1); - String token = termText.substring(position + 1); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(language + pre + token); - replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); - replace.setType(c.type()); - replace.setPositionIncrement(c.getPositionIncrement()); - } else - { - String termText = c.toString(); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(pre + termText); - replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); - replace.setType(c.type()); - replace.setPositionIncrement(c.getPositionIncrement()); - } - } else - { - StringBuilder prefix = new StringBuilder(); - StringBuilder postfix = new StringBuilder(); - StringBuilder builder = prefix; - for (int i = c.startOffset() - 1; i >= replace.endOffset(); i--) - { - char test = testText.charAt(i); - if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) - { - builder.insert(0, test); - } else - { - builder = postfix; - postfix.setLength(0); - } - } - String pre = prefix.toString(); - String post = postfix.toString(); - - // Does it bridge? - if ((pre.length() > 0) && (replace.endOffset() + pre.length()) == c.startOffset()) - { - String termText = c.toString(); - if (requiresMLTokenDuplication) - { - int position = termText.indexOf("}"); - @SuppressWarnings("unused") - String language = termText.substring(0, position + 1); - String token = termText.substring(position + 1); - int oldPositionIncrement = replace.getPositionIncrement(); - String replaceTermText = replace.toString(); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(replaceTermText + pre + token); - replace.setOffset(replace.startOffset(), c.endOffset()); - replace.setType(replace.type()); - replace.setPositionIncrement(oldPositionIncrement); - } else - { - int oldPositionIncrement = replace.getPositionIncrement(); - String replaceTermText = replace.toString(); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(replaceTermText + pre + termText); - replace.setOffset(replace.startOffset(), c.endOffset()); - replace.setType(replace.type()); - replace.setPositionIncrement(oldPositionIncrement); - } - } else - { - String termText = c.toString(); - if (requiresMLTokenDuplication) - { - int position = termText.indexOf("}"); - String language = termText.substring(0, position + 1); - String token = termText.substring(position + 1); - String replaceTermText = replace.toString(); - PackedTokenAttributeImpl last = new PackedTokenAttributeImpl(); - last.setEmpty().append(replaceTermText + post); - last.setOffset(replace.startOffset(), replace.endOffset() + post.length()); - last.setType(replace.type()); - last.setPositionIncrement(replace.getPositionIncrement()); - fixedTokenSequence.add(last); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(language + pre + token); - replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); - replace.setType(c.type()); - replace.setPositionIncrement(c.getPositionIncrement()); - } else - { - String replaceTermText = replace.toString(); - PackedTokenAttributeImpl last = new PackedTokenAttributeImpl(); - last.setEmpty().append(replaceTermText + post); - last.setOffset(replace.startOffset(), replace.endOffset() + post.length()); - last.setType(replace.type()); - last.setPositionIncrement(replace.getPositionIncrement()); - fixedTokenSequence.add(last); - replace = new PackedTokenAttributeImpl(); - replace.setEmpty().append(pre + termText); - replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); - replace.setType(c.type()); - replace.setPositionIncrement(c.getPositionIncrement()); - } - } - } - } - // finish last - if (replace != null) - { - StringBuilder postfix = new StringBuilder(); - if ((replace.endOffset() >= 0) && (replace.endOffset() < testText.length())) - { - for (int i = replace.endOffset(); i < testText.length(); i++) - { - char test = testText.charAt(i); - if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) - { - postfix.append(test); - } else - { - break; - } - } - } - String post = postfix.toString(); - int oldPositionIncrement = replace.getPositionIncrement(); - String replaceTermText = replace.toString(); - PackedTokenAttributeImpl terminal = new PackedTokenAttributeImpl(); - terminal.setEmpty().append(replaceTermText + post); - terminal.setOffset(replace.startOffset(), replace.endOffset() + post.length()); - terminal.setType(replace.type()); - terminal.setPositionIncrement(oldPositionIncrement); - fixedTokenSequence.add(terminal); - } - } - - // rebuild fixed list - - ArrayList fixed = new ArrayList(); - for (LinkedList tokenSequence : fixedTokenSequences) - { - for (PackedTokenAttributeImpl token : tokenSequence) - { - fixed.add(token); - } - } - - // reorder by start position and increment - - Collections.sort(fixed, new Comparator() - { - - public int compare(PackedTokenAttributeImpl o1, PackedTokenAttributeImpl o2) - { - int dif = o1.startOffset() - o2.startOffset(); - if (dif != 0) - { - return dif; - } else - { - return o1.getPositionIncrement() - o2.getPositionIncrement(); - } - } - }); - - // make sure we remove any tokens we have duplicated - - @SuppressWarnings("rawtypes") - OrderedHashSet unique = new OrderedHashSet(); - unique.addAll(fixed); - fixed = new ArrayList(unique); - - list = fixed; - - // add any missing locales back to the tokens - - if (localePrefix.length() > 0) - { - for (int j = 0; j < list.size(); j++) - { - PackedTokenAttributeImpl currentToken = list.get(j); - String termText = currentToken.toString(); - currentToken.setEmpty(); - currentToken.append(localePrefix + termText); - } - } - - SchemaField sf = schema.getField(field); - - boolean isShingled = false; - @SuppressWarnings("resource") - TokenizerChain tokenizerChain = (sf.getType().getQueryAnalyzer() instanceof TokenizerChain) - ? ((TokenizerChain) sf.getType().getQueryAnalyzer()) : null; - if (tokenizerChain != null) - { - for (TokenFilterFactory factory : tokenizerChain.getTokenFilterFactories()) - { - if (factory instanceof ShingleFilterFactory) - { - isShingled = true; - break; - } - } - } - @SuppressWarnings("resource") - AlfrescoAnalyzerWrapper analyzerWrapper = (sf.getType().getQueryAnalyzer() instanceof AlfrescoAnalyzerWrapper) - ? ((AlfrescoAnalyzerWrapper) sf.getType().getQueryAnalyzer()) : null; - if (analyzerWrapper != null) - { - // assume if there are no term positions it is shingled .... - isShingled = true; - } - - boolean forceConjuncion = (rerankPhase == RerankPhase.QUERY_PHASE && !isExactTermSearch(analysisMode)); - if (list.size() == 0) - { - return null; - } - else if (list.size() == 1) - { - nextToken = list.get(0); - String termText = nextToken.toString(); - if (!isNumeric && (termText.contains("*") || termText.contains("?"))) - { - return newWildcardQuery(new Term(field, termText)); - } - else - { - return newTermQuery(new Term(field, termText)); - } - } - else - { - if (severalTokensAtSamePosition) - { - if (positionCount == 1) - { - // no phrase query: - Builder q = newBooleanQuery(); - for (int i = 0; i < list.size(); i++) - { - Query currentQuery; - nextToken = list.get(i); - String termText = nextToken.toString(); - if (termText.contains("*") || termText.contains("?")) - { - currentQuery = newWildcardQuery(new Term(field, termText)); - } else - { - currentQuery = newTermQuery(new Term(field, termText)); - } - q.add(currentQuery, BooleanClause.Occur.SHOULD); - } - return q.build(); - } - else if (forceConjuncion) - { - BooleanQuery.Builder or = new BooleanQuery.Builder(); - - for (LinkedList tokenSequence : fixedTokenSequences) - { - BooleanQuery.Builder and = new BooleanQuery.Builder(); - for (int i = 0; i < tokenSequence.size(); i++) - { - nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); - String termText = nextToken.toString(); - - Term term = new Term(field, termText); - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - and.add(wildQuery, Occur.MUST); - } else - { - TermQuery termQuery = new TermQuery(term); - and.add(termQuery, Occur.MUST); - } - } - if (and.build().clauses().size() > 0) - { - or.add(and.build(), Occur.SHOULD); - } - } - return or.build(); - } - // shingle - else if (sf.omitPositions() && isShingled) - { - - ArrayList nonContained = getNonContained(list); - Query currentQuery; - - BooleanQuery.Builder weakPhrase = new BooleanQuery.Builder(); - for (PackedTokenAttributeImpl shingleToken : nonContained) - { - String termText = shingleToken.toString(); - Term term = new Term(field, termText); - - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - currentQuery = new org.apache.lucene.search.WildcardQuery(term); - } else - { - currentQuery = new TermQuery(term); - } - weakPhrase.add(currentQuery, Occur.MUST); - } - - return weakPhrase.build(); - - } - // Word delimiter factory and other odd things generate complex - // token patterns - // Smart skip token sequences with small tokens that generate - // toomany wildcards - // Fall back to the larger pattern - // e.g Site1* will not do (S ite 1*) or (Site 1*) if 1* matches - // too much (S ite1*) and (Site1*) will still be OK - // If we skip all (for just 1* in the input) this is still an - // issue. - else - { - - return generateSpanOrQuery(field, fixedTokenSequences); - - } - } - else - { - if (forceConjuncion) - { - BooleanQuery.Builder or = new BooleanQuery.Builder(); - - for (LinkedList tokenSequence : fixedTokenSequences) - { - BooleanQuery.Builder and = new BooleanQuery.Builder(); - for (int i = 0; i < tokenSequence.size(); i++) - { - nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); - String termText = nextToken.toString(); - - Term term = new Term(field, termText); - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - and.add(wildQuery, Occur.MUST); - } - else - { - TermQuery termQuery = new TermQuery(term); - and.add(termQuery, Occur.MUST); - } - } - if (and.build().clauses().size() > 0) - { - or.add(and.build(), Occur.SHOULD); - } - } - return or.build(); - } - else - { - SpanQuery spanQuery = null; - ArrayList atSamePositionSpanOrQueryParts = new ArrayList(); - int gap = 0; - for (int i = 0; i < list.size(); i++) - { - nextToken = list.get(i); - String termText = nextToken.toString(); - Term term = new Term(field, termText); - if (getEnablePositionIncrements()) - { - SpanQuery nextSpanQuery; - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( - wildQuery); - wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); - nextSpanQuery = wrapper; - } - else - { - nextSpanQuery = new SpanTermQuery(term); - } - if (gap == 0) - { - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } - else - { - if (atSamePositionSpanOrQueryParts.size() == 0) - { - if (spanQuery == null) - { - spanQuery = nextSpanQuery; - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, nextSpanQuery }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - } - else if (atSamePositionSpanOrQueryParts.size() == 1) - { - if (spanQuery == null) - { - spanQuery = atSamePositionSpanOrQueryParts.get(0); - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, - internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } - else - { - if (spanQuery == null) - { - spanQuery = new SpanOrQuery( - atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})); - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, - new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})) }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } - } - gap = nextToken.getPositionIncrement(); - } - else - { - SpanQuery nextSpanQuery; - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( - wildQuery); - wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); - nextSpanQuery = wrapper; - } - else - { - nextSpanQuery = new SpanTermQuery(term); - } - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(nextSpanQuery); - } - else - { - spanQuery = new SpanOrQuery(spanQuery, nextSpanQuery); - } - } - } - if (atSamePositionSpanOrQueryParts.size() == 0) - { - return spanQuery; - } - else if (atSamePositionSpanOrQueryParts.size() == 1) - { - if (spanQuery == null) - { - spanQuery = atSamePositionSpanOrQueryParts.get(0); - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, - internalSlop < 2); - } - return spanQuery; - } - else - { - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})); - } - else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})) }, (gap - 1) + internalSlop, internalSlop < 2); - } - return spanQuery; - } - } - } - } - } - - /** - * @param list - * @return - */ - private ArrayList getNonContained(ArrayList list) - { - ArrayList nonContained = new ArrayList(); - NEXT_CANDIDATE: for (PackedTokenAttributeImpl candidate : list) - { - NEXT_TEST: for (PackedTokenAttributeImpl test : list) - { - if (candidate == test) - { - continue NEXT_TEST; - } else if ((test.startOffset() == candidate.startOffset()) - && (candidate.endOffset() == test.endOffset()) - && (test.toString().equals(candidate.toString()))) - { - continue NEXT_TEST; - } else if ((test.startOffset() <= candidate.startOffset()) - && (candidate.endOffset() <= test.endOffset()) - && (test.toString().contains(candidate.toString()))) - { - continue NEXT_CANDIDATE; - } - } - nonContained.add(candidate); - } - return nonContained; - } - - /** - * @param field - * @param fixedTokenSequences - * LinkedList> - * @return Query - */ - protected SpanQuery generateSpanOrQuery(String field, - LinkedList> fixedTokenSequences) - { - PackedTokenAttributeImpl nextToken; - ArrayList spanOrQueryParts = new ArrayList(); - - for (LinkedList tokenSequence : fixedTokenSequences) - { - int gap = 1; - SpanQuery spanQuery = null; - ArrayList atSamePositionSpanOrQueryParts = new ArrayList(); - - // MNT-13239: if all tokens's positions are incremented by one then - // create flat nearQuery - if (getEnablePositionIncrements() && isAllTokensSequentiallyShifted(tokenSequence)) - { - // there will be no tokens at same position - List wildWrappedList = new ArrayList(tokenSequence.size()); - for (PackedTokenAttributeImpl token : tokenSequence) - { - String termText = token.toString(); - Term term = new Term(field, termText); - SpanQuery nextSpanQuery = wrapWildcardTerms(term); - wildWrappedList.add(nextSpanQuery); - } - if (wildWrappedList.size() == 1) - { - spanQuery = wildWrappedList.get(0); - } else - { - spanQuery = new SpanNearQuery(wildWrappedList.toArray(new SpanQuery[wildWrappedList.size()]), 0, - true); - } - } else - { - for (int i = 0; i < tokenSequence.size(); i++) - { - nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); - String termText = nextToken.toString(); - - Term term = new Term(field, termText); - - if (getEnablePositionIncrements()) - { - SpanQuery nextSpanQuery = wrapWildcardTerms(term); - if (gap == 0) - { - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } else - { - if (atSamePositionSpanOrQueryParts.size() == 0) - { - if (spanQuery == null) - { - spanQuery = nextSpanQuery; - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, nextSpanQuery }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - } else if (atSamePositionSpanOrQueryParts.size() == 1) - { - if (spanQuery == null) - { - spanQuery = atSamePositionSpanOrQueryParts.get(0); - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, - internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } else - { - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})); - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, spanQuery = new SpanOrQuery( - atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})) }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - atSamePositionSpanOrQueryParts.add(nextSpanQuery); - } - } - gap = nextToken.getPositionIncrement(); - - } else - { - SpanQuery nextSpanQuery; - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( - term); - SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( - wildQuery); - wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); - nextSpanQuery = wrapper; - } else - { - nextSpanQuery = new SpanTermQuery(term); - } - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(nextSpanQuery); - } else - { - spanQuery = new SpanOrQuery(spanQuery, nextSpanQuery); - } - } - } - } - - if (atSamePositionSpanOrQueryParts.size() == 0) - { - spanOrQueryParts.add(spanQuery); - } else if (atSamePositionSpanOrQueryParts.size() == 1) - { - if (spanQuery == null) - { - spanQuery = atSamePositionSpanOrQueryParts.get(0); - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - spanOrQueryParts.add(spanQuery); - } else - { - if (spanQuery == null) - { - spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})); - } else - { - spanQuery = new SpanNearQuery(new SpanQuery[] - { spanQuery, new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] - {})) }, (gap - 1) + internalSlop, internalSlop < 2); - } - atSamePositionSpanOrQueryParts = new ArrayList(); - spanOrQueryParts.add(spanQuery); - } - } - if (spanOrQueryParts.size() == 1) - { - return spanOrQueryParts.get(0); - } else - { - return new SpanOrQuery(spanOrQueryParts.toArray(new SpanQuery[] - {})); - } - } - - private SpanQuery wrapWildcardTerms(org.apache.lucene.index.Term term) - { - String termText = term.text(); - SpanQuery nextSpanQuery; - if ((termText != null) && (termText.contains("*") || termText.contains("?"))) - { - org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery(term); - SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( - wildQuery); - wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); - nextSpanQuery = wrapper; - } else - { - nextSpanQuery = new SpanTermQuery(term); - } - return nextSpanQuery; - } - - protected boolean isAllTokensSequentiallyShifted(List tokenSequence) - { - if (0 == internalSlop) - { - // check if all tokens have gap = 1 - for (PackedTokenAttributeImpl tokenToCheck : tokenSequence) - { - if (1 != tokenToCheck.getPositionIncrement()) - { - return false; - } - } - return true; - } - return false; - } - - private Set getWildcardPositions(String string) - { - HashSet wildcardPositions = new HashSet(); - - boolean lastWasEscape = false; - - for (int i = 0; i < string.length(); i++) - { - char c = string.charAt(i); - if (lastWasEscape) - { - lastWasEscape = false; - } else - { - if (c == '\\') - { - lastWasEscape = true; - } else if (c == '*') - { - wildcardPositions.add(i); - } else if (c == '?') - { - wildcardPositions.add(i); - } - } - } - - return wildcardPositions; - } - - /** - * @exception ParseException - * throw in overridden method to disallow - */ - - protected Query getRangeQuery(String field, String part1, String part2, boolean startInclusive, - boolean endInclusive) throws ParseException - { - return getRangeQuery(field, part1, part2, startInclusive, endInclusive, AnalysisMode.IDENTIFIER, - LuceneFunction.FIELD); - } - - protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException - { - return getRangeQuery(field, part1, part2, inclusive, inclusive, AnalysisMode.IDENTIFIER, LuceneFunction.FIELD); - } - - /** - * @param field - * @param part1 - * @param part2 - * @param includeLower - * @param includeUpper - * @param analysisMode - * @param luceneFunction - * @return the query - * @exception ParseException - * throw in overridden method to disallow - */ - public Query getRangeQuery(String field, String part1, String part2, boolean includeLower, boolean includeUpper, - AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATH); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - - return createDefaultTextQuery(textField -> getRangeQuery(textField, part1, part2, includeLower, - includeUpper, analysisMode, luceneFunction)); - - - } else if (field.equals(FIELD_CASCADETX)) - { - SchemaField sf = schema.getField(FIELD_CASCADETX); - if (sf != null) - { - String start = null; - try - { - analyzeMultitermTerm(FIELD_CASCADETX, part1); - start = part1; - } catch (Exception e) - { - - } - String end = null; - try - { - analyzeMultitermTerm(FIELD_CASCADETX, part2); - end = part2; - } catch (Exception e) - { - - } - return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); - } else - { - throw new UnsupportedOperationException(); - } - } - // FIELD_ID uses the default - // FIELD_DBID uses the default - // FIELD_ISROOT uses the default - // FIELD_ISCONTAINER uses the default - // FIELD_ISNODE uses the default - // FIELD_TX uses the default - // FIELD_PARENT uses the default - // FIELD_PRIMARYPARENT uses the default - // FIELD_QNAME uses the default - // FIELD_PRIMARYASSOCTYPEQNAME uses the default - // FIELD_ASSOCTYPEQNAME uses the default - // FIELD_CLASS uses the default - // FIELD_TYPE uses the default - // FIELD_EXACTTYPE uses the default - // FIELD_ASPECT uses the default - // FIELD_EXACTASPECT uses the default - // FIELD_TYPE uses the default - // FIELD_TYPE uses the default - if (isPropertyField(field)) - { - Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(field); - - String expandedFieldName = null; - PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, fieldNameAndEnding.getFirst()); - IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; - if (propertyDef != null) - { - tokenisationMode = propertyDef.getIndexTokenisationMode(); - if (tokenisationMode == null) - { - tokenisationMode = IndexTokenisationMode.TRUE; - } - } else - { - expandedFieldName = expandAttributeFieldName(field); - } - - if (propertyDef != null) - { - // LOWER AND UPPER - if (luceneFunction != LuceneFunction.FIELD) - { - if (luceneFunction == LuceneFunction.LOWER) - { - if ((false == part1.toLowerCase().equals(part1)) - || (false == part2.toLowerCase().equals(part2))) - { - return createNoMatchQuery(); - } - } - - if (luceneFunction == LuceneFunction.UPPER) - { - if ((false == part1.toUpperCase().equals(part1)) - || (false == part2.toUpperCase().equals(part2))) - { - return createNoMatchQuery(); - } - } - - if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - addLocaleSpecificUntokenisedTextRangeFunction(expandedFieldName, propertyDef, part1, part2, - includeLower, includeUpper, luceneFunction, booleanQuery, locale, tokenisationMode); - } - return booleanQuery.build(); - } else - { - throw new UnsupportedOperationException("Lucene Function"); - } - } - - if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) - { - return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, analysisMode, - expandedFieldName, propertyDef, tokenisationMode); - } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) - { - String solrField = null; - switch (fieldNameAndEnding.getSecond()) - { - case FIELD_SIZE_SUFFIX: - solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_SIZE, FieldUse.ID) - .getFields().get(0).getField(); - break; - case FIELD_MIMETYPE_SUFFIX: - solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_MIMETYPE, FieldUse.ID) - .getFields().get(0).getField(); - break; - case FIELD_ENCODING_SUFFIX: - solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_ENCODING, FieldUse.ID) - .getFields().get(0).getField(); - break; - case FIELD_LOCALE_SUFFIX: - solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_LOCALE, FieldUse.ID) - .getFields().get(0).getField(); - break; - } - if (solrField != null) - { - String start = null; - try - { - analyzeMultitermTerm(solrField, part1); - start = part1; - } catch (Exception e) - { - - } - String end = null; - try - { - analyzeMultitermTerm(solrField, part2); - end = part2; - } catch (Exception e) - { - - } - - SchemaField sf = schema.getField(solrField); - return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); - } else - { - return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, - analysisMode, expandedFieldName, propertyDef, tokenisationMode); - } - } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) - { - return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, analysisMode, - expandedFieldName, propertyDef, tokenisationMode); - } else if ((propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) - || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE)) && - !AlfrescoSolrDataModel.getInstance().isDerivedDateField(fieldNameAndEnding.getSecond())) - { - Pair dateAndResolution1 = parseDateString(part1); - Pair dateAndResolution2 = parseDateString(part2); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - IndexedField indexedField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), null, FieldUse.ID); - for (FieldInstance instance : indexedField.getFields()) - { - String start = dateAndResolution1 == null ? part1 - : (includeLower ? getDateStart(dateAndResolution1) : getDateEnd(dateAndResolution1)); - String end = dateAndResolution2 == null ? part2 - : (includeUpper ? getDateEnd(dateAndResolution2) : getDateStart(dateAndResolution2)); - if (start.equals("*")) - { - start = null; - } - if (end.equals("*")) - { - end = null; - } - - SchemaField sf = schema.getField(instance.getField()); - - Query query = sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); - if (query != null) - { - bQuery.add(query, Occur.SHOULD); - } - } - return bQuery.build(); - } - else - { - String solrField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), - AlfrescoSolrDataModel.getInstance().getTextField(fieldNameAndEnding.getSecond()), - FieldUse.ID) - .getFields().get(0).getField(); - - String start = null; - try - { - analyzeMultitermTerm(solrField, part1); - start = part1; - } catch (Exception e) - { - - } - String end = null; - try - { - analyzeMultitermTerm(solrField, part2); - end = part2; - } catch (Exception e) - { - - } - - SchemaField sf = schema.getField(solrField); - return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); - } - } else - { - throw new UnsupportedOperationException(); - } - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, - includeUpper, analysisMode, luceneFunction); - query.add(part, Occur.SHOULD); - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getRangeQuery(fieldName, part1, part2, includeLower, includeUpper, analysisMode, - luceneFunction); - query.add(part, Occur.SHOULD); - } - return query.build(); - } - - } - // FIELD_ISUNSET uses the default - // FIELD_ISNULL uses the default - // FIELD_ISNOTNULL uses the default - // FIELD_EXISTS uses the default - else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, - includeUpper, analysisMode, luceneFunction); - query.add(part, Occur.SHOULD); - } - return query.build(); - } - // FIELD_FTSSTATUS uses the default - if (field.equals(FIELD_TAG)) - { - throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_TAG); - } else - { - // None property - leave alone - throw new UnsupportedOperationException(); - } - } - - /** - * @param field - * @param part1 - * @param part2 - * @param includeLower - * @param includeUpper - * @param analysisMode - * @param expandedFieldName - * @param propertyDef - * @param tokenisationMode - * @return - * @throws ParseException - */ - private Query buildTextMLTextOrContentRange(String field, String part1, String part2, boolean includeLower, - boolean includeUpper, AnalysisMode analysisMode, String expandedFieldName, PropertyDefinition propertyDef, - IndexTokenisationMode tokenisationMode) throws ParseException - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - - try - { - addTextRange(field, propertyDef, part1, part2, includeLower, includeUpper, analysisMode, - expandedFieldName, propertyDef, tokenisationMode, booleanQuery, locale); - } catch (IOException e) - { - throw new ParseException( - "Failed to tokenise: <" + part1 + "> or <" + part2 + "> for " + propertyDef.getName()); - } - - } - return booleanQuery.build(); - } - - private String expandAttributeFieldName(String field) - { - return PROPERTY_FIELD_PREFIX + QueryParserUtils.expandQName(searchParameters.getNamespace(), - namespacePrefixResolver, field.substring(1)); - } - - // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) - @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") - protected String getToken(String field, String value, AnalysisMode analysisMode) throws ParseException - { - try (TokenStream source = getAnalyzer().tokenStream(field, new StringReader(value))) - { - String tokenised = null; - - while (source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if (source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if (source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - PackedTokenAttributeImpl token = new PackedTokenAttributeImpl(); - token.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); - token.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if (typeAtt != null) - { - token.setType(typeAtt.type()); - } - if (posIncAtt != null) - { - token.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - - tokenised = token.toString(); - } - return tokenised; - } catch (IOException e) - { - throw new ParseException("IO" + e.getMessage()); - } - - } - - @Override - public Query getPrefixQuery(String field, String termStr) throws ParseException - { - return getPrefixQuery(field, termStr, AnalysisMode.PREFIX); - } - - @SuppressWarnings("deprecation") - public Query getPrefixQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATH); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - - return createDefaultTextQuery(textField -> getPrefixQuery(textField, termStr, analysisMode)); - - } else if (field.equals(FIELD_ID)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getPrefixQuery(FIELD_LID, termStr); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) - || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) - || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) - || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getPrefixQuery(field, termStr); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_CLASS)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_CLASS); - } else if (field.equals(FIELD_TYPE)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_TYPE); - } else if (field.equals(FIELD_EXACTTYPE)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_EXACTTYPE); - } else if (field.equals(FIELD_ASPECT)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_ASPECT); - } else if (field.equals(FIELD_EXACTASPECT)) - { - return super.getPrefixQuery(field, termStr); - // throw new UnsupportedOperationException("Prefix Queries are not - // support for "+FIELD_EXACTASPECT); - } else if (isPropertyField(field)) - { - return attributeQueryBuilder(field, termStr, new PrefixQuery(), analysisMode, LuceneFunction.FIELD); - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc - // if required - Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getPrefixQuery(fieldName, termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } - } else if (field.equals(FIELD_ISUNSET)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISUNSET); - } else if (field.equals(FIELD_ISNULL)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNULL); - } else if (field.equals(FIELD_ISNOTNULL)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNOTNULL); - } else if (field.equals(FIELD_EXISTS)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_EXISTS); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc if - // required - Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else if (field.equals(FIELD_FTSSTATUS)) - { - throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_FTSSTATUS); - } else if (field.equals(FIELD_TAG)) - { - return super.getPrefixQuery(field, termStr); - } else if (field.equals(FIELD_SITE)) - { - return super.getPrefixQuery(field, termStr); - } else if (field.equals(FIELD_NPATH)) - { - return super.getPrefixQuery(field, termStr); - } else if (field.equals(FIELD_PNAME)) - { - return super.getPrefixQuery(field, termStr); - } else - { - return super.getPrefixQuery(field, termStr); - } - } - - @Override - public Query getWildcardQuery(String field, String termStr) throws ParseException - { - return getWildcardQuery(field, termStr, AnalysisMode.WILD); - } - - @SuppressWarnings("deprecation") - public Query getWildcardQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_PATH); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - - return createDefaultTextQuery(textField -> getWildcardQuery(textField, termStr, analysisMode)); - - } else if (field.equals(FIELD_ID)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getWildcardQuery(FIELD_LID, termStr); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) - || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) - || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) - || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getWildcardQuery(field, termStr); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_CLASS)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_CLASS); - } else if (field.equals(FIELD_TYPE)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_TYPE); - } else if (field.equals(FIELD_EXACTTYPE)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_EXACTTYPE); - } else if (field.equals(FIELD_ASPECT)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_ASPECT); - } else if (field.equals(FIELD_EXACTASPECT)) - { - return super.getWildcardQuery(field, termStr); - // throw new UnsupportedOperationException("Wildcard Queries are not - // support for "+FIELD_EXACTASPECT); - } else if (isPropertyField(field)) - { - return attributeQueryBuilder(field, termStr, new WildcardQuery(), analysisMode, LuceneFunction.FIELD); - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc - // if required - Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getWildcardQuery(fieldName, termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } - } else if (field.equals(FIELD_ISUNSET)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISUNSET); - } else if (field.equals(FIELD_ISNULL)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNULL); - } else if (field.equals(FIELD_ISNOTNULL)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNOTNULL); - } else if (field.equals(FIELD_EXISTS)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_EXISTS); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc if - // required - Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else if (field.equals(FIELD_FTSSTATUS)) - { - throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_FTSSTATUS); - } else if (field.equals(FIELD_TAG)) - { - return super.getWildcardQuery(field, termStr); - } else if (field.equals(FIELD_SITE)) - { - return super.getWildcardQuery(field, termStr); - } else if (field.equals(FIELD_PNAME)) - { - return super.getWildcardQuery(field, termStr); - } else if (field.equals(FIELD_NPATH)) - { - return super.getWildcardQuery(field, termStr); - } else - { - return super.getWildcardQuery(field, termStr); - } - } - - @SuppressWarnings("deprecation") - @Override - public Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException - { - if (field.equals(FIELD_PATH)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATH); - } else if (field.equals(FIELD_PATHWITHREPEATS)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATHWITHREPEATS); - } else if (field.equals(FIELD_TEXT)) - { - - return createDefaultTextQuery(textField -> getFuzzyQuery(textField, termStr, minSimilarity)); - - } else if (field.equals(FIELD_ID) || field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) - || field.equals(FIELD_ISCONTAINER) || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) - || field.equals(FIELD_PARENT) || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) - || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) - { - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - setLowercaseExpandedTerms(false); - return super.getFuzzyQuery(field, termStr, minSimilarity); - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - } else if (field.equals(FIELD_CLASS)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_CLASS); - } else if (field.equals(FIELD_TYPE)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_TYPE); - } else if (field.equals(FIELD_EXACTTYPE)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTTYPE); - } else if (field.equals(FIELD_ASPECT)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ASPECT); - } else if (field.equals(FIELD_EXACTASPECT)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTASPECT); - } else if (isPropertyField(field)) - { - return attributeQueryBuilder(field, termStr, new FuzzyQuery(minSimilarity), AnalysisMode.FUZZY, - LuceneFunction.FIELD); - } else if (field.equals(FIELD_ALL)) - { - Set all = searchParameters.getAllAttributes(); - if ((all == null) || (all.size() == 0)) - { - Collection contentAttributes = dictionaryService.getAllProperties(null); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc - // if required - Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else - { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (String fieldName : all) - { - Query part = getFuzzyQuery(fieldName, termStr, minSimilarity); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } - } else if (field.equals(FIELD_ISUNSET)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISUNSET); - } else if (field.equals(FIELD_ISNULL)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNULL); - } else if (field.equals(FIELD_ISNOTNULL)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNOTNULL); - } else if (field.equals(FIELD_EXISTS)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXISTS); - } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, - dictionaryService, field) != null) - { - Collection contentAttributes = dictionaryService - .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, field).getName()); - BooleanQuery.Builder query = new BooleanQuery.Builder(); - for (QName qname : contentAttributes) - { - // The super implementation will create phrase queries etc if - // required - Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); - if (part != null) - { - query.add(part, Occur.SHOULD); - } else - { - query.add(createNoMatchQuery(), Occur.SHOULD); - } - } - return query.build(); - } else if (field.equals(FIELD_FTSSTATUS)) - { - throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_FTSSTATUS); - } else if (field.equals(FIELD_TAG)) - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } else if (field.equals(FIELD_SITE)) - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } else if (field.equals(FIELD_PNAME)) - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } else if (field.equals(FIELD_NPATH)) - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } else - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } - } - - /** - * @param dictionaryService - */ - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - /** - * @param field - * @param queryText - * @param analysisMode - * @param luceneFunction - * @return the query - * @throws ParseException - */ - public Query getSuperFieldQuery(String field, String queryText, AnalysisMode analysisMode, - LuceneFunction luceneFunction) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(field, queryText, analysisMode, luceneFunction); - } - - /** - * @param field - * @param termStr - * @param minSimilarity - * @return the query - * @throws ParseException - */ - public Query getSuperFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException - { - return super.getFuzzyQuery(field, termStr, minSimilarity); - } - - /** - * @param field - * @param termStr - * @return the query - * @throws ParseException - */ - public Query getSuperPrefixQuery(String field, String termStr) throws ParseException - { - return super.getPrefixQuery(field, termStr); - } - - /** - * @param field - * @param termStr - * @return the query - * @throws ParseException - */ - public Query getSuperWildcardQuery(String field, String termStr) throws ParseException - { - return super.getWildcardQuery(field, termStr); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.lucene.queryParser.QueryParser#newWildcardQuery(org.apache. - * lucene.index.Term) - */ - @Override - protected Query newWildcardQuery(Term t) - { - if (t.text().equals("*")) - { - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - bQuery.add(createTermQuery(FIELD_FIELDS, t.field()), Occur.SHOULD); - bQuery.add(createTermQuery(FIELD_PROPERTIES, t.field()), Occur.SHOULD); - return bQuery.build(); - } else if (t.text().contains("\\")) - { - String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, - SearchLanguageConversion.DEF_REGEX, t.text()); - return new RegexpQuery(new Term(t.field(), regexp)); - } else - { - org.apache.lucene.search.WildcardQuery query = new org.apache.lucene.search.WildcardQuery(t); - query.setRewriteMethod(new MultiTermQuery.TopTermsScoringBooleanQueryRewrite(topTermSpanRewriteLimit)); - return query; - } - } - - /* - * (non-Javadoc) - * - * @see org.apache.lucene.queryParser.QueryParser#newPrefixQuery(org.apache. - * lucene.index.Term) - */ - @Override - protected Query newPrefixQuery(Term prefix) - { - if (prefix.text().contains("\\")) - { - String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, - SearchLanguageConversion.DEF_REGEX, prefix.text()); - return new RegexpQuery(new Term(prefix.field(), regexp)); - } else - { - return super.newPrefixQuery(prefix); - } - - } - - public interface SubQuery - { - /** - * @param field - * @param queryText - * @param analysisMode - * @param luceneFunction - * @return the query - * @throws ParseException - */ - Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException; - } - - class FieldQuery implements SubQuery - { - public Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return getSuperFieldQuery(field, queryText, analysisMode, luceneFunction); - } - } - - class FuzzyQuery implements SubQuery - { - float minSimilarity; - - FuzzyQuery(float minSimilarity) - { - this.minSimilarity = minSimilarity; - } - - public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return getSuperFuzzyQuery(field, translateLocale(termStr), minSimilarity); - } - } - - private String translateLocale(String localised) - { - if (localised.startsWith("\u0000")) - { - if (localised.startsWith("\u0000\u0000")) - { - if (localised.length() < 3) - { - return ""; - } else - { - return localised.substring(2); - } - } else - { - int end = localised.indexOf('\u0000', 1); - if (end == -1) - { - return localised; - } else - { - StringBuilder buffer = new StringBuilder(); - buffer.append("{"); - buffer.append(localised.substring(1, end)); - buffer.append("}"); - buffer.append(localised.substring(end + 1)); - return buffer.toString(); - } - } - } else - { - return localised; - } - } - - class PrefixQuery implements SubQuery - { - public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - StringBuilder builder = new StringBuilder(termStr.length() + 1); - builder.append(termStr); - builder.append("*"); - return getSuperFieldQuery(field, builder.toString(), analysisMode, luceneFunction); - // return getSuperPrefixQuery(field, termStr); - } - } - - class WildcardQuery implements SubQuery - { - public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) - throws ParseException - { - return getSuperFieldQuery(field, termStr, analysisMode, luceneFunction); - // return getSuperWildcardQuery(field, termStr); - } - } - - private Query spanQueryBuilder(String field, String first, String last, int slop, boolean inOrder) - throws ParseException - { - String propertyFieldName = field.substring(1); - String expandedFieldName = null; - - PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, propertyFieldName); - IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; - if (propertyDef != null) - { - tokenisationMode = propertyDef.getIndexTokenisationMode(); - if (tokenisationMode == null) - { - tokenisationMode = IndexTokenisationMode.TRUE; - } - } else - { - expandedFieldName = expandAttributeFieldName(field); - } - - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) - { - // Build a sub query for each locale and or the results together - - // the analysis will take care of - // cross language matching for each entry - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - addMLTextSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, propertyDef, - tokenisationMode, booleanQuery, locale); - } - return booleanQuery.build(); - } - // Content - else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales - .addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); - } - - return addContentSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, - expandedLocales); - - } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - - addTextSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, - booleanQuery, locale); - - } - return booleanQuery.build(); - } else - { - throw new UnsupportedOperationException( - "Span queries are only supported for d:text, d:mltext and d:content data types"); - } - } - - private Query attributeQueryBuilder(String field, String queryText, SubQuery subQueryBuilder, - AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException - { - // TODO: Fix duplicate token generation for mltext, content and text. - // -locale expansion here and in tokeisation -> duplicates - - // Get type info etc - - // TODO: additional suffixes - - Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(field); - - String expandedFieldName = null; - QName propertyQName; - PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), - namespacePrefixResolver, dictionaryService, fieldNameAndEnding.getFirst()); - IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; - if (propertyDef != null) - { - tokenisationMode = propertyDef.getIndexTokenisationMode(); - if (tokenisationMode == null) - { - tokenisationMode = IndexTokenisationMode.TRUE; - } - propertyQName = propertyDef.getName(); - } else - { - expandedFieldName = expandAttributeFieldName(field); - propertyQName = QName.createQName(fieldNameAndEnding.getFirst()); - } - - if (isAllStar(queryText)) - { - return createTermQuery(FIELD_PROPERTIES, propertyQName.toString()); - } - - if (luceneFunction != LuceneFunction.FIELD) - { - if ((tokenisationMode == IndexTokenisationMode.FALSE) || (tokenisationMode == IndexTokenisationMode.BOTH)) - { - if (luceneFunction == LuceneFunction.LOWER) - { - if (false == queryText.toLowerCase().equals(queryText)) - { - return createNoMatchQuery(); - } - } - if (luceneFunction == LuceneFunction.UPPER) - { - if (false == queryText.toUpperCase().equals(queryText)) - { - return createNoMatchQuery(); - } - } - - return functionQueryBuilder(expandedFieldName, fieldNameAndEnding.getSecond(), propertyQName, - propertyDef, tokenisationMode, queryText, luceneFunction); - } - } - - // Mime type - if (fieldNameAndEnding.getSecond().equals(FIELD_MIMETYPE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_MIMETYPE, FieldUse.ID).getFields().get(0) - .getField(), queryText, analysisMode, luceneFunction); - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_SIZE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_SIZE, FieldUse.ID).getFields().get(0) - .getField(), queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_LOCALE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_LOCALE, FieldUse.ID).getFields().get(0) - .getField(), queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_ENCODING_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_ENCODING, FieldUse.ID).getFields().get(0) - .getField(), queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_STATUS_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder - .getQuery( - AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_STATUS, - FieldUse.ID) - .getFields().get(0).getField(), - queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_TIME_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder - .getQuery( - AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_TIME, - FieldUse.ID) - .getFields().get(0).getField(), - queryText, analysisMode, luceneFunction); - - } - - } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - return subQueryBuilder.getQuery( - AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_EXCEPTION, - FieldUse.ID) - .getFields().get(0).getField(), - queryText, analysisMode, luceneFunction); - - } - - } - else if (AlfrescoSolrDataModel.getInstance().isDerivedDateField(fieldNameAndEnding.getSecond())) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE) - || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME ))) - { - return subQueryBuilder.getQuery( - AlfrescoSolrDataModel.getInstance() - .getDateDerivedField(propertyQName, fieldNameAndEnding.getSecond()), - queryText, analysisMode, luceneFunction); - } - } - - // Already in expanded form - - // ML - - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) - { - // Build a sub query for each locale and or the results together - - // the analysis will take care of - // cross language matching for each entry - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - addMLTextAttributeQuery(field, propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, propertyDef, tokenisationMode, booleanQuery, locale); - } - return getNonEmptyBooleanQuery(booleanQuery.build()); - } - // Content - else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - // Identifier request are ignored for content - - // Build a sub query for each locale and or the results together - - // - add an explicit condition for the locale - - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales - .addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); - } - - return addContentAttributeQuery(propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, expandedLocales); - - } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) - { - // if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) || - // propertyQName.equals(ContentModel.PROP_USERNAME) || - // propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) - // { - // // nasty work around for solr support for user and group look up - // as we can not support lowercased identifiers in the model - // if(isLucene()) - // { - // return subQueryBuilder.getQuery(expandedFieldName, queryText, - // analysisMode, luceneFunction); - // } - // } - - boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) - || propertyQName.equals(ContentModel.PROP_USERNAME) - || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); - - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - Locale fixedLocale = locale; - if (fixedLocale.getLanguage().equals("*")) - { - fixedLocale = new Locale("??"); - } - addTextAttributeQuery(field, propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, tokenisationMode, booleanQuery, fixedLocale); - - } - return getNonEmptyBooleanQuery(booleanQuery.build()); - } else - { - // Date does not support like - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME))) - { - if (analysisMode == AnalysisMode.LIKE) - { - throw new UnsupportedOperationException("Wild cards are not supported for the datetime type"); - } - } - - // expand date for loose date parsing - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) - || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE))) - { - Pair dateAndResolution = parseDateString(queryText); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - IndexedField indexedField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), null, FieldUse.FTS); - for (FieldInstance instance : indexedField.getFields()) - { - if (dateAndResolution != null) - { - Query query = newRangeQuery(instance.getField(), getDateStart(dateAndResolution), - getDateEnd(dateAndResolution), true, true); - if (query != null) - { - bQuery.add(query, Occur.SHOULD); - } - } else - { - Query query = subQueryBuilder.getQuery(instance.getField(), queryText, AnalysisMode.DEFAULT, - luceneFunction); - if (query != null) - { - bQuery.add(query, Occur.SHOULD); - } - } - } - if (bQuery.build().clauses().size() > 0) - { - return bQuery.build(); - } else - { - return createNoMatchQuery(); - } - } - - if ((propertyDef != null) && (tenantService.isTenantUser()) - && (propertyDef.getDataType().getName().equals(DataTypeDefinition.NODE_REF)) - && (queryText.contains(StoreRef.URI_FILLER))) - { - // ALF-6202 - queryText = tenantService.getName(new NodeRef(queryText)).toString(); - } - - // Sort and id is only special for MLText, text, and content - // Dates are not special in this case - if (propertyDef != null) - { - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - IndexedField indexedField = AlfrescoSolrDataModel.getInstance() - .getQueryableFields(propertyDef.getName(), null, FieldUse.FTS); - for (FieldInstance instance : indexedField.getFields()) - { - Query query = subQueryBuilder.getQuery(instance.getField(), queryText, AnalysisMode.DEFAULT, - luceneFunction); - if (query != null) - { - bQuery.add(query, Occur.SHOULD); - } - } - if (bQuery.build().clauses().size() > 0) - { - return bQuery.build(); - } else - { - return createNoMatchQuery(); - } - } else - { - Query query = subQueryBuilder.getQuery(expandedFieldName, queryText, AnalysisMode.DEFAULT, - luceneFunction); - if (query != null) - { - return query; - } else - { - return createNoMatchQuery(); - } - } - } - } - - /** - * @param queryText - * @return - */ - private boolean isAllStar(String queryText) - { - if ((queryText == null) || (queryText.length() == 0)) - { - return false; - } - for (char c : queryText.toCharArray()) - { - if (c != '*') - { - return false; - } - } - return true; - - } - - /** - * @param dateAndResolution - * @return - */ - private String getDateEnd(Pair dateAndResolution) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.setTime(dateAndResolution.getFirst()); - switch (dateAndResolution.getSecond()) - { - case Calendar.YEAR: - cal.set(Calendar.MONTH, cal.getActualMaximum(Calendar.MONTH)); - case Calendar.MONTH: - cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); - case Calendar.DAY_OF_MONTH: - cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY)); - case Calendar.HOUR_OF_DAY: - cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE)); - case Calendar.MINUTE: - cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND)); - case Calendar.SECOND: - cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND)); - case Calendar.MILLISECOND: - default: - } - SimpleDateFormat formatter = CachingDateFormat.getSolrDatetimeFormat(); - formatter.setTimeZone(TimeZone.getTimeZone("UTC")); - return formatter.format(cal.getTime()); - } - - /** - * Returns the formatted start (i.e. lower bound) of a range query. - */ - private String getDateStart(Pair dateAndResolution) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.setTime(dateAndResolution.getFirst()); - switch (dateAndResolution.getSecond()) - { - case Calendar.YEAR: - cal.set(Calendar.MONTH, cal.getActualMinimum(Calendar.MONTH)); - case Calendar.MONTH: - cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH)); - case Calendar.DAY_OF_MONTH: - cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); - case Calendar.HOUR_OF_DAY: - cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE)); - case Calendar.MINUTE: - cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND)); - case Calendar.SECOND: - cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND)); - case Calendar.MILLISECOND: - default: - } - return CachingDateFormat.getSolrDatetimeFormat().format(cal.getTime()); - } - - private Pair parseDateString(String dateString) - { - try - { - return CachingDateFormat.lenientParse(dateString, Calendar.YEAR); - } catch (java.text.ParseException e) - { - SimpleDateFormat oldDf = CachingDateFormat.getDateFormat(); - oldDf.setTimeZone(TimeZone.getTimeZone("UTC")); - try - { - Date date = oldDf.parse(dateString); - return new Pair<>(date, Calendar.SECOND); - } catch (java.text.ParseException ee) - { - if (dateString.equalsIgnoreCase("min")) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.set(Calendar.YEAR, cal.getMinimum(Calendar.YEAR)); - cal.set(Calendar.DAY_OF_YEAR, cal.getMinimum(Calendar.DAY_OF_YEAR)); - cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY)); - cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE)); - cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND)); - cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND)); - return new Pair(cal.getTime(), Calendar.MILLISECOND); - } else if (dateString.equalsIgnoreCase("now")) - { - return new Pair(new Date(), Calendar.MILLISECOND); - } else if (dateString.equalsIgnoreCase("today")) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.setTime(new Date()); - cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY)); - cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE)); - cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND)); - cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND)); - return new Pair(cal.getTime(), Calendar.DAY_OF_MONTH); - } else if (dateString.equalsIgnoreCase("max")) - { - Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); - cal.set(Calendar.YEAR, cal.getMaximum(Calendar.YEAR)); - cal.set(Calendar.DAY_OF_YEAR, cal.getMaximum(Calendar.DAY_OF_YEAR)); - cal.set(Calendar.HOUR_OF_DAY, cal.getMaximum(Calendar.HOUR_OF_DAY)); - cal.set(Calendar.MINUTE, cal.getMaximum(Calendar.MINUTE)); - cal.set(Calendar.SECOND, cal.getMaximum(Calendar.SECOND)); - cal.set(Calendar.MILLISECOND, cal.getMaximum(Calendar.MILLISECOND)); - return new Pair(cal.getTime(), Calendar.MILLISECOND); - } else - { - return null; // delegate to SOLR date parsing - } - } - } - } - - protected Query functionQueryBuilder(String expandedFieldName, String ending, QName propertyQName, - PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, String queryText, - LuceneFunction luceneFunction) throws ParseException - { - - // Mime type - if (ending.equals(FIELD_MIMETYPE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_SIZE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_LOCALE_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_ENCODING_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_CONTENT_DOC_ID_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_TRANSFORMATION_TIME_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } else if (ending.equals(FIELD_TRANSFORMATION_STATUS_SUFFIX)) - { - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene Function"); - } - - } - - // Already in expanded form - - // ML - - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) - { - // Build a sub query for each locale and or the results together - - // the analysis will take care of - // cross language matching for each entry - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - - addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, propertyDef, queryText, luceneFunction, - booleanQuery, locale, tokenisationMode); - - } - return booleanQuery.build(); - } - // Content - else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) - { - throw new UnsupportedOperationException("Lucene functions not supported for content"); - } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) - { - if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) - || propertyQName.equals(ContentModel.PROP_USERNAME) - || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) - { - throw new UnsupportedOperationException("Functions are not supported agaisnt special text fields"); - } - - boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) - || propertyQName.equals(ContentModel.PROP_USERNAME) - || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); - - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - List locales = searchParameters.getLocales(); - List expandedLocales = new ArrayList(); - for (Locale locale : (((locales == null) || (locales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : locales)) - { - expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); - } - for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) - ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) - { - addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, propertyDef, queryText, luceneFunction, - booleanQuery, locale, tokenisationMode); - - } - return booleanQuery.build(); - } else - { - throw new UnsupportedOperationException("Lucene Function"); - } - } - - protected TermQuery createNoMatchQuery() - { - return new TermQuery(new Term("NO_TOKENS", "__")); - } - - /** - * Returns null if all clause words were filtered away by the analyzer - * - * @param booleanQuery - * - initial BooleanQuery - * @return BooleanQuery or null if booleanQuery has no clauses - */ - protected BooleanQuery getNonEmptyBooleanQuery(BooleanQuery booleanQuery) - { - if (booleanQuery.clauses().size() > 0) - { - return booleanQuery; - } else - { - return null; - } - } - - protected Query createSolr4IdQuery(String queryText) - { - return createTermQuery(FIELD_SOLR4_ID, queryText); - } - - // Previous SOLR - - protected Query createIdQuery(String queryText) - { - if (NodeRef.isNodeRef(queryText)) - { - return createNodeRefQuery(FIELD_LID, queryText); - } else - { - return createNodeRefQuery(FIELD_ID, queryText); - } - } - - protected Query createPathQuery(String queryText, boolean withRepeats) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse(queryText); - SolrPathQuery pathQuery = handler.getQuery(); - pathQuery.setRepeats(withRepeats); - return new SolrCachingPathQuery(pathQuery); - } - - protected Query createQNameQuery(String queryText) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse("//" + queryText); - SolrPathQuery pathQuery = handler.getQuery(); - return new SolrCachingPathQuery(pathQuery); - } - - protected Query createPrimaryAssocQNameQuery(String queryText) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse("//" + queryText); - SolrPathQuery pathQuery = handler.getQuery(); - pathQuery.setPathField(FIELD_PRIMARYASSOCQNAME); - return new SolrCachingPathQuery(pathQuery); - } - - protected Query createPrimaryAssocTypeQNameQuery(String queryText) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse("//" + queryText); - SolrPathQuery pathQuery = handler.getQuery(); - pathQuery.setPathField(FIELD_PRIMARYASSOCTYPEQNAME); - return new SolrCachingPathQuery(pathQuery); - } - - protected Query createAssocTypeQNameQuery(String queryText) throws SAXPathException - { - XPathReader reader = new XPathReader(); - SolrXPathHandler handler = new SolrXPathHandler(); - handler.setNamespacePrefixResolver(namespacePrefixResolver); - handler.setDictionaryService(dictionaryService); - reader.setXPathHandler(handler); - reader.parse("//" + queryText); - SolrPathQuery pathQuery = handler.getQuery(); - pathQuery.setPathField(FIELD_ASSOCTYPEQNAME); - return new SolrCachingPathQuery(pathQuery); - } - - /** - * @param queryText - * @return - */ - protected Query createAclIdQuery(String queryText) throws ParseException - { - return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLID, queryText, AnalysisMode.DEFAULT, - LuceneFunction.FIELD); - } - - /** - * @param queryText - * @return - */ - protected Query createOwnerQuery(String queryText) throws ParseException - { - return new SolrOwnerQuery(queryText); - } - - /** - * @param queryText - * @return - */ - protected Query createReaderQuery(String queryText) throws ParseException - { - return new SolrReaderQuery(queryText); - } - - /** - * @param queryText - * @return - */ - protected Query createAuthorityQuery(String queryText) throws ParseException - { - //return new SolrAuthorityQuery(queryText); - return new SolrAuthoritySetQuery(","+queryText); - } - - // TODO: correct field names - protected Query addContentAttributeQuery(PropertyDefinition pDef, String queryText, SubQuery subQueryBuilder, - AnalysisMode analysisMode, LuceneFunction luceneFunction, String expandedFieldName, - List expandedLocales) throws ParseException - { - BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder(); - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, - FieldUse.FTS); - if (isExactTermSearch(analysisMode)) - { - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - Query subQuery = subQueryBuilder.getQuery(field.getField(), queryText, analysisMode, - luceneFunction); - if (subQuery != null) - { - booleanQueryBuilder.add(subQuery, Occur.SHOULD); - } - } - } - if (booleanQueryBuilder.build().clauses().size() == 0) - { - throw new UnsupportedOperationException("Exact Term search is not supported unless you configure the field <"+pDef.getName()+"> for cross locale search"); - } - } else - { - for (Locale locale : expandedLocales) - { - boolean localisedSearch = locale.toString().length() != 0; - if (localisedSearch) - { - StringBuilder builder = new StringBuilder(queryText.length() + 10); - builder.append("\u0000").append(locale.toString()).append("\u0000").append(queryText); - } - for (FieldInstance field : indexedField.getFields()) - { - if ((!localisedSearch && !field.isLocalised()) || (localisedSearch && field.isLocalised())) - { - Query subQuery = subQueryBuilder.getQuery(field.getField(), queryText, analysisMode, - luceneFunction); - if (subQuery != null) - { - booleanQueryBuilder.add(subQuery, Occur.SHOULD); - } - } - } - } - } - return getNonEmptyBooleanQuery(booleanQueryBuilder.build()); - } - - protected void addLocaleSpecificUntokenisedMLOrTextFunction(String expandedFieldName, PropertyDefinition pDef, - String queryText, LuceneFunction luceneFunction, Builder booleanQuery, Locale locale, - IndexTokenisationMode tokenisationMode) - { - // Query subQuery = new CaseInsensitiveFieldQuery(new - // Term(getFieldName(expandedFieldName, locale, tokenisationMode, - // IndexTokenisationMode.FALSE), getFixedFunctionQueryText( - // queryText, locale, tokenisationMode, IndexTokenisationMode.FALSE))); - // booleanQuery.add(subQuery, Occur.SHOULD); - // - // if (booleanQuery.getClauses().length == 0) - // { - // booleanQuery.add(createNoMatchQuery(), Occur.SHOULD); - // } - throw new UnsupportedOperationException(); - } - - private FieldInstance getFieldInstance(String baseFieldName, PropertyDefinition pDef, Locale locale, - IndexTokenisationMode preferredIndexTokenisationMode, AnalysisMode analysisMode) - { - if (pDef != null) - { - switch (preferredIndexTokenisationMode) - { - case BOTH: - throw new IllegalStateException("Preferred mode can not be BOTH"); - case FALSE: - if (locale.toString().length() == 0) - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.ID); - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - return field; - } - } - } else - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.ID); - for (FieldInstance field : indexedField.getFields()) - { - if (field.isLocalised()) - { - return field; - } - } - } - break; - case TRUE: - { - IndexedField indexedField; - if (isExactTermSearch(analysisMode)) - { - indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.EXACT); - } else - { - indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.FTS); - } - if (locale.toString().length() == 0) - { - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - return field; - } - } - } else - { - for (FieldInstance field : indexedField.getFields()) - { - if (field.isLocalised()) - { - return field; - } - } - } - break; - } - } - return null; - - } - - return new FieldInstance(baseFieldName, false, false); - - } - - private boolean isExactTermSearch(AnalysisMode analysisMode) { - return analysisMode==AnalysisMode.IDENTIFIER && !this.solrParams.get(DEFTYPE).equals(CMIS_REQUEST_HANDLER); - } - - protected void addLocaleSpecificUntokenisedTextRangeFunction(String expandedFieldName, PropertyDefinition pDef, - String lower, String upper, boolean includeLower, boolean includeUpper, LuceneFunction luceneFunction, - Builder booleanQuery, Locale locale, IndexTokenisationMode tokenisationMode) throws ParseException - { - // String field = getFieldName(expandedFieldName, locale, - // tokenisationMode, IndexTokenisationMode.FALSE); - // - // StringBuilder builder = new StringBuilder(); - // builder.append("\u0000").append(locale.toString()).append("\u0000").append(lower); - // String first = getToken(field, builder.toString(), - // AnalysisMode.IDENTIFIER); - // - // builder = new StringBuilder(); - // builder.append("\u0000").append(locale.toString()).append("\u0000").append(upper); - // String last = getToken(field, builder.toString(), - // AnalysisMode.IDENTIFIER); - // - // Query query = new CaseInsensitiveFieldRangeQuery(field, first, last, - // includeLower, includeUpper); - // booleanQuery.add(query, Occur.SHOULD); - - throw new UnsupportedOperationException(); - - } - - protected void addMLTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, - SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, - String expandedFieldName, PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, - Builder booleanQuery, Locale locale) throws ParseException - { - - addMLTextOrTextAttributeQuery(field, pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, tokenisationMode, booleanQuery, locale); - } - - private void addMLTextOrTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, - SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, - String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) - throws ParseException - { - - boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); - try - { - switch (tokenisationMode) - { - case BOTH: - switch (analysisMode) - { - default: - case DEFAULT: - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); - - if (ContentModel.PROP_NAME.equals(pDef.getName())) - { - setLowercaseExpandedTerms(false); - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); - } - - break; - case TOKENISE: - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); - break; - case IDENTIFIER: - setLowercaseExpandedTerms(false); - if (isExactTermSearch(analysisMode)) - {//with exact search we favour tokenization, specifically cross locale tokenization - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); - } else - { - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); - } - break; - case FUZZY: - case PREFIX: - case WILD: - case LIKE: - setLowercaseExpandedTerms(false); - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); - - break; - } - break; - case FALSE: - setLowercaseExpandedTerms(false); - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); - break; - case TRUE: - default: - addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); - break; - } - } finally - { - setLowercaseExpandedTerms(lowercaseExpandedTerms); - } - - } - - protected void addTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, - SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, - String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) - throws ParseException - { - - addMLTextOrTextAttributeQuery(field, pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, - expandedFieldName, tokenisationMode, booleanQuery, locale); - } - - private void addLocaleSpecificMLOrTextAttribute(PropertyDefinition pDef, String queryText, SubQuery subQueryBuilder, - AnalysisMode analysisMode, LuceneFunction luceneFunction, Builder booleanQuery, Locale locale, - String textFieldName, IndexTokenisationMode tokenisationMode, - IndexTokenisationMode preferredTokenisationMode) throws ParseException - { - - FieldInstance fieldInstance = getFieldInstance(textFieldName, pDef, locale, preferredTokenisationMode, analysisMode); - if (fieldInstance != null) - { - StringBuilder builder = new StringBuilder(queryText.length() + 10); - if (fieldInstance.isLocalised()) - { - builder.append("\u0000").append(locale.toString()).append("\u0000"); - } - builder.append(queryText); - Query subQuery = subQueryBuilder.getQuery(fieldInstance.getField(), builder.toString(), analysisMode, - luceneFunction); - if (subQuery != null) - { - booleanQuery.add(subQuery, Occur.SHOULD); - } - } - } - - protected void addTextRange(String field, PropertyDefinition pDef, String part1, String part2, boolean includeLower, - boolean includeUpper, AnalysisMode analysisMode, String fieldName, PropertyDefinition propertyDef, - IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) - throws ParseException, IOException - { - switch (tokenisationMode) - { - case BOTH: - switch (analysisMode) - { - case DEFAULT: - case TOKENISE: - addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, - locale, analysisMode, tokenisationMode, IndexTokenisationMode.TRUE); - break; - case IDENTIFIER: - addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, - locale, analysisMode, tokenisationMode, IndexTokenisationMode.FALSE); - break; - case WILD: - case LIKE: - case PREFIX: - case FUZZY: - default: - throw new UnsupportedOperationException(); - } - break; - case FALSE: - addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, locale, - analysisMode, tokenisationMode, IndexTokenisationMode.FALSE); - break; - case TRUE: - addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, locale, - analysisMode, tokenisationMode, IndexTokenisationMode.TRUE); - break; - default: - } - - } - - private void addLocaleSpecificTextRange(String expandedFieldName, PropertyDefinition pDef, String part1, - String part2, boolean includeLower, boolean includeUpper, Builder booleanQuery, Locale locale, - AnalysisMode analysisMode, IndexTokenisationMode tokenisationMode, - IndexTokenisationMode preferredTokenisationMode) throws ParseException, IOException - { - FieldInstance fieldInstance = getFieldInstance(expandedFieldName, pDef, locale, preferredTokenisationMode, analysisMode); - if (fieldInstance != null) - { - String firstString = null; - if ((part1 != null) && !part1.equals("\u0000")) - { - if (fieldInstance.isLocalised()) - { - firstString = getFirstTokenForRange(getLocalePrefixedText(part1, locale), fieldInstance); - if (firstString == null) - { - firstString = "{" + locale.getLanguage() + "}"; - } - } else - { - firstString = getFirstTokenForRange(part1, fieldInstance); - } - } else - { - if (fieldInstance.isLocalised()) - { - firstString = "{" + locale.getLanguage() + "}"; - } else - { - firstString = null; - } - } - - String lastString = null; - if ((part2 != null) && !part2.equals("\uffff")) - { - if (fieldInstance.isLocalised()) - { - lastString = getFirstTokenForRange(getLocalePrefixedText(part2, locale), fieldInstance); - if (lastString == null) - { - lastString = "{" + locale.getLanguage() + "}\uffff"; - } - } else - { - lastString = getFirstTokenForRange(part2, fieldInstance); - } - } else - { - if (fieldInstance.isLocalised()) - { - lastString = "{" + locale.getLanguage() + "}\uffff"; - } else - { - lastString = null; - } - } - - - // * is returned by stringField but it is not correct in case of range queries. - if (firstString != null && firstString.equals("*")) - { - firstString = null; - } - - if (lastString != null && lastString.equals("*")) - { - lastString = null; - } - - TermRangeQuery query = new TermRangeQuery(fieldInstance.getField(), - firstString == null ? null : new BytesRef(firstString), - lastString == null ? null : new BytesRef(lastString), includeLower, includeUpper); - booleanQuery.add(query, Occur.SHOULD); - } - } - - private String getFirstTokenForRange(String string, FieldInstance field) throws IOException - { - PackedTokenAttributeImpl nextToken; - TokenStream source = null; - ; - - try - { - source = getAnalyzer().tokenStream(field.getField(), new StringReader(string)); - source.reset(); - while (source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if (source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if (source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - nextToken = new PackedTokenAttributeImpl(); - nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); - nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if (typeAtt != null) - { - nextToken.setType(typeAtt.type()); - } - if (posIncAtt != null) - { - nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - - return nextToken.toString(); - } - } finally - { - try - { - if (source != null) - { - source.close(); - } - } catch (IOException e) - { - // ignore - } - } - return null; - } - - protected void addTextSpanQuery(String field, PropertyDefinition pDef, String first, String last, int slop, - boolean inOrder, String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, - Locale locale) { - addMLTextOrTextSpanQuery(field, pDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, - booleanQuery, locale); - } - - protected org.apache.lucene.search.Query addContentSpanQuery(String afield, PropertyDefinition pDef, String first, - String last, int slop, boolean inOrder, String expandedFieldName, List expandedLocales) - { - try - { - BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); - for (Locale locale : expandedLocales) - { - if (locale.toString().length() == 0) - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.FTS); - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - SpanQuery firstQuery = buildSpanOrQuery(first, field); - SpanQuery lastQuery = buildSpanOrQuery(last, field); - SpanNearQuery result = new SpanNearQuery(new SpanQuery[] - { firstQuery, lastQuery }, slop, inOrder); - booleanQuery.add(result, Occur.SHOULD); - } - } - } else - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), - null, FieldUse.FTS); - for (FieldInstance field : indexedField.getFields()) - { - if (field.isLocalised()) - { - SpanQuery firstQuery = buildSpanOrQuery(getLocalePrefixedText(first, locale), field); - SpanQuery lastQuery = buildSpanOrQuery(getLocalePrefixedText(last, locale), field); - SpanNearQuery result = new SpanNearQuery(new SpanQuery[] - { firstQuery, lastQuery }, slop, inOrder); - booleanQuery.add(result, Occur.SHOULD); - } - } - } - } - return getNonEmptyBooleanQuery(booleanQuery.build()); - } catch (IOException ioe) - { - return createNoMatchQuery(); - } - - } - - private String getLocalePrefixedText(String text, Locale locale) - { - StringBuilder builder = new StringBuilder(text.length() + 10); - builder.append("\u0000").append(locale.toString()).append("\u0000").append(text); - return builder.toString(); - } - - /** - * @param first - * @param field - * @return SpanOrQuery - * @throws IOException - */ - private SpanQuery buildSpanOrQuery(String first, FieldInstance field) throws IOException - { - ArrayList spanOrQueryParts = new ArrayList(); - - PackedTokenAttributeImpl nextToken; - TokenStream source = null; - - try - { - source = getAnalyzer().tokenStream(field.getField(), new StringReader(first)); - source.reset(); - while (source.incrementToken()) - { - CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); - OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); - TypeAttribute typeAtt = null; - if (source.hasAttribute(TypeAttribute.class)) - { - typeAtt = source.getAttribute(TypeAttribute.class); - } - PositionIncrementAttribute posIncAtt = null; - if (source.hasAttribute(PositionIncrementAttribute.class)) - { - posIncAtt = source.getAttribute(PositionIncrementAttribute.class); - } - nextToken = new PackedTokenAttributeImpl(); - nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); - nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); - if (typeAtt != null) - { - nextToken.setType(typeAtt.type()); - } - if (posIncAtt != null) - { - nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); - } - - SpanQuery termQuery = new SpanTermQuery(new Term(field.getField(), nextToken.toString())); - spanOrQueryParts.add(termQuery); - } - } finally - { - try - { - if (source != null) - { - source.close(); - } - } catch (IOException e) - { - // ignore - } - } - - if (spanOrQueryParts.size() == 1) - { - return spanOrQueryParts.get(0); - } else - { - return new SpanOrQuery(spanOrQueryParts.toArray(new SpanQuery[] - {})); - } - } - - protected void addMLTextSpanQuery(String field, PropertyDefinition pDef, String first, String last, int slop, - boolean inOrder, String expandedFieldName, PropertyDefinition propertyDef, - IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) { - addMLTextOrTextSpanQuery(field, pDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, - booleanQuery, locale); - } - - private void addMLTextOrTextSpanQuery(String afield, PropertyDefinition pDef, String first, String last, int slop, - boolean inOrder, String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, - Locale locale) - { - try - { - - if (locale.toString().length() == 0) - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, - FieldUse.FTS); - for (FieldInstance field : indexedField.getFields()) - { - if (!field.isLocalised()) - { - SpanQuery firstQuery = buildSpanOrQuery(first, field); - SpanQuery lastQuery = buildSpanOrQuery(last, field); - SpanNearQuery result = new SpanNearQuery(new SpanQuery[] - { firstQuery, lastQuery }, slop, inOrder); - booleanQuery.add(result, Occur.SHOULD); - } - } - } else - { - IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, - FieldUse.FTS); - for (FieldInstance field : indexedField.getFields()) - { - if (field.isLocalised()) - { - SpanQuery firstQuery = buildSpanOrQuery(getLocalePrefixedText(first, locale), field); - SpanQuery lastQuery = buildSpanOrQuery(getLocalePrefixedText(last, locale), field); - SpanNearQuery result = new SpanNearQuery(new SpanQuery[] - { firstQuery, lastQuery }, slop, inOrder); - booleanQuery.add(result, Occur.SHOULD); - } - } - } - - } catch (IOException ioe) - { - - } - } - - public boolean addContentCrossLocaleWildcards() - { - return false; - } - - protected Query createOwnerSetQuery(String queryText) throws ParseException - { - return new SolrOwnerSetQuery(queryText); - } - - protected Query createReaderSetQuery(String queryText) throws ParseException - { - return new SolrReaderSetQuery(queryText); - } - - protected Query createAuthoritySetQuery(String queryText) throws ParseException - { - return new SolrAuthoritySetQuery(queryText); - } - - protected Query createDeniedQuery(String queryText) throws ParseException - { - return new SolrDeniedQuery(queryText); - } - - protected Query createDenySetQuery(String queryText) throws ParseException - { - return new SolrDenySetQuery(queryText); - } - - private BytesRef analyzeMultitermTerm(String field, String part) { - return analyzeMultitermTerm(field, part, getAnalyzer()); - } - - // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) - @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") - protected BytesRef analyzeMultitermTerm(String field, String part, Analyzer analyzerIn) { - if (analyzerIn == null) analyzerIn = getAnalyzer(); - - try (TokenStream source = analyzerIn.tokenStream(field, part)) { - source.reset(); - - TermToBytesRefAttribute termAtt = source.getAttribute(TermToBytesRefAttribute.class); - - if (!source.incrementToken()) - throw new IllegalArgumentException("analyzer returned no terms for multiTerm term: " + part); - BytesRef bytes = BytesRef.deepCopyOf(termAtt.getBytesRef()); - if (source.incrementToken()) - throw new IllegalArgumentException("analyzer returned too many terms for multiTerm term: " + part); - source.end(); - return bytes; - } catch (IOException e) { - throw new RuntimeException("Error analyzing multiTerm term: " + part, e); - } - } - - protected Query newRangeQuery(String field, String part1, String part2, boolean startInclusive, boolean endInclusive) { - final BytesRef start; - final BytesRef end; - - if (part1 == null) { - start = null; - } else { - start = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part1) : new BytesRef(part1); - } - - if (part2 == null) { - end = null; - } else { - end = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part2) : new BytesRef(part2); - } - - final TermRangeQuery query = new TermRangeQuery(field, start, end, startInclusive, endInclusive); - - query.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE); - return query; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import edu.umd.cs.findbugs.annotations.SuppressWarnings; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.dictionary.IndexTokenisationMode; +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.repo.search.adaptor.AnalysisMode; +import org.alfresco.repo.search.adaptor.LuceneFunction; +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.repo.search.impl.QueryParserUtils; +import org.alfresco.repo.search.impl.lucene.analysis.MLTokenDuplicator; +import org.alfresco.repo.search.impl.parsers.FTSQueryException; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser.RerankPhase; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +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.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.AlfrescoAnalyzerWrapper; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.SpecializedFieldType; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldInstance; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.alfresco.solr.AlfrescoSolrDataModel.IndexedField; +import org.alfresco.solr.component.FingerPrintComponent; +import org.alfresco.solr.utils.ThrowingFunction; +import org.alfresco.util.CachingDateFormat; +import org.alfresco.util.Pair; +import org.alfresco.util.SearchLanguageConversion; +import org.antlr.misc.OrderedHashSet; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.commongrams.CommonGramsFilter; +import org.apache.lucene.analysis.shingle.ShingleFilterFactory; +import org.apache.lucene.analysis.synonym.SynonymFilter; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; +import org.apache.lucene.analysis.util.TokenFilterFactory; +import org.apache.lucene.document.Document; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.Term; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.queryparser.classic.QueryParser; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BooleanQuery.Builder; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.LegacyNumericRangeQuery; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.RegexpQuery; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TermRangeQuery; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper; +import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper.TopTermsSpanBooleanQueryRewrite; +import org.apache.lucene.search.spans.SpanNearQuery; +import org.apache.lucene.search.spans.SpanOrQuery; +import org.apache.lucene.search.spans.SpanQuery; +import org.apache.lucene.search.spans.SpanTermQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.Version; +import org.apache.solr.analysis.TokenizerChain; +import org.apache.solr.client.solrj.SolrRequest; +import org.apache.solr.client.solrj.impl.HttpClientUtil; +import org.apache.solr.client.solrj.impl.HttpSolrClient; +import org.apache.solr.client.solrj.request.QueryRequest; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.core.SolrCore; +import org.apache.solr.handler.component.HttpShardHandlerFactory; +import org.apache.solr.handler.component.ShardHandlerFactory; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.update.UpdateShardHandlerConfig; +import org.jaxen.saxpath.SAXPathException; +import org.jaxen.saxpath.base.XPathReader; +import org.json.JSONObject; +import org.springframework.extensions.surf.util.I18NUtil; + +import java.io.IOException; +import java.io.StringReader; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.TimeZone; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.apache.solr.search.QueryParsing.DEFTYPE; + +/** + * @author Andy + * + */ +public class Solr4QueryParser extends QueryParser implements QueryConstants +{ + + private static final String CMIS_REQUEST_HANDLER = "cmis"; + + public Solr4QueryParser(SolrQueryRequest req, Version matchVersion, String f, Analyzer a, + FTSQueryParser.RerankPhase rerankPhase) + { + super(f, a); + setAllowLeadingWildcard(true); + setAnalyzeRangeTerms(true); + this.rerankPhase = rerankPhase; + this.schema = req.getSchema(); + this.solrParams = req.getParams(); + SolrCore core = req.getCore(); + if(core != null) { + this.shardHandlerFactory = core.getCoreContainer().getShardHandlerFactory(); + } + this.request = req; + + } + + private RerankPhase rerankPhase; + private SolrParams solrParams; + private ShardHandlerFactory shardHandlerFactory; + private SolrQueryRequest request; + + + IndexSchema schema; + + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(Solr4QueryParser.class); + + protected NamespacePrefixResolver namespacePrefixResolver; + + protected DictionaryService dictionaryService; + + private TenantService tenantService; + + private SearchParameters searchParameters; + + private MLAnalysisMode mlAnalysisMode = MLAnalysisMode.EXACT_LANGUAGE_AND_ALL; + + private int internalSlop = 0; + + int topTermSpanRewriteLimit = 1000; + + /** + * @param topTermSpanRewriteLimit + * the topTermSpanRewriteLimit to set + */ + public void setTopTermSpanRewriteLimit(int topTermSpanRewriteLimit) + { + this.topTermSpanRewriteLimit = topTermSpanRewriteLimit; + } + + /** + * @param searchParameters + */ + public void setSearchParameters(SearchParameters searchParameters) + { + this.searchParameters = searchParameters; + } + + /** + * @param namespacePrefixResolver + */ + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } + + /** + * @param tenantService + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public SearchParameters getSearchParameters() + { + return searchParameters; + } + + @Override + protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException + { + try + { + internalSlop = slop; + Query query; + query = getFieldQuery(field, queryText); + return query; + } + finally + { + internalSlop = 0; + } + + } + + /** + * @param field + * @param queryText + * @param analysisMode + * @param slop + * @param luceneFunction + * @return the query + * @throws ParseException + * @throws IOException + */ + public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, int slop, + LuceneFunction luceneFunction) throws ParseException + { + try + { + internalSlop = slop; + Query query = getFieldQuery(field, queryText, analysisMode, luceneFunction); + return query; + } + finally + { + internalSlop = 0; + } + + } + + /** + * @param field + * @param sqlLikeClause + * @param analysisMode + * @return the query + * @throws ParseException + */ + public Query getLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException + { + String luceneWildCardExpression = translate(sqlLikeClause); + return getWildcardQuery(field, luceneWildCardExpression, AnalysisMode.LIKE); + } + + private String translate(String string) + { + StringBuilder builder = new StringBuilder(string.length()); + + boolean lastWasEscape = false; + + for (int i = 0; i < string.length(); i++) + { + char c = string.charAt(i); + if (lastWasEscape) + { + builder.append(c); + lastWasEscape = false; + } else + { + if (c == '\\') + { + lastWasEscape = true; + } else if (c == '%') + { + builder.append('*'); + } else if (c == '_') + { + builder.append('?'); + } else if (c == '*') + { + builder.append('\\'); + builder.append(c); + } else if (c == '?') + { + builder.append('\\'); + builder.append(c); + } else + { + builder.append(c); + } + } + } + if (lastWasEscape) + { + throw new FTSQueryException("Escape character at end of string " + string); + } + + return builder.toString(); + } + + /** + * @param field + * @param queryText + * @param analysisMode + * @param luceneFunction + * @return the query + * @throws ParseException + * @throws IOException + */ + public Query getDoesNotMatchFieldQuery(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + Query allQuery = new MatchAllDocsQuery(); + Query matchQuery = getFieldQuery(field, queryText, analysisMode, luceneFunction); + if ((matchQuery != null)) + { + query.add(allQuery, Occur.MUST); + query.add(matchQuery, Occur.MUST_NOT); + } else + { + throw new UnsupportedOperationException(); + } + return query.build(); + } + + public Query getFieldQuery(String field, String queryText) throws ParseException + { + return getFieldQuery(field, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); + } + + /** + * @param field + * @param first + * @param last + * @param slop + * @param inOrder + * @return the query + * @throws ParseException + */ + public Query getSpanQuery(String field, String first, String last, int slop, boolean inOrder) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATH); + } + else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + return createDefaultTextQuery(textField -> getSpanQuery(textField, first, last, slop, inOrder)); + } else if (field.equals(FIELD_CLASS)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_CLASS); + } else if (field.equals(FIELD_TYPE)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_TYPE); + } else if (field.equals(FIELD_EXACTTYPE)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTTYPE); + } else if (field.equals(FIELD_ASPECT)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_ASPECT); + } else if (field.equals(FIELD_EXACTASPECT)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTASPECT); + } else if (isPropertyField(field)) + { + return spanQueryBuilder(field, first, last, slop, inOrder); + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); + query.add(part, Occur.SHOULD); + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getSpanQuery(fieldName, first, last, slop, inOrder); + query.add(part, Occur.SHOULD); + } + return query.build(); + } + + } else if (field.equals(FIELD_ISUNSET)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISUNSET); + } else if (field.equals(FIELD_ISNULL)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNULL); + } else if (field.equals(FIELD_ISNOTNULL)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNOTNULL); + } else if (field.equals(FIELD_EXISTS)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXISTS); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); + query.add(part, Occur.SHOULD); + } + return query.build(); + } else if (field.equals(FIELD_FTSSTATUS)) + { + throw new UnsupportedOperationException("Span is not supported for " + FIELD_FTSSTATUS); + } else if (field.equals(FIELD_TAG)) + { + return null; + } else if (isPropertyField(field)) + { + return spanQueryBuilder(field, first, last, slop, inOrder); + } else + { + BytesRef firstBytes = analyzeMultitermTerm(field, first); + BytesRef lastBytes = analyzeMultitermTerm(field, last); + SpanQuery firstTerm = new SpanTermQuery(new Term(field, firstBytes)); + SpanQuery lastTerm = new SpanTermQuery(new Term(field, lastBytes)); + return new SpanNearQuery(new SpanQuery[] + { firstTerm, lastTerm }, slop, inOrder); + } + } + + protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException + { + return getFieldQuery(field, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); + } + + /** + * @param field + * @param queryText + * @param analysisMode + * @param luceneFunction + * @return the query + * @throws ParseException + * @throws IOException + */ + @SuppressWarnings("deprecation") + public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + try + { + if (field.equals(FIELD_PATH)) + { + return createPathQuery(queryText, false); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + return createPathQuery(queryText, true); + } else if (field.equals(FIELD_TEXT)) + { + return createTextQuery(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_ID)) + { + return createIdQuery(queryText); + } else if (field.equals(FIELD_SOLR4_ID)) + { + return createSolr4IdQuery(queryText); + } else if (field.equals(FIELD_DBID)) + { + return createDbidQuery(queryText); + } else if (field.equals(FIELD_ACLID)) + { + return createAclIdQuery(queryText); + } else if (field.equals(FIELD_OWNER)) + { + return createOwnerQuery(queryText); + } else if (field.equals(FIELD_OWNERSET)) + { + return createOwnerSetQuery(queryText); + } else if (field.equals(FIELD_READER)) + { + return createReaderQuery(queryText); + } else if (field.equals(FIELD_READERSET)) + { + return createReaderSetQuery(queryText); + } else if (field.equals(FIELD_AUTHORITY)) + { + /* + * ACL DOCUMENTATION STARTS HERE + * This creates the query that applies the ACL filter + */ + return createAuthorityQuery(queryText); + } else if (field.equals(FIELD_AUTHORITYSET)) + { + /* + * ACL DOCUMENTATION STARTS HERE + * This creates the query that applies the ACL filter + */ + return createAuthoritySetQuery(queryText); + } else if (field.equals(FIELD_DENIED)) + { + return createDeniedQuery(queryText); + } else if (field.equals(FIELD_DENYSET)) + { + return createDenySetQuery(queryText); + } else if (field.equals(FIELD_ISROOT)) + { + return createIsRootQuery(queryText); + } else if (field.equals(FIELD_ISCONTAINER)) + { + return createIsContainerQuery(queryText); + } else if (field.equals(FIELD_ISNODE)) + { + return createIsNodeQuery(queryText); + } else if (field.equals(FIELD_TX)) + { + return createTransactionQuery(queryText); + } else if (field.equals(FIELD_INTXID)) + { + return createInTxIdQuery(queryText); + } else if (field.equals(FIELD_INACLTXID)) + { + return createInAclTxIdQuery(queryText); + } else if (field.equals(FIELD_PARENT)) + { + return createParentQuery(queryText); + } else if (field.equals(FIELD_PRIMARYPARENT)) + { + return createPrimaryParentQuery(queryText); + } else if (field.equals(FIELD_QNAME)) + { + return createQNameQuery(queryText); + } else if (field.equals(FIELD_PRIMARYASSOCQNAME)) + { + return createPrimaryAssocQNameQuery(queryText); + } else if (field.equals(FIELD_PRIMARYASSOCTYPEQNAME)) + { + return createPrimaryAssocTypeQNameQuery(queryText); + } else if (field.equals(FIELD_ASSOCTYPEQNAME)) + { + return createAssocTypeQNameQuery(queryText); + } else if (field.equals(FIELD_CLASS)) + { + ClassDefinition target = QueryParserUtils.matchClassDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (target == null) + { + return new TermQuery(new Term(FIELD_TYPE, "_unknown_")); + } + return getFieldQuery(target.isAspect() ? FIELD_ASPECT : FIELD_TYPE, queryText, analysisMode, + luceneFunction); + } else if (field.equals(FIELD_TYPE)) + { + return createTypeQuery(queryText, false); + } else if (field.equals(FIELD_EXACTTYPE)) + { + return createTypeQuery(queryText, true); + } else if (field.equals(FIELD_ASPECT)) + { + return createAspectQuery(queryText, false); + } else if (field.equals(FIELD_EXACTASPECT)) + { + return createAspectQuery(queryText, true); + } else if (isPropertyField(field)) + { + Query query = attributeQueryBuilder(field, queryText, new FieldQuery(), analysisMode, luceneFunction); + return query; + } else if (field.equals(FIELD_ALL)) + { + return createAllQuery(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_ISUNSET)) + { + return createIsUnsetQuery(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_ISNULL)) + { + return createIsNullQuery(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_ISNOTNULL)) + { + return createIsNotNull(queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_EXISTS)) + { + return createExistsQuery(queryText, analysisMode, luceneFunction); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field) != null) + { + return createDataTypeDefinitionQuery(field, queryText, analysisMode, luceneFunction); + } else if (field.equals(FIELD_TXID)) + { + return createTxIdQuery(queryText); + } else if (field.equals(FIELD_ACLTXID)) + { + return createAclTxIdQuery(queryText); + } else if (field.equals(FIELD_TXCOMMITTIME)) + { + return createTxCommitTimeQuery(queryText); + } else if (field.equals(FIELD_ACLTXCOMMITTIME)) + { + return createAclTxCommitTimeQuery(queryText); + } else if (field.equals(FIELD_TAG)) + { + return createTagQuery(queryText); + } else if (field.equals(FIELD_SITE)) + { + return createSiteQuery(queryText); + } else if (field.equals(FIELD_PNAME)) + { + return createPNameQuery(queryText); + } else if (field.equals(FIELD_NPATH)) + { + return createNPathQuery(queryText); + } else if (field.equals(FIELD_TENANT)) + { + return createTenantQuery(queryText); + } else if (field.equals(FIELD_ANCESTOR)) + { + return createAncestorQuery(queryText); + } else if (field.equals(FIELD_FINGERPRINT)) + { + String[] parts = queryText.split("_"); + Collection values = null; + String nodeId = parts[0]; + + JSONObject json = (JSONObject)request.getContext().get(AbstractQParser.ALFRESCO_JSON); + String fingerPrint = null; + if(json != null) { + //Was the fingerprint passed in + String fingerPrintKey = "fingerprint." + nodeId; + if(json.has(fingerPrintKey)) { + fingerPrint = (String) json.get("fingerprint." + nodeId); + if (fingerPrint != null) { + List l = new ArrayList(); + String[] hashes = fingerPrint.split(" "); + for (String hash : hashes) { + l.add(hash); + } + values = l; + } + } + } else { + json = new JSONObject(); + } + + if (values == null) { + values = fetchMinHash(nodeId); + } + + String shards = this.solrParams.get("shards"); + if(values == null && shards != null) { + //we are in distributed mode + //Fetch the fingerPrint from the shards. + //The UUID and DBID will both work for method call. + values = fetchFingerPrint(shards, nodeId); + } + + //If we're in distributed mode then add the fingerprint to the json + if(values != null && shards != null && fingerPrint == null) { + ModifiableSolrParams newParams = new ModifiableSolrParams(); + newParams.add(solrParams); + solrParams = newParams; + json.put("fingerprint." + nodeId, join(values, " ")); + String jsonString = json.toString(); + newParams.add(AbstractQParser.ALFRESCO_JSON, jsonString); + request.getContext().put(AbstractQParser.ALFRESCO_JSON, json); + request.setParams(newParams); + } + + return createFingerPrintQuery(field, queryText, values, analysisMode, luceneFunction); + + } else + { + return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); + } + + } catch (SAXPathException e) + { + throw new ParseException("Failed to parse XPath...\n" + e.getMessage()); + } catch (IOException e) + { + throw new ParseException("IO: " + e.getMessage()); + } + + } + + private boolean isNumber(String s) { + for(int i=0; i 0) { + builder.append(" "); + } + builder.append(o.toString()); + } + return builder.toString(); + } + + + /** + * fetch MINHASH values from lucene index + * @param id argument of search. It is taken as DBID if is numberic, LID otherwise + * @return List of minhashes + * @throws IOException + */ + private Collection fetchMinHash(String id) throws IOException { + SolrIndexSearcher searcher = request.getSearcher(); + + + Query q; + if(isNumber(id)) + { + long dbid = Long.parseLong(id); + q = LegacyNumericRangeQuery.newLongRange("DBID", dbid, dbid + 1, true, false); + } + else + { + String query = id.startsWith("workspace") ? id : "workspace://SpacesStore/"+id; + q = new TermQuery(new Term(FIELD_LID, query)); + } + + TopDocs docs = searcher.search(q, 1); + Set fields = new HashSet(); + fields.add(FIELD_FINGERPRINT); + + if(docs.totalHits == 1) { + ScoreDoc scoreDoc = docs.scoreDocs[0]; + Document doc = searcher.doc(scoreDoc.doc, fields); + + List values = new ArrayList<>(); + + IndexableField[] minHashes = doc.getFields("MINHASH"); + for (IndexableField minHash : minHashes) + { + SchemaField sf = schema.getFieldOrNull(minHash.name()); + Object value = DocsStreamer.getValue(sf, minHash); + values.add(value); + } + + return values; + } + + return null; + } + + private Collection fetchFingerPrint(String shards, String nodeId) { + shards = shards.replace(",", "|"); + List urls = ((HttpShardHandlerFactory)shardHandlerFactory).buildURLList(shards); + ExecutorService executorService = null; + List futures = new ArrayList(); + Collection fingerPrint = null; + try { + executorService = Executors.newCachedThreadPool(); + for (String url : urls) { + futures.add(executorService.submit(new FingerPrintFetchTask(url, nodeId))); + } + + for (Future future : futures) { + Collection col = future.get(); + if(col != null) { + fingerPrint = col; + } + } + + }catch(Exception e) { + logger.error(e); + } finally { + if (executorService != null) + { + executorService.shutdown(); + } + } + return fingerPrint; + } + + + + + private class FingerPrintFetchTask implements Callable { + private String url; + private String id; + + public FingerPrintFetchTask(String url, String id) { + this.url = url; + this.id = id; + } + + public Collection call() throws Exception { + HttpSolrClient solrClient = null; + CloseableHttpClient closeableHttpClient = null; + try { + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add(FingerPrintComponent.COMPONENT_NAME, "true"); + params.add("id", id); + params.add("qt","/fingerprint"); + closeableHttpClient = HttpClientUtil.createClient(getClientParams()); + solrClient = new HttpSolrClient.Builder(url).withHttpClient(closeableHttpClient).build();; + QueryRequest request = new QueryRequest(params, SolrRequest.METHOD.POST); + QueryResponse response = request.process(solrClient); + NamedList dataResponse = response.getResponse(); + NamedList fingerprint = (NamedList) dataResponse.get("fingerprint"); + return (Collection)fingerprint.get("MINHASH"); + } finally { + if (closeableHttpClient != null) + { + closeableHttpClient.close(); + } + if (solrClient != null) + { + solrClient.close(); + } + } + } + + protected ModifiableSolrParams getClientParams() { + ModifiableSolrParams clientParams = new ModifiableSolrParams(); + clientParams.set(HttpClientUtil.PROP_SO_TIMEOUT, UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATESOTIMEOUT); + clientParams.set(HttpClientUtil.PROP_CONNECTION_TIMEOUT, UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATECONNTIMEOUT); + return clientParams; + } + } + + /** + * @param field + * @param queryText + * @param analysisMode + * + * @param luceneFunction + * @return + * @throws IOException + * @throws ParseException + */ + private Query createFingerPrintQuery(String field, String queryText, Collection values, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws IOException, ParseException + { + String[] parts = queryText.split("_"); + if (parts.length == 0) + { + return createIsNodeQuery("T"); + } + + if (values != null) + { + int bandSize = 1; + float fraction = -1; + float truePositive = 1; + if (parts.length > 1) + { + fraction = Float.parseFloat(parts[1]); + if (fraction > 1) + { + fraction /= 100; + } + } + if (parts.length > 2) + { + truePositive = Float.parseFloat(parts[2]); + if (truePositive > 1) + { + truePositive /= 100; + } + bandSize = computeBandSize(values.size(), fraction, truePositive); + } + BooleanQuery.Builder builder = new BooleanQuery.Builder(); + BooleanQuery.Builder childBuilder = new BooleanQuery.Builder(); + int rowInBand = 0; + for (Object token : values) + { + TermQuery tq = new TermQuery(new Term("MINHASH", token.toString())); + if (bandSize == 1) + { + builder.add(new ConstantScoreQuery(tq), Occur.SHOULD); + } else + { + childBuilder.add(new ConstantScoreQuery(tq), Occur.MUST); + rowInBand++; + if (rowInBand == bandSize) + { + builder.add(new ConstantScoreQuery(childBuilder.setDisableCoord(true).build()), + Occur.SHOULD); + childBuilder = new BooleanQuery.Builder(); + rowInBand = 0; + } + } + } + // Avoid a dubious narrow band .... wrap around and pad with the + // start + if (childBuilder.build().clauses().size() > 0) + { + for (Object token : values) + { + TermQuery tq = new TermQuery(new Term("MINHASH", token.toString())); + childBuilder.add(new ConstantScoreQuery(tq), Occur.MUST); + rowInBand++; + if (rowInBand == bandSize) + { + builder.add(new ConstantScoreQuery(childBuilder.setDisableCoord(true).build()), + Occur.SHOULD); + break; + } + } + } + builder.setDisableCoord(true); + if (parts.length == 2) + { + builder.setMinimumNumberShouldMatch((int) (Math.ceil(values.size() * fraction))); + } + Query q = builder.build(); + return q; + } else + { + return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); + } + } + + private int computeBandSize(int numHash, double sim, double expectedTruePositive) + { + for (int bands = 1; bands <= numHash; bands++) + { + int rowsInBand = numHash / bands; + double truePositive = 1 - Math.pow(1 - Math.pow(sim, rowsInBand), bands); + if (truePositive > expectedTruePositive) + { + return rowsInBand; + } + } + return 1; + } + + private ArrayList getTokens(IndexableField indexableField) throws IOException + { + ArrayList tokens = new ArrayList(); + + TokenStream ts = indexableField.tokenStream(schema.getIndexAnalyzer(), null); + CharTermAttribute termAttribute = ts.getAttribute(CharTermAttribute.class); + ts.reset(); + while (ts.incrementToken()) + { + String token = new String(termAttribute.buffer(), 0, termAttribute.length()); + tokens.add(token); + } + ts.end(); + ts.close(); + + return tokens; + } + + /** + * @param queryText + * @return + */ + private org.apache.lucene.search.Query createNPathQuery(String queryText) + { + return createTermQuery(FIELD_NPATH, queryText); + } + + /** + * @param queryText + * @return + */ + private Query createPNameQuery(String queryText) + { + return createTermQuery(FIELD_PNAME, queryText); + } + + /** + * @param queryText + * @return + */ + private Query createSiteQuery(String queryText) + { + if (queryText.equals("_EVERYTHING_")) + { + return createTermQuery(FIELD_ISNODE, "T"); + } + else if (queryText.equals("_ALL_SITES_")) + { + BooleanQuery.Builder invertedRepositoryQuery = new BooleanQuery.Builder(); + invertedRepositoryQuery.add(createTermQuery(FIELD_ISNODE, "T"), Occur.MUST); + invertedRepositoryQuery.add(createTermQuery(FIELD_SITE, "_REPOSITORY_"), Occur.MUST_NOT); + return invertedRepositoryQuery.build(); + } + else + { + return createTermQuery(FIELD_SITE, queryText); + } + } + + private boolean isPropertyField(String field) + { + if (field.startsWith(PROPERTY_FIELD_PREFIX)) + { + return true; + } + int index = field.lastIndexOf('@'); + if (index > -1) + { + PropertyDefinition pDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field.substring(index + 1)); + if (pDef != null) + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance() + .getIndexedFieldNamesForProperty(pDef.getName()); + for (FieldInstance instance : indexedField.getFields()) + { + if (instance.getField().equals(field)) + { + return true; + } + } + return false; + } else + { + return false; + } + } else + { + return false; + } + + } + + protected Query createTenantQuery(String queryText) throws ParseException + { + + if (queryText.length() > 0) + { + + return getFieldQueryImplWithIOExceptionWrapped(FIELD_TENANT, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + + } + else + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_TENANT, "_DEFAULT_", AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + } + + protected Query createAncestorQuery(String queryText) throws ParseException + { + return createNodeRefQuery(FIELD_ANCESTOR, queryText); + } + + /** + * @param tag + * (which will then be ISO9075 encoded) + * @return + * @throws ParseException + */ + protected Query createTagQuery(String tag) throws ParseException + { + return createTermQuery(FIELD_TAG, tag.toLowerCase()); + } + + private Query getFieldQueryImplWithIOExceptionWrapped(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException + { + try + { + return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); + } + catch (IOException e) + { + throw new ParseException("IO: " + e.getMessage()); + } + } + + /** + * @param queryText + * @return + */ + protected Query createDbidQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_DBID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createTxIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_TXID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createAclTxIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLTXID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createTxCommitTimeQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_TXCOMMITTIME, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createAclTxCommitTimeQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLTXCOMMITTIME, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createDataTypeDefinitionQuery(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc if + // required + Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), queryText, analysisMode, + luceneFunction); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } + + protected Query createIsNotNull(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (pd != null) + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + query.add(createTermQuery(FIELD_PROPERTIES, pd.getName().toString()), Occur.MUST); + query.add(createTermQuery(FIELD_NULLPROPERTIES, pd.getName().toString()), Occur.MUST_NOT); + ; + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + + Query presenceQuery = getWildcardQuery(queryText, "*"); + if (presenceQuery != null) + { + query.add(presenceQuery, Occur.MUST); + } + return query.build(); + } + } + + protected Query createIsNullQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (pd != null) + { + return createTermQuery(FIELD_NULLPROPERTIES, pd.getName().toString()); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + Query presenceQuery = getWildcardQuery(queryText, "*"); + if (presenceQuery != null) + { + query.add(createIsNodeQuery("T"), Occur.MUST); + query.add(presenceQuery, Occur.MUST_NOT); + } + return query.build(); + } + } + + protected Query createIsUnsetQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (pd != null) + { + ClassDefinition containerClass = pd.getContainerClass(); + QName container = containerClass.getName(); + String classType = containerClass.isAspect() ? FIELD_ASPECT : FIELD_TYPE; + Query typeQuery = getFieldQuery(classType, container.toString(), analysisMode, luceneFunction); + + BooleanQuery.Builder query = new BooleanQuery.Builder(); + Query presenceQuery = createTermQuery(FIELD_PROPERTIES, pd.getName().toString()); + if (presenceQuery != null) + { + query.add(typeQuery, Occur.MUST); + query.add(presenceQuery, Occur.MUST_NOT); + } + + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + + Query presenceQuery = getWildcardQuery(queryText, "*"); + if (presenceQuery != null) + { + query.add(presenceQuery, Occur.MUST_NOT); + } + return query.build(); + } + } + + protected Query createExistsQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + PropertyDefinition pd = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (pd != null) + { + return createTermQuery(FIELD_PROPERTIES, pd.getName().toString()); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + + Query presenceQuery = getWildcardQuery(queryText, "*"); + if (presenceQuery != null) + { + query.add(createIsNodeQuery("T"), Occur.MUST); + query.add(presenceQuery, Occur.MUST_NOT); + } + return query.build(); + } + } + + protected Query createAllQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + // Set all = searchParameters.getAllAttributes(); + // if ((all == null) || (all.size() == 0)) + // { + // Collection contentAttributes = + // dictionaryService.getAllProperties(null); + // BooleanQuery query = new BooleanQuery(); + // for (QName qname : contentAttributes) + // { + // // The super implementation will create phrase queries etc if + // required + // Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), + // queryText, analysisMode, luceneFunction); + // if (part != null) + // { + // query.add(part, Occur.SHOULD); + // } + // else + // { + // query.add(createNoMatchQuery(), Occur.SHOULD); + // } + // } + // return query; + // } + // else + // { + // BooleanQuery query = new BooleanQuery(); + // for (String fieldName : all) + // { + // Query part = getFieldQuery(fieldName, queryText, analysisMode, + // luceneFunction); + // if (part != null) + // { + // query.add(part, Occur.SHOULD); + // } + // else + // { + // query.add(createNoMatchQuery(), Occur.SHOULD); + // } + // } + // return query; + // } + throw new UnsupportedOperationException(); + } + + protected Query createAspectQuery(String queryText, boolean exactOnly) + { + AspectDefinition target = QueryParserUtils.matchAspectDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (target == null) + { + return new TermQuery(new Term(FIELD_ASPECT, "_unknown_")); + } + + if (exactOnly) + { + QName targetQName = target.getName(); + TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, targetQName.toString())); + + return termQuery; + } + else + { + Collection subclasses = dictionaryService.getSubAspects(target.getName(), true); + + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + for (QName qname : subclasses) + { + AspectDefinition current = dictionaryService.getAspect(qname); + if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) + { + TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, qname.toString())); + if (termQuery != null) + { + booleanQuery.add(termQuery, Occur.SHOULD); + } + } + } + return booleanQuery.build(); + } + + } + + protected Query createTypeQuery(String queryText, boolean exactOnly) throws ParseException + { + TypeDefinition target = QueryParserUtils.matchTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, queryText); + if (target == null) + { + return new TermQuery(new Term(FIELD_TYPE, "_unknown_")); + } + if (exactOnly) + { + QName targetQName = target.getName(); + TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, targetQName.toString())); + return termQuery; + } + else + { + Collection subclasses = dictionaryService.getSubTypes(target.getName(), true); + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + for (QName qname : subclasses) + { + TypeDefinition current = dictionaryService.getType(qname); + if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) + { + TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, qname.toString())); + if (termQuery != null) + { + booleanQuery.add(termQuery, Occur.SHOULD); + } + } + } + return booleanQuery.build(); + } + } + + protected Query createInTxIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_INTXID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + protected Query createInAclTxIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_INACLTXID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + @SuppressWarnings("deprecation") + protected Query createTransactionQuery(String queryText) + { + return createTermQuery(FIELD_TX, queryText); + } + + protected Query createIsNodeQuery(String queryText) + { + return createTermQuery(FIELD_ISNODE, queryText); + } + + protected Query createIsContainerQuery(String queryText) + { + return createTermQuery(FIELD_ISCONTAINER, queryText); + } + + protected Query createIsRootQuery(String queryText) + { + return createTermQuery(FIELD_ISROOT, queryText); + } + + protected Query createTermQuery(String field, String queryText) + { + TermQuery termQuery = new TermQuery(new Term(field, queryText)); + return termQuery; + } + + protected Query createPrimaryParentQuery(String queryText) + { + return createNodeRefQuery(FIELD_PRIMARYPARENT, queryText); + } + + protected Query createParentQuery(String queryText) + { + return createNodeRefQuery(FIELD_PARENT, queryText); + } + + protected Query createNodeRefQuery(String field, String queryText) + { + if (tenantService.isTenantUser() && (queryText.contains(StoreRef.URI_FILLER))) + { + // assume NodeRef, since it contains StorRef URI filler + queryText = tenantService.getName(new NodeRef(queryText)).toString(); + } + return createTermQuery(field, queryText); + } + + + + /** + * + * Get generic text query + * + * + */ + protected Query createDefaultTextQuery(ThrowingFunction getQuery) throws ParseException + { + + Set text = searchParameters.getTextAttributes(); + if (text == null || text.isEmpty()) + { + text = Stream.of(AlfrescoDefaultTextFields.values()) + .map(t -> PROPERTY_FIELD_PREFIX + t.getFieldName()) + .collect(Collectors.toSet()); + } + + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : text) + { + Query part; + try { + part = getQuery.apply(fieldName); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } catch (UnsupportedOperationException e) { + logger.warn(e); + } + } + return query.build(); + + } + + + protected Query createTextQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return createDefaultTextQuery(textField -> getFieldQuery(textField, queryText, analysisMode, luceneFunction)); + } + + @SuppressWarnings("unchecked") + protected Query getFieldQueryImpl(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException, IOException + { + // make sure the field exists or return a dummy query so we have no + // error ....ACE-3231 + SchemaField schemaField = schema.getFieldOrNull(field); + boolean isNumeric = false; + if (schemaField == null) + { + return new TermQuery(new Term("_dummy_", "_miss_")); + } + else + { + isNumeric = (schemaField.getType().getNumericType() != null); + if (isNumeric) + { + //Check to see if queryText is numeric or else it will fail. + try + { + Double.valueOf(queryText); + } + catch (NumberFormatException e) + { + return new TermQuery(new Term("_dummy_", "_miss_")); + } + } + } + + // Use the analyzer to get all the tokens, and then build a TermQuery, + // PhraseQuery, or noth + + // TODO: Untokenised columns with functions require special handling + + if (luceneFunction != LuceneFunction.FIELD) + { + throw new UnsupportedOperationException( + "Field queries are not supported on lucene functions (UPPER, LOWER, etc)"); + } + + // if the incoming string already has a language identifier we strip it + // iff and addit back on again + + String localePrefix = ""; + + String toTokenise = queryText; + + if (queryText.startsWith("{")) + { + int position = queryText.indexOf("}"); + if (position > 0) + { + String language = queryText.substring(0, position + 1); + Locale locale = new Locale(queryText.substring(1, position)); + String token = queryText.substring(position + 1); + boolean found = false; + for (Locale current : Locale.getAvailableLocales()) + { + if (current.toString().equalsIgnoreCase(locale.toString())) + { + found = true; + break; + } + } + if (found) + { + localePrefix = language; + toTokenise = token; + } else + { + // toTokenise = token; + } + } + } + + String testText = toTokenise; + boolean requiresMLTokenDuplication = false; + String localeString = null; + if (isPropertyField(field) && (localePrefix.length() == 0)) + { + if ((queryText.length() > 0) && (queryText.charAt(0) == '\u0000')) + { + int position = queryText.indexOf("\u0000", 1); + testText = queryText.substring(position + 1); + requiresMLTokenDuplication = true; + localeString = queryText.substring(1, position); + + } + } + + // find the positions of any escaped * and ? and ignore them + + Set wildcardPoistions = getWildcardPositions(testText); + + TokenStream source = null; + ArrayList list = new ArrayList(); + boolean severalTokensAtSamePosition = false; + PackedTokenAttributeImpl nextToken; + int positionCount = 0; + + try + { + source = getAnalyzer().tokenStream(field, new StringReader(toTokenise)); + source.reset(); + while (source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if (source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if (source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + nextToken = new PackedTokenAttributeImpl(); + nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); + nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if (typeAtt != null) + { + nextToken.setType(typeAtt.type()); + } + if (posIncAtt != null) + { + nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + + list.add(nextToken); + if (nextToken.getPositionIncrement() != 0) + positionCount += nextToken.getPositionIncrement(); + else + severalTokensAtSamePosition = true; + } + } + finally + { + try + { + if (source != null) + { + source.close(); + } + } catch (IOException e) + { + // ignore + } + } + + // add any alpha numeric wildcards that have been missed + // Fixes most stop word and wild card issues + + for (int index = 0; index < testText.length(); index++) + { + char current = testText.charAt(index); + if (((current == '*') || (current == '?')) && wildcardPoistions.contains(index)) + { + StringBuilder pre = new StringBuilder(10); + if (index == 0) + { + // "*" and "?" at the start + + boolean found = false; + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl test = list.get(j); + if ((test.startOffset() <= 0) && (0 < test.endOffset())) + { + found = true; + break; + } + } + if (!found && (list.size() == 0)) + { + // Add new token followed by * not given by the + // tokeniser + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append("", 0, 0); + newToken.setType("ALPHANUM"); + if (requiresMLTokenDuplication) + { + Locale locale = I18NUtil.parseLocale(localeString); + @SuppressWarnings("resource") + MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, MLAnalysisMode.EXACT_LANGUAGE); + Iterator it = duplicator.buildIterator(newToken); + if (it != null) + { + int count = 0; + while (it.hasNext()) + { + list.add(it.next()); + count++; + if (count > 1) + { + severalTokensAtSamePosition = true; + } + } + } + } + // content + else + { + list.add(newToken); + } + } + } + else if (index > 0) + { + // Add * and ? back into any tokens from which it has been + // removed + + boolean tokenFound = false; + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl test = list.get(j); + if ((test.startOffset() <= index) && (index < test.endOffset())) + { + if (requiresMLTokenDuplication) + { + String termText = test.toString(); + int position = termText.indexOf("}"); + String language = termText.substring(0, position + 1); + String token = termText.substring(position + 1); + if (index >= test.startOffset() + token.length()) + { + test.setEmpty(); + test.append(language + token + current); + } + } + else + { + if (index >= test.startOffset() + test.length()) + { + test.setEmpty(); + test.append(test.toString() + current); + } + } + tokenFound = true; + break; + } + } + + if (!tokenFound) + { + for (int i = index - 1; i >= 0; i--) + { + char c = testText.charAt(i); + if (Character.isLetterOrDigit(c)) + { + boolean found = false; + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl test = list.get(j); + if ((test.startOffset() <= i) && (i < test.endOffset())) + { + found = true; + break; + } + } + if (found) + { + break; + } + else + { + pre.insert(0, c); + } + } + else + { + break; + } + } + if (pre.length() > 0) + { + // Add new token followed by * not given by the + // tokeniser + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(pre.toString()); + newToken.setOffset(index - pre.length(), index); + newToken.setType("ALPHANUM"); + if (requiresMLTokenDuplication) + { + Locale locale = I18NUtil.parseLocale(localeString); + @SuppressWarnings("resource") + MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, + MLAnalysisMode.EXACT_LANGUAGE); + Iterator it = duplicator.buildIterator(newToken); + if (it != null) + { + int count = 0; + while (it.hasNext()) + { + list.add(it.next()); + count++; + if (count > 1) + { + severalTokensAtSamePosition = true; + } + } + } + } + // content + else + { + list.add(newToken); + } + } + } + } + + StringBuilder post = new StringBuilder(10); + if (index > 0) + { + for (int i = index + 1; i < testText.length(); i++) + { + char c = testText.charAt(i); + if (Character.isLetterOrDigit(c)) + { + boolean found = false; + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl test = list.get(j); + if ((test.startOffset() <= i) && (i < test.endOffset())) + { + found = true; + break; + } + } + if (found) + { + break; + } + else + { + post.append(c); + } + } + else + { + break; + } + } + if (post.length() > 0) + { + // Add new token followed by * not given by the + // tokeniser + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(post.toString()); + newToken.setOffset(index + 1, index + 1 + post.length()); + newToken.setType("ALPHANUM"); + if (requiresMLTokenDuplication) + { + Locale locale = I18NUtil.parseLocale(localeString); + @SuppressWarnings("resource") + MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, MLAnalysisMode.EXACT_LANGUAGE); + Iterator it = duplicator.buildIterator(newToken); + if (it != null) + { + int count = 0; + while (it.hasNext()) + { + list.add(it.next()); + count++; + if (count > 1) + { + severalTokensAtSamePosition = true; + } + } + } + } + // content + else + { + list.add(newToken); + } + } + } + + } + } + + // Put in real position increments as we treat them correctly + + int curentIncrement = -1; + for (PackedTokenAttributeImpl c : list) + { + if (curentIncrement == -1) + { + curentIncrement = c.getPositionIncrement(); + } else if (c.getPositionIncrement() > 0) + { + curentIncrement = c.getPositionIncrement(); + } else + { + c.setPositionIncrement(curentIncrement); + } + } + + // Fix up position increments for in phrase isolated wildcards + + boolean lastWasWild = false; + for(int i = 0; i < list.size() -1; i++) + { + for(int j = list.get(i).endOffset() + 1; j < list.get(i + 1).startOffset() - 1; j++) + { + if(wildcardPoistions.contains(j)) + { + if(!lastWasWild) + { + list.get(i+1).setPositionIncrement(list.get(i+1).getPositionIncrement() + 1); + } + lastWasWild = true; + } + else + { + lastWasWild = false; + } + } + } + + Collections.sort(list, new Comparator() + { + + public int compare(PackedTokenAttributeImpl o1, PackedTokenAttributeImpl o2) + { + int dif = o1.startOffset() - o2.startOffset(); + return dif; + + } + }); + + // Combined * and ? based strings - should redo the tokeniser + + // Build tokens by position + + LinkedList> tokensByPosition = new LinkedList>(); + LinkedList currentList = null; + int lastStart = 0; + for (PackedTokenAttributeImpl c : list) + { + if (c.startOffset() == lastStart) + { + if (currentList == null) + { + currentList = new LinkedList(); + tokensByPosition.add(currentList); + } + currentList.add(c); + } else + { + currentList = new LinkedList(); + tokensByPosition.add(currentList); + currentList.add(c); + } + lastStart = c.startOffset(); + } + + // Build all the token sequences and see which ones get strung together + + OrderedHashSet> allTokenSequencesSet = new OrderedHashSet>(); + for (LinkedList tokensAtPosition : tokensByPosition) + { + OrderedHashSet> positionalSynonymSequencesSet = new OrderedHashSet>(); + + OrderedHashSet> newAllTokenSequencesSet = new OrderedHashSet>(); + + FOR_FIRST_TOKEN_AT_POSITION_ONLY: for (PackedTokenAttributeImpl t : tokensAtPosition) + { + PackedTokenAttributeImpl replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(t); + replace.setOffset(t.startOffset(), t.endOffset()); + replace.setType(t.type()); + replace.setPositionIncrement(t.getPositionIncrement()); + + boolean tokenFoundSequence = false; + for (LinkedList tokenSequence : allTokenSequencesSet) + { + LinkedList newEntry = new LinkedList(); + newEntry.addAll(tokenSequence); + if ((newEntry.getLast().endOffset() == replace.endOffset()) + && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) + { + if ((newEntry.getLast().startOffset() == replace.startOffset()) + && newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM)) + { + positionalSynonymSequencesSet.add(tokenSequence); + newEntry.add(replace); + tokenFoundSequence = true; + } + else if (newEntry.getLast().type().equals(CommonGramsFilter.GRAM_TYPE)) + { + if (newEntry.toString().endsWith(replace.toString())) + { + // already in the gram + positionalSynonymSequencesSet.add(tokenSequence); + tokenFoundSequence = true; + } + else + { + // need to replace the synonym in the current + // gram + tokenFoundSequence = true; + StringBuffer old = new StringBuffer(newEntry.getLast().toString()); + old.replace(replace.startOffset() - newEntry.getLast().startOffset(), + replace.endOffset() - newEntry.getLast().startOffset(), replace.toString()); + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(old.toString()); + newToken.setOffset(newEntry.getLast().startOffset(), newEntry.getLast().endOffset()); + newEntry.removeLast(); + newEntry.add(newToken); + } + } + } + else if ((newEntry.getLast().startOffset() < replace.startOffset()) + && (newEntry.getLast().endOffset() < replace.endOffset())) + { + if (newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM) + && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) + { + positionalSynonymSequencesSet.add(tokenSequence); + } + newEntry.add(replace); + tokenFoundSequence = true; + } + newAllTokenSequencesSet.add(newEntry); + } + if (false == tokenFoundSequence) + { + for (LinkedList tokenSequence : newAllTokenSequencesSet) + { + LinkedList newEntry = new LinkedList(); + newEntry.addAll(tokenSequence); + if ((newEntry.getLast().endOffset() == replace.endOffset()) + && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) + { + if ((newEntry.getLast().startOffset() == replace.startOffset()) + && newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM)) + { + positionalSynonymSequencesSet.add(tokenSequence); + newEntry.add(replace); + tokenFoundSequence = true; + } else if (newEntry.getLast().type().equals(CommonGramsFilter.GRAM_TYPE)) + { + if (newEntry.toString().endsWith(replace.toString())) + { + // already in the gram + positionalSynonymSequencesSet.add(tokenSequence); + tokenFoundSequence = true; + } else + { + // need to replace the synonym in the + // current gram + tokenFoundSequence = true; + StringBuffer old = new StringBuffer(newEntry.getLast().toString()); + old.replace(replace.startOffset() - newEntry.getLast().startOffset(), + replace.endOffset() - newEntry.getLast().startOffset(), replace.toString()); + PackedTokenAttributeImpl newToken = new PackedTokenAttributeImpl(); + newToken.setEmpty().append(old.toString()); + newToken.setOffset(newEntry.getLast().startOffset(), + newEntry.getLast().endOffset()); + newEntry.removeLast(); + newEntry.add(newToken); + positionalSynonymSequencesSet.add(newEntry); + } + } + } else if ((newEntry.getLast().startOffset() < replace.startOffset()) + && (newEntry.getLast().endOffset() < replace.endOffset())) + { + if (newEntry.getLast().type().equals(SynonymFilter.TYPE_SYNONYM) + && replace.type().equals(SynonymFilter.TYPE_SYNONYM)) + { + positionalSynonymSequencesSet.add(tokenSequence); + newEntry.add(replace); + tokenFoundSequence = true; + } + } + } + } + if (false == tokenFoundSequence) + { + LinkedList newEntry = new LinkedList(); + newEntry.add(replace); + newAllTokenSequencesSet.add(newEntry); + } + // Limit the max number of permutations we consider + if (newAllTokenSequencesSet.size() > 64) + { + break FOR_FIRST_TOKEN_AT_POSITION_ONLY; + } + } + allTokenSequencesSet = newAllTokenSequencesSet; + allTokenSequencesSet.addAll(positionalSynonymSequencesSet); + + } + + LinkedList> allTokenSequences = new LinkedList>( + allTokenSequencesSet); + + // build the unique + + LinkedList> fixedTokenSequences = new LinkedList>(); + for (LinkedList tokenSequence : allTokenSequences) + { + LinkedList fixedTokenSequence = new LinkedList(); + fixedTokenSequences.add(fixedTokenSequence); + PackedTokenAttributeImpl replace = null; + for (PackedTokenAttributeImpl c : tokenSequence) + { + if (replace == null) + { + StringBuilder prefix = new StringBuilder(); + for (int i = c.startOffset() - 1; i >= 0; i--) + { + char test = testText.charAt(i); + if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) + { + prefix.insert(0, test); + } else + { + break; + } + } + String pre = prefix.toString(); + if (requiresMLTokenDuplication) + { + String termText = c.toString(); + int position = termText.indexOf("}"); + String language = termText.substring(0, position + 1); + String token = termText.substring(position + 1); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(language + pre + token); + replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); + replace.setType(c.type()); + replace.setPositionIncrement(c.getPositionIncrement()); + } else + { + String termText = c.toString(); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(pre + termText); + replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); + replace.setType(c.type()); + replace.setPositionIncrement(c.getPositionIncrement()); + } + } else + { + StringBuilder prefix = new StringBuilder(); + StringBuilder postfix = new StringBuilder(); + StringBuilder builder = prefix; + for (int i = c.startOffset() - 1; i >= replace.endOffset(); i--) + { + char test = testText.charAt(i); + if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) + { + builder.insert(0, test); + } else + { + builder = postfix; + postfix.setLength(0); + } + } + String pre = prefix.toString(); + String post = postfix.toString(); + + // Does it bridge? + if ((pre.length() > 0) && (replace.endOffset() + pre.length()) == c.startOffset()) + { + String termText = c.toString(); + if (requiresMLTokenDuplication) + { + int position = termText.indexOf("}"); + @SuppressWarnings("unused") + String language = termText.substring(0, position + 1); + String token = termText.substring(position + 1); + int oldPositionIncrement = replace.getPositionIncrement(); + String replaceTermText = replace.toString(); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(replaceTermText + pre + token); + replace.setOffset(replace.startOffset(), c.endOffset()); + replace.setType(replace.type()); + replace.setPositionIncrement(oldPositionIncrement); + } else + { + int oldPositionIncrement = replace.getPositionIncrement(); + String replaceTermText = replace.toString(); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(replaceTermText + pre + termText); + replace.setOffset(replace.startOffset(), c.endOffset()); + replace.setType(replace.type()); + replace.setPositionIncrement(oldPositionIncrement); + } + } else + { + String termText = c.toString(); + if (requiresMLTokenDuplication) + { + int position = termText.indexOf("}"); + String language = termText.substring(0, position + 1); + String token = termText.substring(position + 1); + String replaceTermText = replace.toString(); + PackedTokenAttributeImpl last = new PackedTokenAttributeImpl(); + last.setEmpty().append(replaceTermText + post); + last.setOffset(replace.startOffset(), replace.endOffset() + post.length()); + last.setType(replace.type()); + last.setPositionIncrement(replace.getPositionIncrement()); + fixedTokenSequence.add(last); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(language + pre + token); + replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); + replace.setType(c.type()); + replace.setPositionIncrement(c.getPositionIncrement()); + } else + { + String replaceTermText = replace.toString(); + PackedTokenAttributeImpl last = new PackedTokenAttributeImpl(); + last.setEmpty().append(replaceTermText + post); + last.setOffset(replace.startOffset(), replace.endOffset() + post.length()); + last.setType(replace.type()); + last.setPositionIncrement(replace.getPositionIncrement()); + fixedTokenSequence.add(last); + replace = new PackedTokenAttributeImpl(); + replace.setEmpty().append(pre + termText); + replace.setOffset(c.startOffset() - pre.length(), c.endOffset()); + replace.setType(c.type()); + replace.setPositionIncrement(c.getPositionIncrement()); + } + } + } + } + // finish last + if (replace != null) + { + StringBuilder postfix = new StringBuilder(); + if ((replace.endOffset() >= 0) && (replace.endOffset() < testText.length())) + { + for (int i = replace.endOffset(); i < testText.length(); i++) + { + char test = testText.charAt(i); + if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) + { + postfix.append(test); + } else + { + break; + } + } + } + String post = postfix.toString(); + int oldPositionIncrement = replace.getPositionIncrement(); + String replaceTermText = replace.toString(); + PackedTokenAttributeImpl terminal = new PackedTokenAttributeImpl(); + terminal.setEmpty().append(replaceTermText + post); + terminal.setOffset(replace.startOffset(), replace.endOffset() + post.length()); + terminal.setType(replace.type()); + terminal.setPositionIncrement(oldPositionIncrement); + fixedTokenSequence.add(terminal); + } + } + + // rebuild fixed list + + ArrayList fixed = new ArrayList(); + for (LinkedList tokenSequence : fixedTokenSequences) + { + for (PackedTokenAttributeImpl token : tokenSequence) + { + fixed.add(token); + } + } + + // reorder by start position and increment + + Collections.sort(fixed, new Comparator() + { + + public int compare(PackedTokenAttributeImpl o1, PackedTokenAttributeImpl o2) + { + int dif = o1.startOffset() - o2.startOffset(); + if (dif != 0) + { + return dif; + } else + { + return o1.getPositionIncrement() - o2.getPositionIncrement(); + } + } + }); + + // make sure we remove any tokens we have duplicated + + @SuppressWarnings("rawtypes") + OrderedHashSet unique = new OrderedHashSet(); + unique.addAll(fixed); + fixed = new ArrayList(unique); + + list = fixed; + + // add any missing locales back to the tokens + + if (localePrefix.length() > 0) + { + for (int j = 0; j < list.size(); j++) + { + PackedTokenAttributeImpl currentToken = list.get(j); + String termText = currentToken.toString(); + currentToken.setEmpty(); + currentToken.append(localePrefix + termText); + } + } + + SchemaField sf = schema.getField(field); + + boolean isShingled = false; + @SuppressWarnings("resource") + TokenizerChain tokenizerChain = (sf.getType().getQueryAnalyzer() instanceof TokenizerChain) + ? ((TokenizerChain) sf.getType().getQueryAnalyzer()) : null; + if (tokenizerChain != null) + { + for (TokenFilterFactory factory : tokenizerChain.getTokenFilterFactories()) + { + if (factory instanceof ShingleFilterFactory) + { + isShingled = true; + break; + } + } + } + @SuppressWarnings("resource") + AlfrescoAnalyzerWrapper analyzerWrapper = (sf.getType().getQueryAnalyzer() instanceof AlfrescoAnalyzerWrapper) + ? ((AlfrescoAnalyzerWrapper) sf.getType().getQueryAnalyzer()) : null; + if (analyzerWrapper != null) + { + // assume if there are no term positions it is shingled .... + isShingled = true; + } + + boolean forceConjuncion = (rerankPhase == RerankPhase.QUERY_PHASE && !isExactTermSearch(analysisMode)); + if (list.size() == 0) + { + return null; + } + else if (list.size() == 1) + { + nextToken = list.get(0); + String termText = nextToken.toString(); + if (!isNumeric && (termText.contains("*") || termText.contains("?"))) + { + return newWildcardQuery(new Term(field, termText)); + } + else + { + return newTermQuery(new Term(field, termText)); + } + } + else + { + if (severalTokensAtSamePosition) + { + if (positionCount == 1) + { + // no phrase query: + Builder q = newBooleanQuery(); + for (int i = 0; i < list.size(); i++) + { + Query currentQuery; + nextToken = list.get(i); + String termText = nextToken.toString(); + if (termText.contains("*") || termText.contains("?")) + { + currentQuery = newWildcardQuery(new Term(field, termText)); + } else + { + currentQuery = newTermQuery(new Term(field, termText)); + } + q.add(currentQuery, BooleanClause.Occur.SHOULD); + } + return q.build(); + } + else if (forceConjuncion) + { + BooleanQuery.Builder or = new BooleanQuery.Builder(); + + for (LinkedList tokenSequence : fixedTokenSequences) + { + BooleanQuery.Builder and = new BooleanQuery.Builder(); + for (int i = 0; i < tokenSequence.size(); i++) + { + nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); + String termText = nextToken.toString(); + + Term term = new Term(field, termText); + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + and.add(wildQuery, Occur.MUST); + } else + { + TermQuery termQuery = new TermQuery(term); + and.add(termQuery, Occur.MUST); + } + } + if (and.build().clauses().size() > 0) + { + or.add(and.build(), Occur.SHOULD); + } + } + return or.build(); + } + // shingle + else if (sf.omitPositions() && isShingled) + { + + ArrayList nonContained = getNonContained(list); + Query currentQuery; + + BooleanQuery.Builder weakPhrase = new BooleanQuery.Builder(); + for (PackedTokenAttributeImpl shingleToken : nonContained) + { + String termText = shingleToken.toString(); + Term term = new Term(field, termText); + + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + currentQuery = new org.apache.lucene.search.WildcardQuery(term); + } else + { + currentQuery = new TermQuery(term); + } + weakPhrase.add(currentQuery, Occur.MUST); + } + + return weakPhrase.build(); + + } + // Word delimiter factory and other odd things generate complex + // token patterns + // Smart skip token sequences with small tokens that generate + // toomany wildcards + // Fall back to the larger pattern + // e.g Site1* will not do (S ite 1*) or (Site 1*) if 1* matches + // too much (S ite1*) and (Site1*) will still be OK + // If we skip all (for just 1* in the input) this is still an + // issue. + else + { + + return generateSpanOrQuery(field, fixedTokenSequences); + + } + } + else + { + if (forceConjuncion) + { + BooleanQuery.Builder or = new BooleanQuery.Builder(); + + for (LinkedList tokenSequence : fixedTokenSequences) + { + BooleanQuery.Builder and = new BooleanQuery.Builder(); + for (int i = 0; i < tokenSequence.size(); i++) + { + nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); + String termText = nextToken.toString(); + + Term term = new Term(field, termText); + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + and.add(wildQuery, Occur.MUST); + } + else + { + TermQuery termQuery = new TermQuery(term); + and.add(termQuery, Occur.MUST); + } + } + if (and.build().clauses().size() > 0) + { + or.add(and.build(), Occur.SHOULD); + } + } + return or.build(); + } + else + { + SpanQuery spanQuery = null; + ArrayList atSamePositionSpanOrQueryParts = new ArrayList(); + int gap = 0; + for (int i = 0; i < list.size(); i++) + { + nextToken = list.get(i); + String termText = nextToken.toString(); + Term term = new Term(field, termText); + if (getEnablePositionIncrements()) + { + SpanQuery nextSpanQuery; + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( + wildQuery); + wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); + nextSpanQuery = wrapper; + } + else + { + nextSpanQuery = new SpanTermQuery(term); + } + if (gap == 0) + { + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } + else + { + if (atSamePositionSpanOrQueryParts.size() == 0) + { + if (spanQuery == null) + { + spanQuery = nextSpanQuery; + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, nextSpanQuery }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + } + else if (atSamePositionSpanOrQueryParts.size() == 1) + { + if (spanQuery == null) + { + spanQuery = atSamePositionSpanOrQueryParts.get(0); + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, + internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } + else + { + if (spanQuery == null) + { + spanQuery = new SpanOrQuery( + atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})); + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, + new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})) }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } + } + gap = nextToken.getPositionIncrement(); + } + else + { + SpanQuery nextSpanQuery; + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( + wildQuery); + wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); + nextSpanQuery = wrapper; + } + else + { + nextSpanQuery = new SpanTermQuery(term); + } + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(nextSpanQuery); + } + else + { + spanQuery = new SpanOrQuery(spanQuery, nextSpanQuery); + } + } + } + if (atSamePositionSpanOrQueryParts.size() == 0) + { + return spanQuery; + } + else if (atSamePositionSpanOrQueryParts.size() == 1) + { + if (spanQuery == null) + { + spanQuery = atSamePositionSpanOrQueryParts.get(0); + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, + internalSlop < 2); + } + return spanQuery; + } + else + { + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})); + } + else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})) }, (gap - 1) + internalSlop, internalSlop < 2); + } + return spanQuery; + } + } + } + } + } + + /** + * @param list + * @return + */ + private ArrayList getNonContained(ArrayList list) + { + ArrayList nonContained = new ArrayList(); + NEXT_CANDIDATE: for (PackedTokenAttributeImpl candidate : list) + { + NEXT_TEST: for (PackedTokenAttributeImpl test : list) + { + if (candidate == test) + { + continue NEXT_TEST; + } else if ((test.startOffset() == candidate.startOffset()) + && (candidate.endOffset() == test.endOffset()) + && (test.toString().equals(candidate.toString()))) + { + continue NEXT_TEST; + } else if ((test.startOffset() <= candidate.startOffset()) + && (candidate.endOffset() <= test.endOffset()) + && (test.toString().contains(candidate.toString()))) + { + continue NEXT_CANDIDATE; + } + } + nonContained.add(candidate); + } + return nonContained; + } + + /** + * @param field + * @param fixedTokenSequences + * LinkedList> + * @return Query + */ + protected SpanQuery generateSpanOrQuery(String field, + LinkedList> fixedTokenSequences) + { + PackedTokenAttributeImpl nextToken; + ArrayList spanOrQueryParts = new ArrayList(); + + for (LinkedList tokenSequence : fixedTokenSequences) + { + int gap = 1; + SpanQuery spanQuery = null; + ArrayList atSamePositionSpanOrQueryParts = new ArrayList(); + + // MNT-13239: if all tokens's positions are incremented by one then + // create flat nearQuery + if (getEnablePositionIncrements() && isAllTokensSequentiallyShifted(tokenSequence)) + { + // there will be no tokens at same position + List wildWrappedList = new ArrayList(tokenSequence.size()); + for (PackedTokenAttributeImpl token : tokenSequence) + { + String termText = token.toString(); + Term term = new Term(field, termText); + SpanQuery nextSpanQuery = wrapWildcardTerms(term); + wildWrappedList.add(nextSpanQuery); + } + if (wildWrappedList.size() == 1) + { + spanQuery = wildWrappedList.get(0); + } else + { + spanQuery = new SpanNearQuery(wildWrappedList.toArray(new SpanQuery[wildWrappedList.size()]), 0, + true); + } + } else + { + for (int i = 0; i < tokenSequence.size(); i++) + { + nextToken = (PackedTokenAttributeImpl) tokenSequence.get(i); + String termText = nextToken.toString(); + + Term term = new Term(field, termText); + + if (getEnablePositionIncrements()) + { + SpanQuery nextSpanQuery = wrapWildcardTerms(term); + if (gap == 0) + { + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } else + { + if (atSamePositionSpanOrQueryParts.size() == 0) + { + if (spanQuery == null) + { + spanQuery = nextSpanQuery; + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, nextSpanQuery }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + } else if (atSamePositionSpanOrQueryParts.size() == 1) + { + if (spanQuery == null) + { + spanQuery = atSamePositionSpanOrQueryParts.get(0); + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, + internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } else + { + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})); + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, spanQuery = new SpanOrQuery( + atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})) }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + atSamePositionSpanOrQueryParts.add(nextSpanQuery); + } + } + gap = nextToken.getPositionIncrement(); + + } else + { + SpanQuery nextSpanQuery; + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery( + term); + SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( + wildQuery); + wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); + nextSpanQuery = wrapper; + } else + { + nextSpanQuery = new SpanTermQuery(term); + } + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(nextSpanQuery); + } else + { + spanQuery = new SpanOrQuery(spanQuery, nextSpanQuery); + } + } + } + } + + if (atSamePositionSpanOrQueryParts.size() == 0) + { + spanOrQueryParts.add(spanQuery); + } else if (atSamePositionSpanOrQueryParts.size() == 1) + { + if (spanQuery == null) + { + spanQuery = atSamePositionSpanOrQueryParts.get(0); + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, atSamePositionSpanOrQueryParts.get(0) }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + spanOrQueryParts.add(spanQuery); + } else + { + if (spanQuery == null) + { + spanQuery = new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})); + } else + { + spanQuery = new SpanNearQuery(new SpanQuery[] + { spanQuery, new SpanOrQuery(atSamePositionSpanOrQueryParts.toArray(new SpanQuery[] + {})) }, (gap - 1) + internalSlop, internalSlop < 2); + } + atSamePositionSpanOrQueryParts = new ArrayList(); + spanOrQueryParts.add(spanQuery); + } + } + if (spanOrQueryParts.size() == 1) + { + return spanOrQueryParts.get(0); + } else + { + return new SpanOrQuery(spanOrQueryParts.toArray(new SpanQuery[] + {})); + } + } + + private SpanQuery wrapWildcardTerms(org.apache.lucene.index.Term term) + { + String termText = term.text(); + SpanQuery nextSpanQuery; + if ((termText != null) && (termText.contains("*") || termText.contains("?"))) + { + org.apache.lucene.search.WildcardQuery wildQuery = new org.apache.lucene.search.WildcardQuery(term); + SpanMultiTermQueryWrapper wrapper = new SpanMultiTermQueryWrapper( + wildQuery); + wrapper.setRewriteMethod(new TopTermsSpanBooleanQueryRewrite(topTermSpanRewriteLimit)); + nextSpanQuery = wrapper; + } else + { + nextSpanQuery = new SpanTermQuery(term); + } + return nextSpanQuery; + } + + protected boolean isAllTokensSequentiallyShifted(List tokenSequence) + { + if (0 == internalSlop) + { + // check if all tokens have gap = 1 + for (PackedTokenAttributeImpl tokenToCheck : tokenSequence) + { + if (1 != tokenToCheck.getPositionIncrement()) + { + return false; + } + } + return true; + } + return false; + } + + private Set getWildcardPositions(String string) + { + HashSet wildcardPositions = new HashSet(); + + boolean lastWasEscape = false; + + for (int i = 0; i < string.length(); i++) + { + char c = string.charAt(i); + if (lastWasEscape) + { + lastWasEscape = false; + } else + { + if (c == '\\') + { + lastWasEscape = true; + } else if (c == '*') + { + wildcardPositions.add(i); + } else if (c == '?') + { + wildcardPositions.add(i); + } + } + } + + return wildcardPositions; + } + + /** + * @exception ParseException + * throw in overridden method to disallow + */ + + protected Query getRangeQuery(String field, String part1, String part2, boolean startInclusive, + boolean endInclusive) throws ParseException + { + return getRangeQuery(field, part1, part2, startInclusive, endInclusive, AnalysisMode.IDENTIFIER, + LuceneFunction.FIELD); + } + + protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException + { + return getRangeQuery(field, part1, part2, inclusive, inclusive, AnalysisMode.IDENTIFIER, LuceneFunction.FIELD); + } + + /** + * @param field + * @param part1 + * @param part2 + * @param includeLower + * @param includeUpper + * @param analysisMode + * @param luceneFunction + * @return the query + * @exception ParseException + * throw in overridden method to disallow + */ + public Query getRangeQuery(String field, String part1, String part2, boolean includeLower, boolean includeUpper, + AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATH); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + + return createDefaultTextQuery(textField -> getRangeQuery(textField, part1, part2, includeLower, + includeUpper, analysisMode, luceneFunction)); + + + } else if (field.equals(FIELD_CASCADETX)) + { + SchemaField sf = schema.getField(FIELD_CASCADETX); + if (sf != null) + { + String start = null; + try + { + analyzeMultitermTerm(FIELD_CASCADETX, part1); + start = part1; + } catch (Exception e) + { + + } + String end = null; + try + { + analyzeMultitermTerm(FIELD_CASCADETX, part2); + end = part2; + } catch (Exception e) + { + + } + return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); + } else + { + throw new UnsupportedOperationException(); + } + } + // FIELD_ID uses the default + // FIELD_DBID uses the default + // FIELD_ISROOT uses the default + // FIELD_ISCONTAINER uses the default + // FIELD_ISNODE uses the default + // FIELD_TX uses the default + // FIELD_PARENT uses the default + // FIELD_PRIMARYPARENT uses the default + // FIELD_QNAME uses the default + // FIELD_PRIMARYASSOCTYPEQNAME uses the default + // FIELD_ASSOCTYPEQNAME uses the default + // FIELD_CLASS uses the default + // FIELD_TYPE uses the default + // FIELD_EXACTTYPE uses the default + // FIELD_ASPECT uses the default + // FIELD_EXACTASPECT uses the default + // FIELD_TYPE uses the default + // FIELD_TYPE uses the default + if (isPropertyField(field)) + { + Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(field); + + String expandedFieldName = null; + PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, fieldNameAndEnding.getFirst()); + IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; + if (propertyDef != null) + { + tokenisationMode = propertyDef.getIndexTokenisationMode(); + if (tokenisationMode == null) + { + tokenisationMode = IndexTokenisationMode.TRUE; + } + } else + { + expandedFieldName = expandAttributeFieldName(field); + } + + if (propertyDef != null) + { + // LOWER AND UPPER + if (luceneFunction != LuceneFunction.FIELD) + { + if (luceneFunction == LuceneFunction.LOWER) + { + if ((false == part1.toLowerCase().equals(part1)) + || (false == part2.toLowerCase().equals(part2))) + { + return createNoMatchQuery(); + } + } + + if (luceneFunction == LuceneFunction.UPPER) + { + if ((false == part1.toUpperCase().equals(part1)) + || (false == part2.toUpperCase().equals(part2))) + { + return createNoMatchQuery(); + } + } + + if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + addLocaleSpecificUntokenisedTextRangeFunction(expandedFieldName, propertyDef, part1, part2, + includeLower, includeUpper, luceneFunction, booleanQuery, locale, tokenisationMode); + } + return booleanQuery.build(); + } else + { + throw new UnsupportedOperationException("Lucene Function"); + } + } + + if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) + { + return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, analysisMode, + expandedFieldName, propertyDef, tokenisationMode); + } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) + { + String solrField = null; + switch (fieldNameAndEnding.getSecond()) + { + case FIELD_SIZE_SUFFIX: + solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_SIZE, FieldUse.ID) + .getFields().get(0).getField(); + break; + case FIELD_MIMETYPE_SUFFIX: + solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_MIMETYPE, FieldUse.ID) + .getFields().get(0).getField(); + break; + case FIELD_ENCODING_SUFFIX: + solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_ENCODING, FieldUse.ID) + .getFields().get(0).getField(); + break; + case FIELD_LOCALE_SUFFIX: + solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), SpecializedFieldType.CONTENT_LOCALE, FieldUse.ID) + .getFields().get(0).getField(); + break; + } + if (solrField != null) + { + String start = null; + try + { + analyzeMultitermTerm(solrField, part1); + start = part1; + } catch (Exception e) + { + + } + String end = null; + try + { + analyzeMultitermTerm(solrField, part2); + end = part2; + } catch (Exception e) + { + + } + + SchemaField sf = schema.getField(solrField); + return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); + } else + { + return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, + analysisMode, expandedFieldName, propertyDef, tokenisationMode); + } + } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) + { + return buildTextMLTextOrContentRange(field, part1, part2, includeLower, includeUpper, analysisMode, + expandedFieldName, propertyDef, tokenisationMode); + } else if ((propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) + || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE)) && + !AlfrescoSolrDataModel.getInstance().isDerivedDateField(fieldNameAndEnding.getSecond())) + { + Pair dateAndResolution1 = parseDateString(part1); + Pair dateAndResolution2 = parseDateString(part2); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + IndexedField indexedField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), null, FieldUse.ID); + for (FieldInstance instance : indexedField.getFields()) + { + String start = dateAndResolution1 == null ? part1 + : (includeLower ? getDateStart(dateAndResolution1) : getDateEnd(dateAndResolution1)); + String end = dateAndResolution2 == null ? part2 + : (includeUpper ? getDateEnd(dateAndResolution2) : getDateStart(dateAndResolution2)); + if (start.equals("*")) + { + start = null; + } + if (end.equals("*")) + { + end = null; + } + + SchemaField sf = schema.getField(instance.getField()); + + Query query = sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); + if (query != null) + { + bQuery.add(query, Occur.SHOULD); + } + } + return bQuery.build(); + } + else + { + String solrField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), + AlfrescoSolrDataModel.getInstance().getTextField(fieldNameAndEnding.getSecond()), + FieldUse.ID) + .getFields().get(0).getField(); + + String start = null; + try + { + analyzeMultitermTerm(solrField, part1); + start = part1; + } catch (Exception e) + { + + } + String end = null; + try + { + analyzeMultitermTerm(solrField, part2); + end = part2; + } catch (Exception e) + { + + } + + SchemaField sf = schema.getField(solrField); + return sf.getType().getRangeQuery(null, sf, start, end, includeLower, includeUpper); + } + } else + { + throw new UnsupportedOperationException(); + } + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, + includeUpper, analysisMode, luceneFunction); + query.add(part, Occur.SHOULD); + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getRangeQuery(fieldName, part1, part2, includeLower, includeUpper, analysisMode, + luceneFunction); + query.add(part, Occur.SHOULD); + } + return query.build(); + } + + } + // FIELD_ISUNSET uses the default + // FIELD_ISNULL uses the default + // FIELD_ISNOTNULL uses the default + // FIELD_EXISTS uses the default + else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, + includeUpper, analysisMode, luceneFunction); + query.add(part, Occur.SHOULD); + } + return query.build(); + } + // FIELD_FTSSTATUS uses the default + if (field.equals(FIELD_TAG)) + { + throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_TAG); + } else + { + // None property - leave alone + throw new UnsupportedOperationException(); + } + } + + /** + * @param field + * @param part1 + * @param part2 + * @param includeLower + * @param includeUpper + * @param analysisMode + * @param expandedFieldName + * @param propertyDef + * @param tokenisationMode + * @return + * @throws ParseException + */ + private Query buildTextMLTextOrContentRange(String field, String part1, String part2, boolean includeLower, + boolean includeUpper, AnalysisMode analysisMode, String expandedFieldName, PropertyDefinition propertyDef, + IndexTokenisationMode tokenisationMode) throws ParseException + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + + try + { + addTextRange(field, propertyDef, part1, part2, includeLower, includeUpper, analysisMode, + expandedFieldName, propertyDef, tokenisationMode, booleanQuery, locale); + } catch (IOException e) + { + throw new ParseException( + "Failed to tokenise: <" + part1 + "> or <" + part2 + "> for " + propertyDef.getName()); + } + + } + return booleanQuery.build(); + } + + private String expandAttributeFieldName(String field) + { + return PROPERTY_FIELD_PREFIX + QueryParserUtils.expandQName(searchParameters.getNamespace(), + namespacePrefixResolver, field.substring(1)); + } + + // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) + @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") + protected String getToken(String field, String value, AnalysisMode analysisMode) throws ParseException + { + try (TokenStream source = getAnalyzer().tokenStream(field, new StringReader(value))) + { + String tokenised = null; + + while (source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if (source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if (source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + PackedTokenAttributeImpl token = new PackedTokenAttributeImpl(); + token.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); + token.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if (typeAtt != null) + { + token.setType(typeAtt.type()); + } + if (posIncAtt != null) + { + token.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + + tokenised = token.toString(); + } + return tokenised; + } catch (IOException e) + { + throw new ParseException("IO" + e.getMessage()); + } + + } + + @Override + public Query getPrefixQuery(String field, String termStr) throws ParseException + { + return getPrefixQuery(field, termStr, AnalysisMode.PREFIX); + } + + @SuppressWarnings("deprecation") + public Query getPrefixQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATH); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + + return createDefaultTextQuery(textField -> getPrefixQuery(textField, termStr, analysisMode)); + + } else if (field.equals(FIELD_ID)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getPrefixQuery(FIELD_LID, termStr); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) + || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) + || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) + || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getPrefixQuery(field, termStr); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_CLASS)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_CLASS); + } else if (field.equals(FIELD_TYPE)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_TYPE); + } else if (field.equals(FIELD_EXACTTYPE)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_EXACTTYPE); + } else if (field.equals(FIELD_ASPECT)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_ASPECT); + } else if (field.equals(FIELD_EXACTASPECT)) + { + return super.getPrefixQuery(field, termStr); + // throw new UnsupportedOperationException("Prefix Queries are not + // support for "+FIELD_EXACTASPECT); + } else if (isPropertyField(field)) + { + return attributeQueryBuilder(field, termStr, new PrefixQuery(), analysisMode, LuceneFunction.FIELD); + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc + // if required + Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getPrefixQuery(fieldName, termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } + } else if (field.equals(FIELD_ISUNSET)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISUNSET); + } else if (field.equals(FIELD_ISNULL)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNULL); + } else if (field.equals(FIELD_ISNOTNULL)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNOTNULL); + } else if (field.equals(FIELD_EXISTS)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_EXISTS); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc if + // required + Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else if (field.equals(FIELD_FTSSTATUS)) + { + throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_FTSSTATUS); + } else if (field.equals(FIELD_TAG)) + { + return super.getPrefixQuery(field, termStr); + } else if (field.equals(FIELD_SITE)) + { + return super.getPrefixQuery(field, termStr); + } else if (field.equals(FIELD_NPATH)) + { + return super.getPrefixQuery(field, termStr); + } else if (field.equals(FIELD_PNAME)) + { + return super.getPrefixQuery(field, termStr); + } else + { + return super.getPrefixQuery(field, termStr); + } + } + + @Override + public Query getWildcardQuery(String field, String termStr) throws ParseException + { + return getWildcardQuery(field, termStr, AnalysisMode.WILD); + } + + @SuppressWarnings("deprecation") + public Query getWildcardQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_PATH); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + + return createDefaultTextQuery(textField -> getWildcardQuery(textField, termStr, analysisMode)); + + } else if (field.equals(FIELD_ID)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getWildcardQuery(FIELD_LID, termStr); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) + || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) + || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) + || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getWildcardQuery(field, termStr); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_CLASS)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_CLASS); + } else if (field.equals(FIELD_TYPE)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_TYPE); + } else if (field.equals(FIELD_EXACTTYPE)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_EXACTTYPE); + } else if (field.equals(FIELD_ASPECT)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_ASPECT); + } else if (field.equals(FIELD_EXACTASPECT)) + { + return super.getWildcardQuery(field, termStr); + // throw new UnsupportedOperationException("Wildcard Queries are not + // support for "+FIELD_EXACTASPECT); + } else if (isPropertyField(field)) + { + return attributeQueryBuilder(field, termStr, new WildcardQuery(), analysisMode, LuceneFunction.FIELD); + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc + // if required + Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getWildcardQuery(fieldName, termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } + } else if (field.equals(FIELD_ISUNSET)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISUNSET); + } else if (field.equals(FIELD_ISNULL)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNULL); + } else if (field.equals(FIELD_ISNOTNULL)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNOTNULL); + } else if (field.equals(FIELD_EXISTS)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_EXISTS); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc if + // required + Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else if (field.equals(FIELD_FTSSTATUS)) + { + throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_FTSSTATUS); + } else if (field.equals(FIELD_TAG)) + { + return super.getWildcardQuery(field, termStr); + } else if (field.equals(FIELD_SITE)) + { + return super.getWildcardQuery(field, termStr); + } else if (field.equals(FIELD_PNAME)) + { + return super.getWildcardQuery(field, termStr); + } else if (field.equals(FIELD_NPATH)) + { + return super.getWildcardQuery(field, termStr); + } else + { + return super.getWildcardQuery(field, termStr); + } + } + + @SuppressWarnings("deprecation") + @Override + public Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException + { + if (field.equals(FIELD_PATH)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATH); + } else if (field.equals(FIELD_PATHWITHREPEATS)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATHWITHREPEATS); + } else if (field.equals(FIELD_TEXT)) + { + + return createDefaultTextQuery(textField -> getFuzzyQuery(textField, termStr, minSimilarity)); + + } else if (field.equals(FIELD_ID) || field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) + || field.equals(FIELD_ISCONTAINER) || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) + || field.equals(FIELD_PARENT) || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) + || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) + { + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + setLowercaseExpandedTerms(false); + return super.getFuzzyQuery(field, termStr, minSimilarity); + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + } else if (field.equals(FIELD_CLASS)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_CLASS); + } else if (field.equals(FIELD_TYPE)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_TYPE); + } else if (field.equals(FIELD_EXACTTYPE)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTTYPE); + } else if (field.equals(FIELD_ASPECT)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ASPECT); + } else if (field.equals(FIELD_EXACTASPECT)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTASPECT); + } else if (isPropertyField(field)) + { + return attributeQueryBuilder(field, termStr, new FuzzyQuery(minSimilarity), AnalysisMode.FUZZY, + LuceneFunction.FIELD); + } else if (field.equals(FIELD_ALL)) + { + Set all = searchParameters.getAllAttributes(); + if ((all == null) || (all.size() == 0)) + { + Collection contentAttributes = dictionaryService.getAllProperties(null); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc + // if required + Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else + { + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (String fieldName : all) + { + Query part = getFuzzyQuery(fieldName, termStr, minSimilarity); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } + } else if (field.equals(FIELD_ISUNSET)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISUNSET); + } else if (field.equals(FIELD_ISNULL)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNULL); + } else if (field.equals(FIELD_ISNOTNULL)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNOTNULL); + } else if (field.equals(FIELD_EXISTS)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXISTS); + } else if (QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, + dictionaryService, field) != null) + { + Collection contentAttributes = dictionaryService + .getAllProperties(QueryParserUtils.matchDataTypeDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, field).getName()); + BooleanQuery.Builder query = new BooleanQuery.Builder(); + for (QName qname : contentAttributes) + { + // The super implementation will create phrase queries etc if + // required + Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); + if (part != null) + { + query.add(part, Occur.SHOULD); + } else + { + query.add(createNoMatchQuery(), Occur.SHOULD); + } + } + return query.build(); + } else if (field.equals(FIELD_FTSSTATUS)) + { + throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_FTSSTATUS); + } else if (field.equals(FIELD_TAG)) + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } else if (field.equals(FIELD_SITE)) + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } else if (field.equals(FIELD_PNAME)) + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } else if (field.equals(FIELD_NPATH)) + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } else + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } + } + + /** + * @param dictionaryService + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param field + * @param queryText + * @param analysisMode + * @param luceneFunction + * @return the query + * @throws ParseException + */ + public Query getSuperFieldQuery(String field, String queryText, AnalysisMode analysisMode, + LuceneFunction luceneFunction) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(field, queryText, analysisMode, luceneFunction); + } + + /** + * @param field + * @param termStr + * @param minSimilarity + * @return the query + * @throws ParseException + */ + public Query getSuperFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException + { + return super.getFuzzyQuery(field, termStr, minSimilarity); + } + + /** + * @param field + * @param termStr + * @return the query + * @throws ParseException + */ + public Query getSuperPrefixQuery(String field, String termStr) throws ParseException + { + return super.getPrefixQuery(field, termStr); + } + + /** + * @param field + * @param termStr + * @return the query + * @throws ParseException + */ + public Query getSuperWildcardQuery(String field, String termStr) throws ParseException + { + return super.getWildcardQuery(field, termStr); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.lucene.queryParser.QueryParser#newWildcardQuery(org.apache. + * lucene.index.Term) + */ + @Override + protected Query newWildcardQuery(Term t) + { + if (t.text().equals("*")) + { + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + bQuery.add(createTermQuery(FIELD_FIELDS, t.field()), Occur.SHOULD); + bQuery.add(createTermQuery(FIELD_PROPERTIES, t.field()), Occur.SHOULD); + return bQuery.build(); + } else if (t.text().contains("\\")) + { + String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, + SearchLanguageConversion.DEF_REGEX, t.text()); + return new RegexpQuery(new Term(t.field(), regexp)); + } else + { + org.apache.lucene.search.WildcardQuery query = new org.apache.lucene.search.WildcardQuery(t); + query.setRewriteMethod(new MultiTermQuery.TopTermsScoringBooleanQueryRewrite(topTermSpanRewriteLimit)); + return query; + } + } + + /* + * (non-Javadoc) + * + * @see org.apache.lucene.queryParser.QueryParser#newPrefixQuery(org.apache. + * lucene.index.Term) + */ + @Override + protected Query newPrefixQuery(Term prefix) + { + if (prefix.text().contains("\\")) + { + String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, + SearchLanguageConversion.DEF_REGEX, prefix.text()); + return new RegexpQuery(new Term(prefix.field(), regexp)); + } else + { + return super.newPrefixQuery(prefix); + } + + } + + public interface SubQuery + { + /** + * @param field + * @param queryText + * @param analysisMode + * @param luceneFunction + * @return the query + * @throws ParseException + */ + Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException; + } + + class FieldQuery implements SubQuery + { + public Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return getSuperFieldQuery(field, queryText, analysisMode, luceneFunction); + } + } + + class FuzzyQuery implements SubQuery + { + float minSimilarity; + + FuzzyQuery(float minSimilarity) + { + this.minSimilarity = minSimilarity; + } + + public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return getSuperFuzzyQuery(field, translateLocale(termStr), minSimilarity); + } + } + + private String translateLocale(String localised) + { + if (localised.startsWith("\u0000")) + { + if (localised.startsWith("\u0000\u0000")) + { + if (localised.length() < 3) + { + return ""; + } else + { + return localised.substring(2); + } + } else + { + int end = localised.indexOf('\u0000', 1); + if (end == -1) + { + return localised; + } else + { + StringBuilder buffer = new StringBuilder(); + buffer.append("{"); + buffer.append(localised.substring(1, end)); + buffer.append("}"); + buffer.append(localised.substring(end + 1)); + return buffer.toString(); + } + } + } else + { + return localised; + } + } + + class PrefixQuery implements SubQuery + { + public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + StringBuilder builder = new StringBuilder(termStr.length() + 1); + builder.append(termStr); + builder.append("*"); + return getSuperFieldQuery(field, builder.toString(), analysisMode, luceneFunction); + // return getSuperPrefixQuery(field, termStr); + } + } + + class WildcardQuery implements SubQuery + { + public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) + throws ParseException + { + return getSuperFieldQuery(field, termStr, analysisMode, luceneFunction); + // return getSuperWildcardQuery(field, termStr); + } + } + + private Query spanQueryBuilder(String field, String first, String last, int slop, boolean inOrder) + throws ParseException + { + String propertyFieldName = field.substring(1); + String expandedFieldName = null; + + PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, propertyFieldName); + IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; + if (propertyDef != null) + { + tokenisationMode = propertyDef.getIndexTokenisationMode(); + if (tokenisationMode == null) + { + tokenisationMode = IndexTokenisationMode.TRUE; + } + } else + { + expandedFieldName = expandAttributeFieldName(field); + } + + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) + { + // Build a sub query for each locale and or the results together - + // the analysis will take care of + // cross language matching for each entry + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + addMLTextSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, propertyDef, + tokenisationMode, booleanQuery, locale); + } + return booleanQuery.build(); + } + // Content + else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales + .addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); + } + + return addContentSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, + expandedLocales); + + } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + + addTextSpanQuery(field, propertyDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, + booleanQuery, locale); + + } + return booleanQuery.build(); + } else + { + throw new UnsupportedOperationException( + "Span queries are only supported for d:text, d:mltext and d:content data types"); + } + } + + private Query attributeQueryBuilder(String field, String queryText, SubQuery subQueryBuilder, + AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException + { + // TODO: Fix duplicate token generation for mltext, content and text. + // -locale expansion here and in tokeisation -> duplicates + + // Get type info etc + + // TODO: additional suffixes + + Pair fieldNameAndEnding = QueryParserUtils.extractFieldNameAndEnding(field); + + String expandedFieldName = null; + QName propertyQName; + PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition(searchParameters.getNamespace(), + namespacePrefixResolver, dictionaryService, fieldNameAndEnding.getFirst()); + IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; + if (propertyDef != null) + { + tokenisationMode = propertyDef.getIndexTokenisationMode(); + if (tokenisationMode == null) + { + tokenisationMode = IndexTokenisationMode.TRUE; + } + propertyQName = propertyDef.getName(); + } else + { + expandedFieldName = expandAttributeFieldName(field); + propertyQName = QName.createQName(fieldNameAndEnding.getFirst()); + } + + if (isAllStar(queryText)) + { + return createTermQuery(FIELD_PROPERTIES, propertyQName.toString()); + } + + if (luceneFunction != LuceneFunction.FIELD) + { + if ((tokenisationMode == IndexTokenisationMode.FALSE) || (tokenisationMode == IndexTokenisationMode.BOTH)) + { + if (luceneFunction == LuceneFunction.LOWER) + { + if (false == queryText.toLowerCase().equals(queryText)) + { + return createNoMatchQuery(); + } + } + if (luceneFunction == LuceneFunction.UPPER) + { + if (false == queryText.toUpperCase().equals(queryText)) + { + return createNoMatchQuery(); + } + } + + return functionQueryBuilder(expandedFieldName, fieldNameAndEnding.getSecond(), propertyQName, + propertyDef, tokenisationMode, queryText, luceneFunction); + } + } + + // Mime type + if (fieldNameAndEnding.getSecond().equals(FIELD_MIMETYPE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_MIMETYPE, FieldUse.ID).getFields().get(0) + .getField(), queryText, analysisMode, luceneFunction); + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_SIZE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_SIZE, FieldUse.ID).getFields().get(0) + .getField(), queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_LOCALE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_LOCALE, FieldUse.ID).getFields().get(0) + .getField(), queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_ENCODING_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery(AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.CONTENT_ENCODING, FieldUse.ID).getFields().get(0) + .getField(), queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_STATUS_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder + .getQuery( + AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_STATUS, + FieldUse.ID) + .getFields().get(0).getField(), + queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_TIME_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder + .getQuery( + AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_TIME, + FieldUse.ID) + .getFields().get(0).getField(), + queryText, analysisMode, luceneFunction); + + } + + } else if (fieldNameAndEnding.getSecond().equals(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + return subQueryBuilder.getQuery( + AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyQName, SpecializedFieldType.TRANSFORMATION_EXCEPTION, + FieldUse.ID) + .getFields().get(0).getField(), + queryText, analysisMode, luceneFunction); + + } + + } + else if (AlfrescoSolrDataModel.getInstance().isDerivedDateField(fieldNameAndEnding.getSecond())) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE) + || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME ))) + { + return subQueryBuilder.getQuery( + AlfrescoSolrDataModel.getInstance() + .getDateDerivedField(propertyQName, fieldNameAndEnding.getSecond()), + queryText, analysisMode, luceneFunction); + } + } + + // Already in expanded form + + // ML + + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) + { + // Build a sub query for each locale and or the results together - + // the analysis will take care of + // cross language matching for each entry + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + addMLTextAttributeQuery(field, propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, propertyDef, tokenisationMode, booleanQuery, locale); + } + return getNonEmptyBooleanQuery(booleanQuery.build()); + } + // Content + else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + // Identifier request are ignored for content + + // Build a sub query for each locale and or the results together - + // - add an explicit condition for the locale + + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales + .addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); + } + + return addContentAttributeQuery(propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, expandedLocales); + + } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) + { + // if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) || + // propertyQName.equals(ContentModel.PROP_USERNAME) || + // propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) + // { + // // nasty work around for solr support for user and group look up + // as we can not support lowercased identifiers in the model + // if(isLucene()) + // { + // return subQueryBuilder.getQuery(expandedFieldName, queryText, + // analysisMode, luceneFunction); + // } + // } + + boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) + || propertyQName.equals(ContentModel.PROP_USERNAME) + || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); + + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + Locale fixedLocale = locale; + if (fixedLocale.getLanguage().equals("*")) + { + fixedLocale = new Locale("??"); + } + addTextAttributeQuery(field, propertyDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, tokenisationMode, booleanQuery, fixedLocale); + + } + return getNonEmptyBooleanQuery(booleanQuery.build()); + } else + { + // Date does not support like + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME))) + { + if (analysisMode == AnalysisMode.LIKE) + { + throw new UnsupportedOperationException("Wild cards are not supported for the datetime type"); + } + } + + // expand date for loose date parsing + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) + || propertyDef.getDataType().getName().equals(DataTypeDefinition.DATE))) + { + Pair dateAndResolution = parseDateString(queryText); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + IndexedField indexedField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), null, FieldUse.FTS); + for (FieldInstance instance : indexedField.getFields()) + { + if (dateAndResolution != null) + { + Query query = newRangeQuery(instance.getField(), getDateStart(dateAndResolution), + getDateEnd(dateAndResolution), true, true); + if (query != null) + { + bQuery.add(query, Occur.SHOULD); + } + } else + { + Query query = subQueryBuilder.getQuery(instance.getField(), queryText, AnalysisMode.DEFAULT, + luceneFunction); + if (query != null) + { + bQuery.add(query, Occur.SHOULD); + } + } + } + if (bQuery.build().clauses().size() > 0) + { + return bQuery.build(); + } else + { + return createNoMatchQuery(); + } + } + + if ((propertyDef != null) && (tenantService.isTenantUser()) + && (propertyDef.getDataType().getName().equals(DataTypeDefinition.NODE_REF)) + && (queryText.contains(StoreRef.URI_FILLER))) + { + // ALF-6202 + queryText = tenantService.getName(new NodeRef(queryText)).toString(); + } + + // Sort and id is only special for MLText, text, and content + // Dates are not special in this case + if (propertyDef != null) + { + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + IndexedField indexedField = AlfrescoSolrDataModel.getInstance() + .getQueryableFields(propertyDef.getName(), null, FieldUse.FTS); + for (FieldInstance instance : indexedField.getFields()) + { + Query query = subQueryBuilder.getQuery(instance.getField(), queryText, AnalysisMode.DEFAULT, + luceneFunction); + if (query != null) + { + bQuery.add(query, Occur.SHOULD); + } + } + if (bQuery.build().clauses().size() > 0) + { + return bQuery.build(); + } else + { + return createNoMatchQuery(); + } + } else + { + Query query = subQueryBuilder.getQuery(expandedFieldName, queryText, AnalysisMode.DEFAULT, + luceneFunction); + if (query != null) + { + return query; + } else + { + return createNoMatchQuery(); + } + } + } + } + + /** + * @param queryText + * @return + */ + private boolean isAllStar(String queryText) + { + if ((queryText == null) || (queryText.length() == 0)) + { + return false; + } + for (char c : queryText.toCharArray()) + { + if (c != '*') + { + return false; + } + } + return true; + + } + + /** + * @param dateAndResolution + * @return + */ + private String getDateEnd(Pair dateAndResolution) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.setTime(dateAndResolution.getFirst()); + switch (dateAndResolution.getSecond()) + { + case Calendar.YEAR: + cal.set(Calendar.MONTH, cal.getActualMaximum(Calendar.MONTH)); + case Calendar.MONTH: + cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); + case Calendar.DAY_OF_MONTH: + cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY)); + case Calendar.HOUR_OF_DAY: + cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE)); + case Calendar.MINUTE: + cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND)); + case Calendar.SECOND: + cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND)); + case Calendar.MILLISECOND: + default: + } + SimpleDateFormat formatter = CachingDateFormat.getSolrDatetimeFormat(); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + return formatter.format(cal.getTime()); + } + + /** + * Returns the formatted start (i.e. lower bound) of a range query. + */ + private String getDateStart(Pair dateAndResolution) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.setTime(dateAndResolution.getFirst()); + switch (dateAndResolution.getSecond()) + { + case Calendar.YEAR: + cal.set(Calendar.MONTH, cal.getActualMinimum(Calendar.MONTH)); + case Calendar.MONTH: + cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH)); + case Calendar.DAY_OF_MONTH: + cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); + case Calendar.HOUR_OF_DAY: + cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE)); + case Calendar.MINUTE: + cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND)); + case Calendar.SECOND: + cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND)); + case Calendar.MILLISECOND: + default: + } + return CachingDateFormat.getSolrDatetimeFormat().format(cal.getTime()); + } + + private Pair parseDateString(String dateString) + { + try + { + return CachingDateFormat.lenientParse(dateString, Calendar.YEAR); + } catch (java.text.ParseException e) + { + SimpleDateFormat oldDf = CachingDateFormat.getDateFormat(); + oldDf.setTimeZone(TimeZone.getTimeZone("UTC")); + try + { + Date date = oldDf.parse(dateString); + return new Pair<>(date, Calendar.SECOND); + } catch (java.text.ParseException ee) + { + if (dateString.equalsIgnoreCase("min")) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.set(Calendar.YEAR, cal.getMinimum(Calendar.YEAR)); + cal.set(Calendar.DAY_OF_YEAR, cal.getMinimum(Calendar.DAY_OF_YEAR)); + cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY)); + cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE)); + cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND)); + cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND)); + return new Pair(cal.getTime(), Calendar.MILLISECOND); + } else if (dateString.equalsIgnoreCase("now")) + { + return new Pair(new Date(), Calendar.MILLISECOND); + } else if (dateString.equalsIgnoreCase("today")) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.setTime(new Date()); + cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY)); + cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE)); + cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND)); + cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND)); + return new Pair(cal.getTime(), Calendar.DAY_OF_MONTH); + } else if (dateString.equalsIgnoreCase("max")) + { + Calendar cal = Calendar.getInstance(I18NUtil.getLocale()); + cal.set(Calendar.YEAR, cal.getMaximum(Calendar.YEAR)); + cal.set(Calendar.DAY_OF_YEAR, cal.getMaximum(Calendar.DAY_OF_YEAR)); + cal.set(Calendar.HOUR_OF_DAY, cal.getMaximum(Calendar.HOUR_OF_DAY)); + cal.set(Calendar.MINUTE, cal.getMaximum(Calendar.MINUTE)); + cal.set(Calendar.SECOND, cal.getMaximum(Calendar.SECOND)); + cal.set(Calendar.MILLISECOND, cal.getMaximum(Calendar.MILLISECOND)); + return new Pair(cal.getTime(), Calendar.MILLISECOND); + } else + { + return null; // delegate to SOLR date parsing + } + } + } + } + + protected Query functionQueryBuilder(String expandedFieldName, String ending, QName propertyQName, + PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, String queryText, + LuceneFunction luceneFunction) throws ParseException + { + + // Mime type + if (ending.equals(FIELD_MIMETYPE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_SIZE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_LOCALE_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_ENCODING_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_CONTENT_DOC_ID_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_TRANSFORMATION_TIME_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } else if (ending.equals(FIELD_TRANSFORMATION_STATUS_SUFFIX)) + { + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene Function"); + } + + } + + // Already in expanded form + + // ML + + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) + { + // Build a sub query for each locale and or the results together - + // the analysis will take care of + // cross language matching for each entry + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + + addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, propertyDef, queryText, luceneFunction, + booleanQuery, locale, tokenisationMode); + + } + return booleanQuery.build(); + } + // Content + else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) + { + throw new UnsupportedOperationException("Lucene functions not supported for content"); + } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) + { + if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) + || propertyQName.equals(ContentModel.PROP_USERNAME) + || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) + { + throw new UnsupportedOperationException("Functions are not supported agaisnt special text fields"); + } + + boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) + || propertyQName.equals(ContentModel.PROP_USERNAME) + || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); + + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + List locales = searchParameters.getLocales(); + List expandedLocales = new ArrayList(); + for (Locale locale : (((locales == null) || (locales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : locales)) + { + expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); + } + for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) + ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) + { + addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, propertyDef, queryText, luceneFunction, + booleanQuery, locale, tokenisationMode); + + } + return booleanQuery.build(); + } else + { + throw new UnsupportedOperationException("Lucene Function"); + } + } + + protected TermQuery createNoMatchQuery() + { + return new TermQuery(new Term("NO_TOKENS", "__")); + } + + /** + * Returns null if all clause words were filtered away by the analyzer + * + * @param booleanQuery + * - initial BooleanQuery + * @return BooleanQuery or null if booleanQuery has no clauses + */ + protected BooleanQuery getNonEmptyBooleanQuery(BooleanQuery booleanQuery) + { + if (booleanQuery.clauses().size() > 0) + { + return booleanQuery; + } else + { + return null; + } + } + + protected Query createSolr4IdQuery(String queryText) + { + return createTermQuery(FIELD_SOLR4_ID, queryText); + } + + // Previous SOLR + + protected Query createIdQuery(String queryText) + { + if (NodeRef.isNodeRef(queryText)) + { + return createNodeRefQuery(FIELD_LID, queryText); + } else + { + return createNodeRefQuery(FIELD_ID, queryText); + } + } + + protected Query createPathQuery(String queryText, boolean withRepeats) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse(queryText); + SolrPathQuery pathQuery = handler.getQuery(); + pathQuery.setRepeats(withRepeats); + return new SolrCachingPathQuery(pathQuery); + } + + protected Query createQNameQuery(String queryText) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse("//" + queryText); + SolrPathQuery pathQuery = handler.getQuery(); + return new SolrCachingPathQuery(pathQuery); + } + + protected Query createPrimaryAssocQNameQuery(String queryText) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse("//" + queryText); + SolrPathQuery pathQuery = handler.getQuery(); + pathQuery.setPathField(FIELD_PRIMARYASSOCQNAME); + return new SolrCachingPathQuery(pathQuery); + } + + protected Query createPrimaryAssocTypeQNameQuery(String queryText) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse("//" + queryText); + SolrPathQuery pathQuery = handler.getQuery(); + pathQuery.setPathField(FIELD_PRIMARYASSOCTYPEQNAME); + return new SolrCachingPathQuery(pathQuery); + } + + protected Query createAssocTypeQNameQuery(String queryText) throws SAXPathException + { + XPathReader reader = new XPathReader(); + SolrXPathHandler handler = new SolrXPathHandler(); + handler.setNamespacePrefixResolver(namespacePrefixResolver); + handler.setDictionaryService(dictionaryService); + reader.setXPathHandler(handler); + reader.parse("//" + queryText); + SolrPathQuery pathQuery = handler.getQuery(); + pathQuery.setPathField(FIELD_ASSOCTYPEQNAME); + return new SolrCachingPathQuery(pathQuery); + } + + /** + * @param queryText + * @return + */ + protected Query createAclIdQuery(String queryText) throws ParseException + { + return getFieldQueryImplWithIOExceptionWrapped(FIELD_ACLID, queryText, AnalysisMode.DEFAULT, + LuceneFunction.FIELD); + } + + /** + * @param queryText + * @return + */ + protected Query createOwnerQuery(String queryText) throws ParseException + { + return new SolrOwnerQuery(queryText); + } + + /** + * @param queryText + * @return + */ + protected Query createReaderQuery(String queryText) throws ParseException + { + return new SolrReaderQuery(queryText); + } + + /** + * @param queryText + * @return + */ + protected Query createAuthorityQuery(String queryText) throws ParseException + { + //return new SolrAuthorityQuery(queryText); + return new SolrAuthoritySetQuery(","+queryText); + } + + // TODO: correct field names + protected Query addContentAttributeQuery(PropertyDefinition pDef, String queryText, SubQuery subQueryBuilder, + AnalysisMode analysisMode, LuceneFunction luceneFunction, String expandedFieldName, + List expandedLocales) throws ParseException + { + BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder(); + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, + FieldUse.FTS); + if (isExactTermSearch(analysisMode)) + { + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + Query subQuery = subQueryBuilder.getQuery(field.getField(), queryText, analysisMode, + luceneFunction); + if (subQuery != null) + { + booleanQueryBuilder.add(subQuery, Occur.SHOULD); + } + } + } + if (booleanQueryBuilder.build().clauses().size() == 0) + { + throw new UnsupportedOperationException("Exact Term search is not supported unless you configure the field <"+pDef.getName()+"> for cross locale search"); + } + } else + { + for (Locale locale : expandedLocales) + { + boolean localisedSearch = locale.toString().length() != 0; + if (localisedSearch) + { + StringBuilder builder = new StringBuilder(queryText.length() + 10); + builder.append("\u0000").append(locale.toString()).append("\u0000").append(queryText); + } + for (FieldInstance field : indexedField.getFields()) + { + if ((!localisedSearch && !field.isLocalised()) || (localisedSearch && field.isLocalised())) + { + Query subQuery = subQueryBuilder.getQuery(field.getField(), queryText, analysisMode, + luceneFunction); + if (subQuery != null) + { + booleanQueryBuilder.add(subQuery, Occur.SHOULD); + } + } + } + } + } + return getNonEmptyBooleanQuery(booleanQueryBuilder.build()); + } + + protected void addLocaleSpecificUntokenisedMLOrTextFunction(String expandedFieldName, PropertyDefinition pDef, + String queryText, LuceneFunction luceneFunction, Builder booleanQuery, Locale locale, + IndexTokenisationMode tokenisationMode) + { + // Query subQuery = new CaseInsensitiveFieldQuery(new + // Term(getFieldName(expandedFieldName, locale, tokenisationMode, + // IndexTokenisationMode.FALSE), getFixedFunctionQueryText( + // queryText, locale, tokenisationMode, IndexTokenisationMode.FALSE))); + // booleanQuery.add(subQuery, Occur.SHOULD); + // + // if (booleanQuery.getClauses().length == 0) + // { + // booleanQuery.add(createNoMatchQuery(), Occur.SHOULD); + // } + throw new UnsupportedOperationException(); + } + + private FieldInstance getFieldInstance(String baseFieldName, PropertyDefinition pDef, Locale locale, + IndexTokenisationMode preferredIndexTokenisationMode, AnalysisMode analysisMode) + { + if (pDef != null) + { + switch (preferredIndexTokenisationMode) + { + case BOTH: + throw new IllegalStateException("Preferred mode can not be BOTH"); + case FALSE: + if (locale.toString().length() == 0) + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.ID); + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + return field; + } + } + } else + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.ID); + for (FieldInstance field : indexedField.getFields()) + { + if (field.isLocalised()) + { + return field; + } + } + } + break; + case TRUE: + { + IndexedField indexedField; + if (isExactTermSearch(analysisMode)) + { + indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.EXACT); + } else + { + indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.FTS); + } + if (locale.toString().length() == 0) + { + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + return field; + } + } + } else + { + for (FieldInstance field : indexedField.getFields()) + { + if (field.isLocalised()) + { + return field; + } + } + } + break; + } + } + return null; + + } + + return new FieldInstance(baseFieldName, false, false); + + } + + private boolean isExactTermSearch(AnalysisMode analysisMode) { + return analysisMode==AnalysisMode.IDENTIFIER && !this.solrParams.get(DEFTYPE).equals(CMIS_REQUEST_HANDLER); + } + + protected void addLocaleSpecificUntokenisedTextRangeFunction(String expandedFieldName, PropertyDefinition pDef, + String lower, String upper, boolean includeLower, boolean includeUpper, LuceneFunction luceneFunction, + Builder booleanQuery, Locale locale, IndexTokenisationMode tokenisationMode) throws ParseException + { + // String field = getFieldName(expandedFieldName, locale, + // tokenisationMode, IndexTokenisationMode.FALSE); + // + // StringBuilder builder = new StringBuilder(); + // builder.append("\u0000").append(locale.toString()).append("\u0000").append(lower); + // String first = getToken(field, builder.toString(), + // AnalysisMode.IDENTIFIER); + // + // builder = new StringBuilder(); + // builder.append("\u0000").append(locale.toString()).append("\u0000").append(upper); + // String last = getToken(field, builder.toString(), + // AnalysisMode.IDENTIFIER); + // + // Query query = new CaseInsensitiveFieldRangeQuery(field, first, last, + // includeLower, includeUpper); + // booleanQuery.add(query, Occur.SHOULD); + + throw new UnsupportedOperationException(); + + } + + protected void addMLTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, + SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, + String expandedFieldName, PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, + Builder booleanQuery, Locale locale) throws ParseException + { + + addMLTextOrTextAttributeQuery(field, pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, tokenisationMode, booleanQuery, locale); + } + + private void addMLTextOrTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, + SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, + String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) + throws ParseException + { + + boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); + try + { + switch (tokenisationMode) + { + case BOTH: + switch (analysisMode) + { + default: + case DEFAULT: + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); + + if (ContentModel.PROP_NAME.equals(pDef.getName())) + { + setLowercaseExpandedTerms(false); + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); + } + + break; + case TOKENISE: + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); + break; + case IDENTIFIER: + setLowercaseExpandedTerms(false); + if (isExactTermSearch(analysisMode)) + {//with exact search we favour tokenization, specifically cross locale tokenization + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); + } else + { + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); + } + break; + case FUZZY: + case PREFIX: + case WILD: + case LIKE: + setLowercaseExpandedTerms(false); + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); + + break; + } + break; + case FALSE: + setLowercaseExpandedTerms(false); + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.FALSE); + break; + case TRUE: + default: + addLocaleSpecificMLOrTextAttribute(pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + booleanQuery, locale, expandedFieldName, tokenisationMode, IndexTokenisationMode.TRUE); + break; + } + } finally + { + setLowercaseExpandedTerms(lowercaseExpandedTerms); + } + + } + + protected void addTextAttributeQuery(String field, PropertyDefinition pDef, String queryText, + SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, + String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) + throws ParseException + { + + addMLTextOrTextAttributeQuery(field, pDef, queryText, subQueryBuilder, analysisMode, luceneFunction, + expandedFieldName, tokenisationMode, booleanQuery, locale); + } + + private void addLocaleSpecificMLOrTextAttribute(PropertyDefinition pDef, String queryText, SubQuery subQueryBuilder, + AnalysisMode analysisMode, LuceneFunction luceneFunction, Builder booleanQuery, Locale locale, + String textFieldName, IndexTokenisationMode tokenisationMode, + IndexTokenisationMode preferredTokenisationMode) throws ParseException + { + + FieldInstance fieldInstance = getFieldInstance(textFieldName, pDef, locale, preferredTokenisationMode, analysisMode); + if (fieldInstance != null) + { + StringBuilder builder = new StringBuilder(queryText.length() + 10); + if (fieldInstance.isLocalised()) + { + builder.append("\u0000").append(locale.toString()).append("\u0000"); + } + builder.append(queryText); + Query subQuery = subQueryBuilder.getQuery(fieldInstance.getField(), builder.toString(), analysisMode, + luceneFunction); + if (subQuery != null) + { + booleanQuery.add(subQuery, Occur.SHOULD); + } + } + } + + protected void addTextRange(String field, PropertyDefinition pDef, String part1, String part2, boolean includeLower, + boolean includeUpper, AnalysisMode analysisMode, String fieldName, PropertyDefinition propertyDef, + IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) + throws ParseException, IOException + { + switch (tokenisationMode) + { + case BOTH: + switch (analysisMode) + { + case DEFAULT: + case TOKENISE: + addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, + locale, analysisMode, tokenisationMode, IndexTokenisationMode.TRUE); + break; + case IDENTIFIER: + addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, + locale, analysisMode, tokenisationMode, IndexTokenisationMode.FALSE); + break; + case WILD: + case LIKE: + case PREFIX: + case FUZZY: + default: + throw new UnsupportedOperationException(); + } + break; + case FALSE: + addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, locale, + analysisMode, tokenisationMode, IndexTokenisationMode.FALSE); + break; + case TRUE: + addLocaleSpecificTextRange(fieldName, pDef, part1, part2, includeLower, includeUpper, booleanQuery, locale, + analysisMode, tokenisationMode, IndexTokenisationMode.TRUE); + break; + default: + } + + } + + private void addLocaleSpecificTextRange(String expandedFieldName, PropertyDefinition pDef, String part1, + String part2, boolean includeLower, boolean includeUpper, Builder booleanQuery, Locale locale, + AnalysisMode analysisMode, IndexTokenisationMode tokenisationMode, + IndexTokenisationMode preferredTokenisationMode) throws ParseException, IOException + { + FieldInstance fieldInstance = getFieldInstance(expandedFieldName, pDef, locale, preferredTokenisationMode, analysisMode); + if (fieldInstance != null) + { + String firstString = null; + if ((part1 != null) && !part1.equals("\u0000")) + { + if (fieldInstance.isLocalised()) + { + firstString = getFirstTokenForRange(getLocalePrefixedText(part1, locale), fieldInstance); + if (firstString == null) + { + firstString = "{" + locale.getLanguage() + "}"; + } + } else + { + firstString = getFirstTokenForRange(part1, fieldInstance); + } + } else + { + if (fieldInstance.isLocalised()) + { + firstString = "{" + locale.getLanguage() + "}"; + } else + { + firstString = null; + } + } + + String lastString = null; + if ((part2 != null) && !part2.equals("\uffff")) + { + if (fieldInstance.isLocalised()) + { + lastString = getFirstTokenForRange(getLocalePrefixedText(part2, locale), fieldInstance); + if (lastString == null) + { + lastString = "{" + locale.getLanguage() + "}\uffff"; + } + } else + { + lastString = getFirstTokenForRange(part2, fieldInstance); + } + } else + { + if (fieldInstance.isLocalised()) + { + lastString = "{" + locale.getLanguage() + "}\uffff"; + } else + { + lastString = null; + } + } + + + // * is returned by stringField but it is not correct in case of range queries. + if (firstString != null && firstString.equals("*")) + { + firstString = null; + } + + if (lastString != null && lastString.equals("*")) + { + lastString = null; + } + + TermRangeQuery query = new TermRangeQuery(fieldInstance.getField(), + firstString == null ? null : new BytesRef(firstString), + lastString == null ? null : new BytesRef(lastString), includeLower, includeUpper); + booleanQuery.add(query, Occur.SHOULD); + } + } + + private String getFirstTokenForRange(String string, FieldInstance field) throws IOException + { + PackedTokenAttributeImpl nextToken; + TokenStream source = null; + ; + + try + { + source = getAnalyzer().tokenStream(field.getField(), new StringReader(string)); + source.reset(); + while (source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if (source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if (source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + nextToken = new PackedTokenAttributeImpl(); + nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); + nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if (typeAtt != null) + { + nextToken.setType(typeAtt.type()); + } + if (posIncAtt != null) + { + nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + + return nextToken.toString(); + } + } finally + { + try + { + if (source != null) + { + source.close(); + } + } catch (IOException e) + { + // ignore + } + } + return null; + } + + protected void addTextSpanQuery(String field, PropertyDefinition pDef, String first, String last, int slop, + boolean inOrder, String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, + Locale locale) { + addMLTextOrTextSpanQuery(field, pDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, + booleanQuery, locale); + } + + protected org.apache.lucene.search.Query addContentSpanQuery(String afield, PropertyDefinition pDef, String first, + String last, int slop, boolean inOrder, String expandedFieldName, List expandedLocales) + { + try + { + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + for (Locale locale : expandedLocales) + { + if (locale.toString().length() == 0) + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.FTS); + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + SpanQuery firstQuery = buildSpanOrQuery(first, field); + SpanQuery lastQuery = buildSpanOrQuery(last, field); + SpanNearQuery result = new SpanNearQuery(new SpanQuery[] + { firstQuery, lastQuery }, slop, inOrder); + booleanQuery.add(result, Occur.SHOULD); + } + } + } else + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), + null, FieldUse.FTS); + for (FieldInstance field : indexedField.getFields()) + { + if (field.isLocalised()) + { + SpanQuery firstQuery = buildSpanOrQuery(getLocalePrefixedText(first, locale), field); + SpanQuery lastQuery = buildSpanOrQuery(getLocalePrefixedText(last, locale), field); + SpanNearQuery result = new SpanNearQuery(new SpanQuery[] + { firstQuery, lastQuery }, slop, inOrder); + booleanQuery.add(result, Occur.SHOULD); + } + } + } + } + return getNonEmptyBooleanQuery(booleanQuery.build()); + } catch (IOException ioe) + { + return createNoMatchQuery(); + } + + } + + private String getLocalePrefixedText(String text, Locale locale) + { + StringBuilder builder = new StringBuilder(text.length() + 10); + builder.append("\u0000").append(locale.toString()).append("\u0000").append(text); + return builder.toString(); + } + + /** + * @param first + * @param field + * @return SpanOrQuery + * @throws IOException + */ + private SpanQuery buildSpanOrQuery(String first, FieldInstance field) throws IOException + { + ArrayList spanOrQueryParts = new ArrayList(); + + PackedTokenAttributeImpl nextToken; + TokenStream source = null; + + try + { + source = getAnalyzer().tokenStream(field.getField(), new StringReader(first)); + source.reset(); + while (source.incrementToken()) + { + CharTermAttribute cta = source.getAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = source.getAttribute(OffsetAttribute.class); + TypeAttribute typeAtt = null; + if (source.hasAttribute(TypeAttribute.class)) + { + typeAtt = source.getAttribute(TypeAttribute.class); + } + PositionIncrementAttribute posIncAtt = null; + if (source.hasAttribute(PositionIncrementAttribute.class)) + { + posIncAtt = source.getAttribute(PositionIncrementAttribute.class); + } + nextToken = new PackedTokenAttributeImpl(); + nextToken.setEmpty().copyBuffer(cta.buffer(), 0, cta.length()); + nextToken.setOffset(offsetAtt.startOffset(), offsetAtt.endOffset()); + if (typeAtt != null) + { + nextToken.setType(typeAtt.type()); + } + if (posIncAtt != null) + { + nextToken.setPositionIncrement(posIncAtt.getPositionIncrement()); + } + + SpanQuery termQuery = new SpanTermQuery(new Term(field.getField(), nextToken.toString())); + spanOrQueryParts.add(termQuery); + } + } finally + { + try + { + if (source != null) + { + source.close(); + } + } catch (IOException e) + { + // ignore + } + } + + if (spanOrQueryParts.size() == 1) + { + return spanOrQueryParts.get(0); + } else + { + return new SpanOrQuery(spanOrQueryParts.toArray(new SpanQuery[] + {})); + } + } + + protected void addMLTextSpanQuery(String field, PropertyDefinition pDef, String first, String last, int slop, + boolean inOrder, String expandedFieldName, PropertyDefinition propertyDef, + IndexTokenisationMode tokenisationMode, Builder booleanQuery, Locale locale) { + addMLTextOrTextSpanQuery(field, pDef, first, last, slop, inOrder, expandedFieldName, tokenisationMode, + booleanQuery, locale); + } + + private void addMLTextOrTextSpanQuery(String afield, PropertyDefinition pDef, String first, String last, int slop, + boolean inOrder, String expandedFieldName, IndexTokenisationMode tokenisationMode, Builder booleanQuery, + Locale locale) + { + try + { + + if (locale.toString().length() == 0) + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, + FieldUse.FTS); + for (FieldInstance field : indexedField.getFields()) + { + if (!field.isLocalised()) + { + SpanQuery firstQuery = buildSpanOrQuery(first, field); + SpanQuery lastQuery = buildSpanOrQuery(last, field); + SpanNearQuery result = new SpanNearQuery(new SpanQuery[] + { firstQuery, lastQuery }, slop, inOrder); + booleanQuery.add(result, Occur.SHOULD); + } + } + } else + { + IndexedField indexedField = AlfrescoSolrDataModel.getInstance().getQueryableFields(pDef.getName(), null, + FieldUse.FTS); + for (FieldInstance field : indexedField.getFields()) + { + if (field.isLocalised()) + { + SpanQuery firstQuery = buildSpanOrQuery(getLocalePrefixedText(first, locale), field); + SpanQuery lastQuery = buildSpanOrQuery(getLocalePrefixedText(last, locale), field); + SpanNearQuery result = new SpanNearQuery(new SpanQuery[] + { firstQuery, lastQuery }, slop, inOrder); + booleanQuery.add(result, Occur.SHOULD); + } + } + } + + } catch (IOException ioe) + { + + } + } + + public boolean addContentCrossLocaleWildcards() + { + return false; + } + + protected Query createOwnerSetQuery(String queryText) throws ParseException + { + return new SolrOwnerSetQuery(queryText); + } + + protected Query createReaderSetQuery(String queryText) throws ParseException + { + return new SolrReaderSetQuery(queryText); + } + + protected Query createAuthoritySetQuery(String queryText) throws ParseException + { + return new SolrAuthoritySetQuery(queryText); + } + + protected Query createDeniedQuery(String queryText) throws ParseException + { + return new SolrDeniedQuery(queryText); + } + + protected Query createDenySetQuery(String queryText) throws ParseException + { + return new SolrDenySetQuery(queryText); + } + + private BytesRef analyzeMultitermTerm(String field, String part) { + return analyzeMultitermTerm(field, part, getAnalyzer()); + } + + // Avoid FindBugs false positive (https://github.com/spotbugs/spotbugs/issues/756) + @SuppressWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") + protected BytesRef analyzeMultitermTerm(String field, String part, Analyzer analyzerIn) { + if (analyzerIn == null) analyzerIn = getAnalyzer(); + + try (TokenStream source = analyzerIn.tokenStream(field, part)) { + source.reset(); + + TermToBytesRefAttribute termAtt = source.getAttribute(TermToBytesRefAttribute.class); + + if (!source.incrementToken()) + throw new IllegalArgumentException("analyzer returned no terms for multiTerm term: " + part); + BytesRef bytes = BytesRef.deepCopyOf(termAtt.getBytesRef()); + if (source.incrementToken()) + throw new IllegalArgumentException("analyzer returned too many terms for multiTerm term: " + part); + source.end(); + return bytes; + } catch (IOException e) { + throw new RuntimeException("Error analyzing multiTerm term: " + part, e); + } + } + + protected Query newRangeQuery(String field, String part1, String part2, boolean startInclusive, boolean endInclusive) { + final BytesRef start; + final BytesRef end; + + if (part1 == null) { + start = null; + } else { + start = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part1) : new BytesRef(part1); + } + + if (part2 == null) { + end = null; + } else { + end = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part2) : new BytesRef(part2); + } + + final TermRangeQuery query = new TermRangeQuery(field, start, end, startInclusive, endInclusive); + + query.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE); + return query; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityQuery.java index d77d845f2..5d96000fa 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityQuery.java @@ -1,91 +1,91 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Query for docs the supplied authority is able to read. - * - * @author Matt Ward - */ -public class SolrAuthorityQuery extends AbstractAuthorityQuery -{ - public SolrAuthorityQuery(String authority) - { - super(authority); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_AUTHORITY).append(':'); - stringBuilder.append(authority); - return stringBuilder.toString(); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrAuthorityQueryWeight((SolrIndexSearcher)searcher, needsScores, this, authority); - } - - private class SolrAuthorityQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrAuthorityQueryWeight(SolrIndexSearcher searcher, boolean needsScores, Query query, String authority) throws IOException - { - super(searcher, needsScores, query, QueryConstants.FIELD_AUTHORITY, authority); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrAuthorityScorer.createAuthorityScorer(this, context, searcher, authority); - } - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_AUTHORITY, authority)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Query for docs the supplied authority is able to read. + * + * @author Matt Ward + */ +public class SolrAuthorityQuery extends AbstractAuthorityQuery +{ + public SolrAuthorityQuery(String authority) + { + super(authority); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_AUTHORITY).append(':'); + stringBuilder.append(authority); + return stringBuilder.toString(); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrAuthorityQueryWeight((SolrIndexSearcher)searcher, needsScores, this, authority); + } + + private class SolrAuthorityQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrAuthorityQueryWeight(SolrIndexSearcher searcher, boolean needsScores, Query query, String authority) throws IOException + { + super(searcher, needsScores, query, QueryConstants.FIELD_AUTHORITY, authority); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrAuthorityScorer.createAuthorityScorer(this, context, searcher, authority); + } + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_AUTHORITY, authority)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityScorer.java index da9cff399..909cf8ae7 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthorityScorer.java @@ -1,109 +1,109 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Properties; - -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.solr.cache.CacheConstants; -import org.alfresco.solr.data.GlobalReaders; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Find the set of docs that the supplied authority can read. - *

- * Note that anyDenyDenies is not directly supported by this class, see {@link AbstractQParser}. - * - * @see AbstractQParser - * @author Matt Ward - */ -public class SolrAuthorityScorer extends AbstractSolrCachingScorer -{ - SolrAuthorityScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrAuthorityScorer createAuthorityScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException - { - Properties p = searcher.getSchema().getResourceLoader().getCoreProperties(); - boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); - - Query key = new SolrAuthorityQuery(authority); - - DocSet answer = (DocSet)searcher.cacheLookup(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key); - if(answer != null) - { - // Answer was in the cache, so return it. - return new SolrAuthorityScorer(weight, answer, context, searcher); - } - - // Answer was not in cache, so build the results, cache and return. - final HashSet globalReaders = GlobalReaders.getReaders(); - - if (globalReaders.contains(authority) || (doPermissionChecks == false)) - { - // can read all - DocSet allDocs = searcher.getDocSet(new MatchAllDocsQuery()); - return new SolrAuthorityScorer(weight, allDocs, context, searcher); - } - - // Docs for which the authority has explicit read access. - DocSet readableDocSet = searcher.getDocSet(new SolrReaderQuery(authority)); - - // Are all doc owners granted read permissions at a global level? - if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) - { - // Get the set of docs owned by the authority (which they can therefore read). - DocSet authorityOwnedDocs = searcher.getDocSet(new SolrOwnerQuery(authority)); - // Final set of docs that the authority can read. - DocSet toCache = readableDocSet.union(authorityOwnedDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); - return new SolrAuthorityScorer(weight, toCache, context, searcher); - } - else - { - // for that docs I own that have owner Read rights - DocSet ownerReadableDocSet = searcher.getDocSet(new SolrReaderQuery(PermissionService.OWNER_AUTHORITY)); - DocSet authorityOwnedDocs = searcher.getDocSet(new SolrOwnerQuery(authority)); - - // Docs where the authority is an owner and where owners have read rights. - DocSet docsAuthorityOwnsAndCanRead = ownerReadableDocSet.intersection(authorityOwnedDocs); - // Final set of docs that the authority can read. - DocSet toCache = readableDocSet.union(docsAuthorityOwnsAndCanRead); - searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); - return new SolrAuthorityScorer(weight, toCache, context, searcher); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Properties; + +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.solr.cache.CacheConstants; +import org.alfresco.solr.data.GlobalReaders; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Find the set of docs that the supplied authority can read. + *

+ * Note that anyDenyDenies is not directly supported by this class, see {@link AbstractQParser}. + * + * @see AbstractQParser + * @author Matt Ward + */ +public class SolrAuthorityScorer extends AbstractSolrCachingScorer +{ + SolrAuthorityScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrAuthorityScorer createAuthorityScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException + { + Properties p = searcher.getSchema().getResourceLoader().getCoreProperties(); + boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); + + Query key = new SolrAuthorityQuery(authority); + + DocSet answer = (DocSet)searcher.cacheLookup(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key); + if(answer != null) + { + // Answer was in the cache, so return it. + return new SolrAuthorityScorer(weight, answer, context, searcher); + } + + // Answer was not in cache, so build the results, cache and return. + final HashSet globalReaders = GlobalReaders.getReaders(); + + if (globalReaders.contains(authority) || (doPermissionChecks == false)) + { + // can read all + DocSet allDocs = searcher.getDocSet(new MatchAllDocsQuery()); + return new SolrAuthorityScorer(weight, allDocs, context, searcher); + } + + // Docs for which the authority has explicit read access. + DocSet readableDocSet = searcher.getDocSet(new SolrReaderQuery(authority)); + + // Are all doc owners granted read permissions at a global level? + if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) + { + // Get the set of docs owned by the authority (which they can therefore read). + DocSet authorityOwnedDocs = searcher.getDocSet(new SolrOwnerQuery(authority)); + // Final set of docs that the authority can read. + DocSet toCache = readableDocSet.union(authorityOwnedDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); + return new SolrAuthorityScorer(weight, toCache, context, searcher); + } + else + { + // for that docs I own that have owner Read rights + DocSet ownerReadableDocSet = searcher.getDocSet(new SolrReaderQuery(PermissionService.OWNER_AUTHORITY)); + DocSet authorityOwnedDocs = searcher.getDocSet(new SolrOwnerQuery(authority)); + + // Docs where the authority is an owner and where owners have read rights. + DocSet docsAuthorityOwnsAndCanRead = ownerReadableDocSet.intersection(authorityOwnedDocs); + // Final set of docs that the authority can read. + DocSet toCache = readableDocSet.union(docsAuthorityOwnsAndCanRead); + searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); + return new SolrAuthorityScorer(weight, toCache, context, searcher); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetQuery.java index 194cdc973..f3e53bb75 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetQuery.java @@ -1,372 +1,372 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Properties; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.solr.data.GlobalReaders; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BooleanQuery.Builder; -import org.apache.lucene.search.Collector; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.LeafCollector; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.DelegatingCollector; -import org.apache.solr.search.PostFilter; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * @author Andy - * - */ -public class SolrAuthoritySetQuery extends AbstractAuthoritySetQuery implements PostFilter -{ - public SolrAuthoritySetQuery(String authorities) - { - super(authorities); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - SolrIndexSearcher solrIndexSearcher = (SolrIndexSearcher)searcher; - Properties p = solrIndexSearcher.getSchema().getResourceLoader().getCoreProperties(); - boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); - - boolean hasGlobalRead = false; - - final HashSet globalReaders = GlobalReaders.getReaders(); - - for(String auth : auths) - { - if(globalReaders.contains(auth)) - { - hasGlobalRead = true; - break; - } - } - - if (hasGlobalRead || (doPermissionChecks == false)) - { - return new MatchAllDocsQuery().createWeight(searcher, needsScores); - } - - BitsFilter readFilter = getACLFilter(auths, QueryConstants.FIELD_READER, solrIndexSearcher); - BitsFilter ownerFilter = getOwnerFilter(auths, solrIndexSearcher); - - if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) - { - readFilter.or(ownerFilter); - return new ConstantScoreQuery(readFilter).createWeight(searcher, needsScores); - } - else - { - String[] ownerAuth = {PermissionService.OWNER_AUTHORITY}; - BitsFilter ownerReadFilter = getACLFilter(ownerAuth, QueryConstants.FIELD_READER, solrIndexSearcher); - ownerReadFilter.and(ownerFilter); - readFilter.or(ownerReadFilter); - return new ConstantScoreQuery(readFilter).createWeight(searcher, needsScores); - } - } - - /* - * ACL PostFilter - * - * The getFilterCollector function returns a DelegatingCollector - * which is used to filter the documents that match the query. - * - * A delegating collector wraps the TopDocs Collector which gathers the top documents that - * match a query. A delegating can filter the documents before "delegating" to the TopDocs - * collector. This filtering process is where the ACL logic is applied. - * - * The getFilterCollector method sets up the data structures needed to apply the acl rules. - * These data structures are then passed to the access control collectors. - * - */ - - public DelegatingCollector getFilterCollector(IndexSearcher searcher) - { - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - SolrIndexSearcher solrIndexSearcher = (SolrIndexSearcher)searcher; - Properties p = solrIndexSearcher.getSchema().getResourceLoader().getCoreProperties(); - boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); - boolean hasGlobalRead = false; - - final HashSet globalReaders = GlobalReaders.getReaders(); - - for(String auth : auths) - { - if(globalReaders.contains(auth)) - { - hasGlobalRead = true; - break; - } - } - - if (hasGlobalRead || (doPermissionChecks == false)) - { - return new AllAccessCollector(); - } - - try - { - - /* - * Collect the ACLID's that match the authorities. - * This is done by querying the ACL records in the index. See the method for more - * documentation on this query. - */ - - HybridBitSet aclSet = getACLSet(auths, QueryConstants.FIELD_READER, solrIndexSearcher); - - /* - * Collect the documents that the user owns. - */ - - BitsFilter ownerFilter = getOwnerFilter(auths, solrIndexSearcher); - - if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) - { - return new AccessControlCollector(aclSet, ownerFilter); - } - else - { - String[] ownerAuth = {PermissionService.OWNER_AUTHORITY}; - HybridBitSet ownerAclSet = getACLSet(ownerAuth, QueryConstants.FIELD_READER, solrIndexSearcher); - return new AccessControlCollectorWithoutOwnerRead(aclSet, ownerAclSet, ownerFilter); - } - } - catch(Exception e) - { - throw new RuntimeException(e); - } - } - - public int getCost() - { - return 202; - } - - public void setCost(int cost) - { - - } - - public boolean getCache() { - return true; - } - - public void setCache(boolean cache) - { - - } - - public boolean getCacheSep() - { - return false; - } - - public void setCacheSep(boolean sep) - { - - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_AUTHORITYSET).append(':'); - stringBuilder.append(authorities); - return stringBuilder.toString(); - } - - private BitsFilter getOwnerFilter(String[] auths, SolrIndexSearcher searcher) throws IOException - { - Builder builder = new BooleanQuery.Builder(); - for(String current : auths) - { - if (AuthorityType.getAuthorityType(current) == AuthorityType.USER) - { - builder.add(new TermQuery(new Term(QueryConstants.FIELD_OWNER, current)), BooleanClause.Occur.SHOULD); - } - } - - BitsFilterCollector collector = new BitsFilterCollector(searcher.getTopReaderContext().leaves().size()); - searcher.search(builder.build(), collector); - return collector.getBitsFilter(); - } - - class BitsFilterCollector implements Collector, LeafCollector - { - private List sets; - private FixedBitSet set; - - public BitsFilterCollector(int leafCount) - { - this.sets = new ArrayList(leafCount); - } - - public BitsFilter getBitsFilter() { - return new BitsFilter(sets); - } - - public boolean acceptsDocsOutOfOrder() { - return false; - } - - public void setScorer(Scorer scorer) { - - } - - public void collect(int doc) { - set.set(doc); - } - - @Override - public LeafCollector getLeafCollector(LeafReaderContext context) - throws IOException { - set = new FixedBitSet(context.reader().maxDoc()); - sets.add(set); - return this; - } - - @Override - public boolean needsScores() { - // TODO Auto-generated method stub - return false; - } - } - - - /* - * The AccessControlCollector applies that ACL logic given aclIds and ownerFilter - */ - - class AccessControlCollector extends DelegatingCollector - { - private HybridBitSet aclIds; - private NumericDocValues fieldValues; - private BitsFilter ownerFilter; - private FixedBitSet ownerDocs; - - public AccessControlCollector(HybridBitSet aclIds, BitsFilter ownerFilter) - { - this.aclIds=aclIds; - this.ownerFilter = ownerFilter; - } - - public boolean acceptsDocsOutOfOrder() - { - return false; - } - - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); - this.ownerDocs = ownerFilter.getBitSets().get(context.ord); - } - - /* - * The collect method is applied to each document that matches the - * query. The document's aclId must be in the set of aclId's passed into the collector, - * or the documents id must be in the ownerDocs. - */ - - public void collect(int doc) throws IOException - { - long aclId = this.fieldValues.get(doc); - - if(aclIds.get(aclId) || ownerDocs.get(doc)) - { - super.collect(doc); - } - } - } - - class AccessControlCollectorWithoutOwnerRead extends DelegatingCollector - { - private HybridBitSet aclIds; - private HybridBitSet ownerAclIds; - private NumericDocValues fieldValues; - private BitsFilter ownerFilter; - private FixedBitSet ownerDocs; - public AccessControlCollectorWithoutOwnerRead(HybridBitSet aclIds, HybridBitSet ownerAclIds, BitsFilter ownerFilter) - { - this.aclIds=aclIds; - this.ownerAclIds = ownerAclIds; - this.ownerFilter = ownerFilter; - } - - public boolean acceptsDocsOutOfOrder() - { - return false; - } - - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); - this.ownerDocs = ownerFilter.getBitSets().get(context.ord); - } - - public void collect(int doc) throws IOException - { - long aclId = this.fieldValues.get(doc); - if(aclIds.get(aclId) || (ownerDocs.get(doc) && ownerAclIds.get(aclId))) - { - super.collect(doc); - } - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.solr.data.GlobalReaders; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BooleanQuery.Builder; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.LeafCollector; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.DelegatingCollector; +import org.apache.solr.search.PostFilter; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * @author Andy + * + */ +public class SolrAuthoritySetQuery extends AbstractAuthoritySetQuery implements PostFilter +{ + public SolrAuthoritySetQuery(String authorities) + { + super(authorities); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + SolrIndexSearcher solrIndexSearcher = (SolrIndexSearcher)searcher; + Properties p = solrIndexSearcher.getSchema().getResourceLoader().getCoreProperties(); + boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); + + boolean hasGlobalRead = false; + + final HashSet globalReaders = GlobalReaders.getReaders(); + + for(String auth : auths) + { + if(globalReaders.contains(auth)) + { + hasGlobalRead = true; + break; + } + } + + if (hasGlobalRead || (doPermissionChecks == false)) + { + return new MatchAllDocsQuery().createWeight(searcher, needsScores); + } + + BitsFilter readFilter = getACLFilter(auths, QueryConstants.FIELD_READER, solrIndexSearcher); + BitsFilter ownerFilter = getOwnerFilter(auths, solrIndexSearcher); + + if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) + { + readFilter.or(ownerFilter); + return new ConstantScoreQuery(readFilter).createWeight(searcher, needsScores); + } + else + { + String[] ownerAuth = {PermissionService.OWNER_AUTHORITY}; + BitsFilter ownerReadFilter = getACLFilter(ownerAuth, QueryConstants.FIELD_READER, solrIndexSearcher); + ownerReadFilter.and(ownerFilter); + readFilter.or(ownerReadFilter); + return new ConstantScoreQuery(readFilter).createWeight(searcher, needsScores); + } + } + + /* + * ACL PostFilter + * + * The getFilterCollector function returns a DelegatingCollector + * which is used to filter the documents that match the query. + * + * A delegating collector wraps the TopDocs Collector which gathers the top documents that + * match a query. A delegating can filter the documents before "delegating" to the TopDocs + * collector. This filtering process is where the ACL logic is applied. + * + * The getFilterCollector method sets up the data structures needed to apply the acl rules. + * These data structures are then passed to the access control collectors. + * + */ + + public DelegatingCollector getFilterCollector(IndexSearcher searcher) + { + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + SolrIndexSearcher solrIndexSearcher = (SolrIndexSearcher)searcher; + Properties p = solrIndexSearcher.getSchema().getResourceLoader().getCoreProperties(); + boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); + boolean hasGlobalRead = false; + + final HashSet globalReaders = GlobalReaders.getReaders(); + + for(String auth : auths) + { + if(globalReaders.contains(auth)) + { + hasGlobalRead = true; + break; + } + } + + if (hasGlobalRead || (doPermissionChecks == false)) + { + return new AllAccessCollector(); + } + + try + { + + /* + * Collect the ACLID's that match the authorities. + * This is done by querying the ACL records in the index. See the method for more + * documentation on this query. + */ + + HybridBitSet aclSet = getACLSet(auths, QueryConstants.FIELD_READER, solrIndexSearcher); + + /* + * Collect the documents that the user owns. + */ + + BitsFilter ownerFilter = getOwnerFilter(auths, solrIndexSearcher); + + if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) + { + return new AccessControlCollector(aclSet, ownerFilter); + } + else + { + String[] ownerAuth = {PermissionService.OWNER_AUTHORITY}; + HybridBitSet ownerAclSet = getACLSet(ownerAuth, QueryConstants.FIELD_READER, solrIndexSearcher); + return new AccessControlCollectorWithoutOwnerRead(aclSet, ownerAclSet, ownerFilter); + } + } + catch(Exception e) + { + throw new RuntimeException(e); + } + } + + public int getCost() + { + return 202; + } + + public void setCost(int cost) + { + + } + + public boolean getCache() { + return true; + } + + public void setCache(boolean cache) + { + + } + + public boolean getCacheSep() + { + return false; + } + + public void setCacheSep(boolean sep) + { + + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_AUTHORITYSET).append(':'); + stringBuilder.append(authorities); + return stringBuilder.toString(); + } + + private BitsFilter getOwnerFilter(String[] auths, SolrIndexSearcher searcher) throws IOException + { + Builder builder = new BooleanQuery.Builder(); + for(String current : auths) + { + if (AuthorityType.getAuthorityType(current) == AuthorityType.USER) + { + builder.add(new TermQuery(new Term(QueryConstants.FIELD_OWNER, current)), BooleanClause.Occur.SHOULD); + } + } + + BitsFilterCollector collector = new BitsFilterCollector(searcher.getTopReaderContext().leaves().size()); + searcher.search(builder.build(), collector); + return collector.getBitsFilter(); + } + + class BitsFilterCollector implements Collector, LeafCollector + { + private List sets; + private FixedBitSet set; + + public BitsFilterCollector(int leafCount) + { + this.sets = new ArrayList(leafCount); + } + + public BitsFilter getBitsFilter() { + return new BitsFilter(sets); + } + + public boolean acceptsDocsOutOfOrder() { + return false; + } + + public void setScorer(Scorer scorer) { + + } + + public void collect(int doc) { + set.set(doc); + } + + @Override + public LeafCollector getLeafCollector(LeafReaderContext context) + throws IOException { + set = new FixedBitSet(context.reader().maxDoc()); + sets.add(set); + return this; + } + + @Override + public boolean needsScores() { + // TODO Auto-generated method stub + return false; + } + } + + + /* + * The AccessControlCollector applies that ACL logic given aclIds and ownerFilter + */ + + class AccessControlCollector extends DelegatingCollector + { + private HybridBitSet aclIds; + private NumericDocValues fieldValues; + private BitsFilter ownerFilter; + private FixedBitSet ownerDocs; + + public AccessControlCollector(HybridBitSet aclIds, BitsFilter ownerFilter) + { + this.aclIds=aclIds; + this.ownerFilter = ownerFilter; + } + + public boolean acceptsDocsOutOfOrder() + { + return false; + } + + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); + this.ownerDocs = ownerFilter.getBitSets().get(context.ord); + } + + /* + * The collect method is applied to each document that matches the + * query. The document's aclId must be in the set of aclId's passed into the collector, + * or the documents id must be in the ownerDocs. + */ + + public void collect(int doc) throws IOException + { + long aclId = this.fieldValues.get(doc); + + if(aclIds.get(aclId) || ownerDocs.get(doc)) + { + super.collect(doc); + } + } + } + + class AccessControlCollectorWithoutOwnerRead extends DelegatingCollector + { + private HybridBitSet aclIds; + private HybridBitSet ownerAclIds; + private NumericDocValues fieldValues; + private BitsFilter ownerFilter; + private FixedBitSet ownerDocs; + public AccessControlCollectorWithoutOwnerRead(HybridBitSet aclIds, HybridBitSet ownerAclIds, BitsFilter ownerFilter) + { + this.aclIds=aclIds; + this.ownerAclIds = ownerAclIds; + this.ownerFilter = ownerFilter; + } + + public boolean acceptsDocsOutOfOrder() + { + return false; + } + + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); + this.ownerDocs = ownerFilter.getBitSets().get(context.ord); + } + + public void collect(int doc) throws IOException + { + long aclId = this.fieldValues.get(doc); + if(aclIds.get(aclId) || (ownerDocs.get(doc) && ownerAclIds.get(aclId))) + { + super.collect(doc); + } + } + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetScorer.java index c88879e3c..29202fdd4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrAuthoritySetScorer.java @@ -1,136 +1,136 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Properties; - -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.solr.cache.CacheConstants; -import org.alfresco.solr.data.GlobalReaders; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -/** - * Find the set of docs that the specified authority set can read. - *

- * Note that anyDenyDenies is not directly supported by this class, see {@link AbstractQParser}. - * - * @see AbstractQParser - * @author Matt Ward - */ -public class SolrAuthoritySetScorer extends AbstractSolrCachingScorer -{ - SolrAuthoritySetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrAuthoritySetScorer createAuthoritySetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities) throws IOException - { - Properties p = searcher.getSchema().getResourceLoader().getCoreProperties(); - boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); - - Query key = new SolrAuthoritySetQuery(authorities); - - DocSet answer = (DocSet)searcher.cacheLookup(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key); - if(answer != null) - { - // Answer was in the cache, so return it. - return new SolrAuthoritySetScorer(weight, answer, context, searcher); - } - - // Answer was not in cache, so build the results, cache and return. - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - boolean hasGlobalRead = false; - - final HashSet globalReaders = GlobalReaders.getReaders(); - - for(String auth : auths) - { - if(globalReaders.contains(auth)) - { - hasGlobalRead = true; - break; - } - - } - - if (hasGlobalRead || (doPermissionChecks == false)) - { - // can read all - WrappedQuery wrapped = new WrappedQuery(new MatchAllDocsQuery()); - wrapped.setCache(false); - DocSet allDocs = searcher.getDocSet(wrapped); - return new SolrAuthoritySetScorer(weight, allDocs, context, searcher); - } - - // Docs for which the authorities have explicit read access. - WrappedQuery wrapped; - wrapped = new WrappedQuery(new SolrReaderSetQuery(authorities)); - wrapped.setCache(false); - DocSet readableDocSet = searcher.getDocSet(wrapped); - - // Are all doc owners granted read permissions at a global level? - if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) - { - // Get the set of docs owned by the authorities (which they can therefore read). - wrapped = new WrappedQuery(new SolrOwnerSetQuery(authorities)); - wrapped.setCache(false); - DocSet authorityOwnedDocs = searcher.getDocSet(wrapped); - // Final set of docs that the authorities can read. - DocSet toCache = readableDocSet.union(authorityOwnedDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); - return new SolrAuthoritySetScorer(weight, toCache, context, searcher); - } - else - { - // for that docs I own that have owner Read rights - wrapped = new WrappedQuery(new SolrReaderSetQuery("|"+PermissionService.OWNER_AUTHORITY)); - wrapped.setCache(false); - DocSet ownerReadableDocSet = searcher.getDocSet(wrapped); - wrapped = new WrappedQuery(new SolrOwnerSetQuery(authorities)); - wrapped.setCache(false); - DocSet authorityOwnedDocs = searcher.getDocSet(wrapped); - - // Docs where the authority is an owner and where owners have read rights. - DocSet docsAuthorityOwnsAndCanRead = ownerReadableDocSet.intersection(authorityOwnedDocs); - // Final set of docs that the authorities can read. - DocSet toCache = readableDocSet.union(docsAuthorityOwnsAndCanRead); - searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); - return new SolrAuthoritySetScorer(weight, toCache, context, searcher); - } - } -} - +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Properties; + +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.solr.cache.CacheConstants; +import org.alfresco.solr.data.GlobalReaders; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +/** + * Find the set of docs that the specified authority set can read. + *

+ * Note that anyDenyDenies is not directly supported by this class, see {@link AbstractQParser}. + * + * @see AbstractQParser + * @author Matt Ward + */ +public class SolrAuthoritySetScorer extends AbstractSolrCachingScorer +{ + SolrAuthoritySetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrAuthoritySetScorer createAuthoritySetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities) throws IOException + { + Properties p = searcher.getSchema().getResourceLoader().getCoreProperties(); + boolean doPermissionChecks = Boolean.parseBoolean(p.getProperty("alfresco.doPermissionChecks", "true")); + + Query key = new SolrAuthoritySetQuery(authorities); + + DocSet answer = (DocSet)searcher.cacheLookup(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key); + if(answer != null) + { + // Answer was in the cache, so return it. + return new SolrAuthoritySetScorer(weight, answer, context, searcher); + } + + // Answer was not in cache, so build the results, cache and return. + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + boolean hasGlobalRead = false; + + final HashSet globalReaders = GlobalReaders.getReaders(); + + for(String auth : auths) + { + if(globalReaders.contains(auth)) + { + hasGlobalRead = true; + break; + } + + } + + if (hasGlobalRead || (doPermissionChecks == false)) + { + // can read all + WrappedQuery wrapped = new WrappedQuery(new MatchAllDocsQuery()); + wrapped.setCache(false); + DocSet allDocs = searcher.getDocSet(wrapped); + return new SolrAuthoritySetScorer(weight, allDocs, context, searcher); + } + + // Docs for which the authorities have explicit read access. + WrappedQuery wrapped; + wrapped = new WrappedQuery(new SolrReaderSetQuery(authorities)); + wrapped.setCache(false); + DocSet readableDocSet = searcher.getDocSet(wrapped); + + // Are all doc owners granted read permissions at a global level? + if (globalReaders.contains(PermissionService.OWNER_AUTHORITY)) + { + // Get the set of docs owned by the authorities (which they can therefore read). + wrapped = new WrappedQuery(new SolrOwnerSetQuery(authorities)); + wrapped.setCache(false); + DocSet authorityOwnedDocs = searcher.getDocSet(wrapped); + // Final set of docs that the authorities can read. + DocSet toCache = readableDocSet.union(authorityOwnedDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); + return new SolrAuthoritySetScorer(weight, toCache, context, searcher); + } + else + { + // for that docs I own that have owner Read rights + wrapped = new WrappedQuery(new SolrReaderSetQuery("|"+PermissionService.OWNER_AUTHORITY)); + wrapped.setCache(false); + DocSet ownerReadableDocSet = searcher.getDocSet(wrapped); + wrapped = new WrappedQuery(new SolrOwnerSetQuery(authorities)); + wrapped.setCache(false); + DocSet authorityOwnedDocs = searcher.getDocSet(wrapped); + + // Docs where the authority is an owner and where owners have read rights. + DocSet docsAuthorityOwnsAndCanRead = ownerReadableDocSet.intersection(authorityOwnedDocs); + // Final set of docs that the authorities can read. + DocSet toCache = readableDocSet.union(docsAuthorityOwnsAndCanRead); + searcher.cacheInsert(CacheConstants.ALFRESCO_AUTHORITY_CACHE, key, toCache); + return new SolrAuthoritySetScorer(weight, toCache, context, searcher); + } + } +} + diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathQuery.java index 07ef57c14..62146dc3a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathQuery.java @@ -1,108 +1,108 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -/** - * Decorator that executes a SolrPathQuery and returns cached results where possible. - * - * @author Andy - */ -public class SolrCachingPathQuery extends Query -{ - final SolrPathQuery pathQuery; - - public SolrCachingPathQuery(SolrPathQuery pathQuery) - { - if (pathQuery == null) throw new IllegalStateException("pathQuery cannot be null"); - this.pathQuery = pathQuery; - } - - /* - * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) - */ - public Weight createWeight(IndexSearcher indexSearcher, boolean requiresScore) throws IOException - { - SolrIndexSearcher searcher = null; - if(!(indexSearcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - else - { - searcher = (SolrIndexSearcher)indexSearcher; - } - - DocSet results = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_PATH_CACHE, pathQuery); - if (results == null) - { - // Cache miss: get path query results and cache them - WrappedQuery wrapped = new WrappedQuery(pathQuery); - wrapped.setCache(false); - results = searcher.getDocSet(wrapped); - searcher.cacheInsert(CacheConstants.ALFRESCO_PATH_CACHE, pathQuery, results); - } - - return new ConstantScoreQuery(results.getTopFilter()).createWeight(searcher, false); - } - - /* - * @see org.apache.lucene.search.Query#toString(java.lang.String) - */ - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("CACHED -> :"); - stringBuilder.append(pathQuery.toString()); - return stringBuilder.toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof SolrCachingPathQuery)) return false; - - SolrCachingPathQuery that = (SolrCachingPathQuery) o; - return pathQuery.equals(that.pathQuery); - - } - - @Override - public int hashCode() { - return pathQuery.hashCode(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +/** + * Decorator that executes a SolrPathQuery and returns cached results where possible. + * + * @author Andy + */ +public class SolrCachingPathQuery extends Query +{ + final SolrPathQuery pathQuery; + + public SolrCachingPathQuery(SolrPathQuery pathQuery) + { + if (pathQuery == null) throw new IllegalStateException("pathQuery cannot be null"); + this.pathQuery = pathQuery; + } + + /* + * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) + */ + public Weight createWeight(IndexSearcher indexSearcher, boolean requiresScore) throws IOException + { + SolrIndexSearcher searcher = null; + if(!(indexSearcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + else + { + searcher = (SolrIndexSearcher)indexSearcher; + } + + DocSet results = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_PATH_CACHE, pathQuery); + if (results == null) + { + // Cache miss: get path query results and cache them + WrappedQuery wrapped = new WrappedQuery(pathQuery); + wrapped.setCache(false); + results = searcher.getDocSet(wrapped); + searcher.cacheInsert(CacheConstants.ALFRESCO_PATH_CACHE, pathQuery, results); + } + + return new ConstantScoreQuery(results.getTopFilter()).createWeight(searcher, false); + } + + /* + * @see org.apache.lucene.search.Query#toString(java.lang.String) + */ + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("CACHED -> :"); + stringBuilder.append(pathQuery.toString()); + return stringBuilder.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SolrCachingPathQuery)) return false; + + SolrCachingPathQuery that = (SolrCachingPathQuery) o; + return pathQuery.equals(that.pathQuery); + + } + + @Override + public int hashCode() { + return pathQuery.hashCode(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathScorer.java index e56c4b2d5..5f03dd69e 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathScorer.java @@ -1,72 +1,72 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -/** - * Caching wrapper for {@link SolrPathQuery}. - * - * @author Matt Ward - */ -public class SolrCachingPathScorer extends AbstractSolrCachingScorer -{ - SolrCachingPathScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - - /** - * Factory method used to create {@link SolrCachingPathScorer} instances. - * @param acceptDocs - */ - public static SolrCachingPathScorer create(SolrCachingPathWeight weight, - LeafReaderContext context, - SolrIndexSearcher searcher, - SolrPathQuery wrappedPathQuery) throws IOException - { - DocSet results = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_PATH_CACHE, wrappedPathQuery); - if (results == null) - { - // Cache miss: get path query results and cache them - WrappedQuery wrapped = new WrappedQuery(wrappedPathQuery); - wrapped.setCache(false); - results = searcher.getDocSet(wrapped); - searcher.cacheInsert(CacheConstants.ALFRESCO_PATH_CACHE, wrappedPathQuery, results); - } - - return new SolrCachingPathScorer(weight, results, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +/** + * Caching wrapper for {@link SolrPathQuery}. + * + * @author Matt Ward + */ +public class SolrCachingPathScorer extends AbstractSolrCachingScorer +{ + SolrCachingPathScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + + /** + * Factory method used to create {@link SolrCachingPathScorer} instances. + * @param acceptDocs + */ + public static SolrCachingPathScorer create(SolrCachingPathWeight weight, + LeafReaderContext context, + SolrIndexSearcher searcher, + SolrPathQuery wrappedPathQuery) throws IOException + { + DocSet results = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_PATH_CACHE, wrappedPathQuery); + if (results == null) + { + // Cache miss: get path query results and cache them + WrappedQuery wrapped = new WrappedQuery(wrappedPathQuery); + wrapped.setCache(false); + results = searcher.getDocSet(wrapped); + searcher.cacheInsert(CacheConstants.ALFRESCO_PATH_CACHE, wrappedPathQuery, results); + } + + return new SolrCachingPathScorer(weight, results, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathWeight.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathWeight.java index ffc0f88a8..b5448bab3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathWeight.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrCachingPathWeight.java @@ -1,84 +1,84 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * {@link Weight} implementation for the {@link SolrCachingPathQuery}. - * - * @author Matt Ward - */ -public class SolrCachingPathWeight extends Weight -{ - private SolrIndexSearcher searcher; - private Weight queryWeight; - - public SolrCachingPathWeight(SolrCachingPathQuery cachingPathQuery, SolrIndexSearcher searcher) throws IOException - { - super(cachingPathQuery); - this.searcher = searcher; - queryWeight = cachingPathQuery.pathQuery.createWeight(searcher, false); - } - - @Override - public Explanation explain(LeafReaderContext context, int doc) throws IOException - { - throw new UnsupportedOperationException(); - } - - @Override - public float getValueForNormalization() throws IOException - { - return 1.0f; - } - - @Override - public void normalize(float norm, float topLevelBoost) - { - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrCachingPathScorer.create(this, context, searcher, ((SolrCachingPathQuery)getQuery()).pathQuery); - } - - @Override - public void extractTerms(Set terms) - { - queryWeight.extractTerms(terms); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * {@link Weight} implementation for the {@link SolrCachingPathQuery}. + * + * @author Matt Ward + */ +public class SolrCachingPathWeight extends Weight +{ + private SolrIndexSearcher searcher; + private Weight queryWeight; + + public SolrCachingPathWeight(SolrCachingPathQuery cachingPathQuery, SolrIndexSearcher searcher) throws IOException + { + super(cachingPathQuery); + this.searcher = searcher; + queryWeight = cachingPathQuery.pathQuery.createWeight(searcher, false); + } + + @Override + public Explanation explain(LeafReaderContext context, int doc) throws IOException + { + throw new UnsupportedOperationException(); + } + + @Override + public float getValueForNormalization() throws IOException + { + return 1.0f; + } + + @Override + public void normalize(float norm, float topLevelBoost) + { + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrCachingPathScorer.create(this, context, searcher, ((SolrCachingPathQuery)getQuery()).pathQuery); + } + + @Override + public void extractTerms(Set terms) + { + queryWeight.extractTerms(terms); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrContainerScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrContainerScorer.java index 641d5f505..db53d2153 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrContainerScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrContainerScorer.java @@ -1,581 +1,581 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; - -/** - * The scorer for structured field queries. - * - * A document either matches or it does not, there for the frequency is reported - * as 0.0f or 1.0. - * - * - * - * @author andyh - */ -public class SolrContainerScorer extends Scorer -{ - SolrContainerScorerDocIdSetIterator iterator; - - - /** - * The arguments here follow the same pattern as used by the PhraseQuery. - * (It has the same unused arguments) - * - * @param weight - - * curently unsued - * @param root - - * the term positions for documents with multiple entries - this - * may be null, or contain no matches - it specifies those things - * that appear under multiple categories etc. - * @param positions - - * the structured field positions - where terms should appear - */ - public SolrContainerScorer(Weight weight, PostingsEnum root, StructuredFieldPosition[] positions) - { - super(weight); - iterator = new SolrContainerScorerDocIdSetIterator(root, positions); - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.Scorer#score() - */ - @Override - public float score() throws IOException - { - return 1.0f; - } - - /* (non-Javadoc) - * @see org.apache.lucene.index.DocsEnum#freq() - */ - @Override - public int freq() throws IOException - { - // Could loop through positions and return min freq?? - return 1; - } - - @Override - public int docID() - { - return iterator.docID(); - } - - @Override - public DocIdSetIterator iterator() - { - return iterator; - } - - - - private static class SolrContainerScorerDocIdSetIterator extends DocIdSetIterator - { - - // Positions of documents with multiple structure elements - // e.g have mutiple paths, multiple categories or multiples entries in the - // same category - PostingsEnum root; - - // The Field positions that describe the structure we are trying to match - StructuredFieldPosition[] positions; - - // The minium document found so far - int min = -1; - - // The max document found so far - int max = -1; - - // The next root doc - // Integer.MAX_VALUE and it has gone off the end - int rootDoc = -1; - - // Are there potentially more documents - boolean more = true; - - SolrContainerScorerDocIdSetIterator( PostingsEnum root, StructuredFieldPosition[] positions) - { - this.positions = positions; - this.root = root; - } - - /* - * (non-Javadoc) - * - * @see org.apache.lucene.search.Scorer#next() - */ - private boolean next() throws IOException - { - if(root == null) - { - return false; - } - if (!more) - { - // One of the search terms has no more docuements - return false; - } - - if (max == -1) - { - // We need to initialise - // Just do a next on all terms and check if the first doc matches - doNextOnAll(); - if (found()) - { - return true; - } - // drop through to the normal find sequence - } - - return findNext(); - } - - - /** - * @return - * @throws IOException - */ - private boolean findNext() throws IOException - { - if(root == null) - { - more = false; - max= NO_MORE_DOCS; - return false; - } - - // Move to the next document - - while (more) - { - move(); // may set more to false - if (found()) - { - return true; - } - } - - // If we get here we must have no more documents - return false; - } - - /** - * Check if we have found a match - * - * @return - * @throws IOException - */ - - private boolean found() throws IOException - { - // No predicate test if there are no positions - if (positions.length == 0) - { - return true; - } - - // no more documents - no match - if (!more) - { - return false; - } - - // min and max must point to the same document - if (min != max) - { - return false; - } - - if (rootDoc != max) - { - return false; - } - - // We have duplicate entries - support should be improved but it is not used at the moment - // This should work akin to the leaf scorer - // It would compact the index - // The match must be in a known term range - int count = root.freq(); - int start = 0; - int end = -1; - for (int i = 0; i < count; i++) - { - if (i == 0) - { - // First starts at zero - start = 0; - end = root.nextPosition() ; - } - else - { - start = end + 1; - end = root.nextPosition() ; - } - - if (check(start, end)) - { - return true; - } - } - - // We had checks to do and they all failed. - return false; - } - - /* - * We have all documents at the same state. Now we check the positions of - * the terms. - */ - - private boolean check(int start, int end) throws IOException - { - int offset = checkTail(start, end, 0, 0, true); - // Last match may fail - if (offset == -1) - { - return false; - } - else - { - return true; - } - } - - /** - * For // type pattern matches we need to test patterns of variable greedyness. - * - * - * @param start - first term position - * @param end - end term position marker - * @param currentPosition - current path match index - * @param currentOffset - current path position starting at zero (position being considered is start+offset) - * @return - * @throws IOException - */ - private int checkTail(int start, int end, int currentPosition, int currentOffset, boolean checkLastMatch) throws IOException - { - // pre check last can match some combination or we are done - if(checkLastMatch) - { - if(end-start > 1) - { - StructuredFieldPosition last = getLastPositionNotSelfCheck(); - if(last != null) - { - if(last.matches(start, end, end-start-2) == -1) - { - return -1; - } - } - } - } - - int offset = currentOffset; - for (int i = currentPosition, l = positions.length; i < l; i++) - { - offset = positions[i].matches(start, end, offset); - if (offset == -1) - { - return -1; - } - if (positions[i].isDescendant()) - { - // go up in twos as there are two parts to a QName - for (int j = offset; j < (end - start); j+=2) - { - int newOffset = checkTail(start, end, i + 1, j, false); - if (newOffset != -1) - { - return newOffset; - } - } - return -1; - } - } - - if((start+offset+1) == end) - { - return offset; - } - else - { - return -1; - } - } - - private StructuredFieldPosition getLastPositionNotSelfCheck() - { - for(int i = positions.length-1; i >= 0; i--) - { - if(positions[i].linkSelf()) - { - continue; - } - return positions[i]; - } - return null; - } - - /* - * Move to the next position to consider for a match test - */ - - private void move() throws IOException - { - if (min == max) - { - // If we were at a match just do next on all terms - // They all must move on - doNextOnAll(); - } - else - { - // We are in a range - try and skip to the max position on all terms - // Only some need to move on - some may move past the current max and set a new target - skipToMax(); - } - } - - /* - * Go through all the term positions and try and move to next document. Any - * failure measn we have no more. - * - * This can be used at initialisation and when moving away from an existing - * match. - * - * This will set min, max, more and rootDoc - * - */ - private void doNextOnAll() throws IOException - { - if(root == null) - { - more = false; - max = NO_MORE_DOCS; - return; - } - // Do the terms - int current; - boolean first = true; - for (int i = 0, l = positions.length; i < l; i++) - { - if (positions[i].getCachingTermPositions() != null) - { - if (positions[i].getCachingTermPositions().nextDoc() != NO_MORE_DOCS) - - { - current = positions[i].getCachingTermPositions().docID(); - adjustMinMax(current, first); - first = false; - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - } - } - - // Do the root term - it must always exists as the path could well have multiple entries - // If an entry in the index does not have a root terminal it is broken - if (root.nextDoc() != NO_MORE_DOCS) - { - rootDoc = root.docID(); - if(first) - { - adjustMinMax(rootDoc, first); - } - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - if (root.docID() < max) - { - if (root.advance(max) != NO_MORE_DOCS) - { - rootDoc = root.docID(); - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - } - } - - /* - * Try and skip all those term positions at documents less than the current - * max up to value. This is quite likely to fail and leave us with (min != - * max) but that is OK, we try again. - * - * It is possible that max increases as we process terms, this is OK. We - * just failed to skip to a given value of max and start doing the next. - */ - private void skipToMax() throws IOException - { - // Do the terms - int current; - for (int i = 0, l = positions.length; i < l; i++) - { - if (i == 0) - { - min = max; - } - if (positions[i].getCachingTermPositions() != null) - { - if (positions[i].getCachingTermPositions().docID() < max) - { - if (positions[i].getCachingTermPositions().advance(max) != NO_MORE_DOCS) - { - current = positions[i].getCachingTermPositions().docID(); - adjustMinMax(current, false); - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - } - } - } - - // Do the root - if (root.docID() < max) - { - if (root.advance(max) != NO_MORE_DOCS) - { - rootDoc = root.docID(); - } - else - { - more = false; - max = NO_MORE_DOCS; - return; - } - } - } - - /* - * Adjust the min and max values Convenience boolean to set or adjust the - * minimum. - */ - private void adjustMinMax(int doc, boolean setMin) - { - - if (max < doc) - { - max = doc; - } - - if (setMin) - { - min = doc; - } - else if (min > doc) - { - min = doc; - } - } - - - - /* (non-Javadoc) - * @see org.apache.lucene.search.DocIdSetIterator#docID() - */ - @Override - public int docID() - { - return max; - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.DocIdSetIterator#nextDoc() - */ - @Override - public int nextDoc() throws IOException - { - boolean found = next(); - if(found) - { - return docID(); - } - else - { - return NO_MORE_DOCS; - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.DocIdSetIterator#advance(int) - */ - @Override - public int advance(int target) throws IOException - { - if(target == NO_MORE_DOCS) - { - more = false; - max = NO_MORE_DOCS; - return NO_MORE_DOCS; - } - - max = target; - boolean found = findNext(); - if(found) - { - return docID(); - } - else - { - return NO_MORE_DOCS; - } - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.DocIdSetIterator#cost() - */ - @Override - public long cost() - { - return 1; - } - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.apache.lucene.index.PostingsEnum; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; + +/** + * The scorer for structured field queries. + * + * A document either matches or it does not, there for the frequency is reported + * as 0.0f or 1.0. + * + * + * + * @author andyh + */ +public class SolrContainerScorer extends Scorer +{ + SolrContainerScorerDocIdSetIterator iterator; + + + /** + * The arguments here follow the same pattern as used by the PhraseQuery. + * (It has the same unused arguments) + * + * @param weight - + * curently unsued + * @param root - + * the term positions for documents with multiple entries - this + * may be null, or contain no matches - it specifies those things + * that appear under multiple categories etc. + * @param positions - + * the structured field positions - where terms should appear + */ + public SolrContainerScorer(Weight weight, PostingsEnum root, StructuredFieldPosition[] positions) + { + super(weight); + iterator = new SolrContainerScorerDocIdSetIterator(root, positions); + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.Scorer#score() + */ + @Override + public float score() throws IOException + { + return 1.0f; + } + + /* (non-Javadoc) + * @see org.apache.lucene.index.DocsEnum#freq() + */ + @Override + public int freq() throws IOException + { + // Could loop through positions and return min freq?? + return 1; + } + + @Override + public int docID() + { + return iterator.docID(); + } + + @Override + public DocIdSetIterator iterator() + { + return iterator; + } + + + + private static class SolrContainerScorerDocIdSetIterator extends DocIdSetIterator + { + + // Positions of documents with multiple structure elements + // e.g have mutiple paths, multiple categories or multiples entries in the + // same category + PostingsEnum root; + + // The Field positions that describe the structure we are trying to match + StructuredFieldPosition[] positions; + + // The minium document found so far + int min = -1; + + // The max document found so far + int max = -1; + + // The next root doc + // Integer.MAX_VALUE and it has gone off the end + int rootDoc = -1; + + // Are there potentially more documents + boolean more = true; + + SolrContainerScorerDocIdSetIterator( PostingsEnum root, StructuredFieldPosition[] positions) + { + this.positions = positions; + this.root = root; + } + + /* + * (non-Javadoc) + * + * @see org.apache.lucene.search.Scorer#next() + */ + private boolean next() throws IOException + { + if(root == null) + { + return false; + } + if (!more) + { + // One of the search terms has no more docuements + return false; + } + + if (max == -1) + { + // We need to initialise + // Just do a next on all terms and check if the first doc matches + doNextOnAll(); + if (found()) + { + return true; + } + // drop through to the normal find sequence + } + + return findNext(); + } + + + /** + * @return + * @throws IOException + */ + private boolean findNext() throws IOException + { + if(root == null) + { + more = false; + max= NO_MORE_DOCS; + return false; + } + + // Move to the next document + + while (more) + { + move(); // may set more to false + if (found()) + { + return true; + } + } + + // If we get here we must have no more documents + return false; + } + + /** + * Check if we have found a match + * + * @return + * @throws IOException + */ + + private boolean found() throws IOException + { + // No predicate test if there are no positions + if (positions.length == 0) + { + return true; + } + + // no more documents - no match + if (!more) + { + return false; + } + + // min and max must point to the same document + if (min != max) + { + return false; + } + + if (rootDoc != max) + { + return false; + } + + // We have duplicate entries - support should be improved but it is not used at the moment + // This should work akin to the leaf scorer + // It would compact the index + // The match must be in a known term range + int count = root.freq(); + int start = 0; + int end = -1; + for (int i = 0; i < count; i++) + { + if (i == 0) + { + // First starts at zero + start = 0; + end = root.nextPosition() ; + } + else + { + start = end + 1; + end = root.nextPosition() ; + } + + if (check(start, end)) + { + return true; + } + } + + // We had checks to do and they all failed. + return false; + } + + /* + * We have all documents at the same state. Now we check the positions of + * the terms. + */ + + private boolean check(int start, int end) throws IOException + { + int offset = checkTail(start, end, 0, 0, true); + // Last match may fail + if (offset == -1) + { + return false; + } + else + { + return true; + } + } + + /** + * For // type pattern matches we need to test patterns of variable greedyness. + * + * + * @param start - first term position + * @param end - end term position marker + * @param currentPosition - current path match index + * @param currentOffset - current path position starting at zero (position being considered is start+offset) + * @return + * @throws IOException + */ + private int checkTail(int start, int end, int currentPosition, int currentOffset, boolean checkLastMatch) throws IOException + { + // pre check last can match some combination or we are done + if(checkLastMatch) + { + if(end-start > 1) + { + StructuredFieldPosition last = getLastPositionNotSelfCheck(); + if(last != null) + { + if(last.matches(start, end, end-start-2) == -1) + { + return -1; + } + } + } + } + + int offset = currentOffset; + for (int i = currentPosition, l = positions.length; i < l; i++) + { + offset = positions[i].matches(start, end, offset); + if (offset == -1) + { + return -1; + } + if (positions[i].isDescendant()) + { + // go up in twos as there are two parts to a QName + for (int j = offset; j < (end - start); j+=2) + { + int newOffset = checkTail(start, end, i + 1, j, false); + if (newOffset != -1) + { + return newOffset; + } + } + return -1; + } + } + + if((start+offset+1) == end) + { + return offset; + } + else + { + return -1; + } + } + + private StructuredFieldPosition getLastPositionNotSelfCheck() + { + for(int i = positions.length-1; i >= 0; i--) + { + if(positions[i].linkSelf()) + { + continue; + } + return positions[i]; + } + return null; + } + + /* + * Move to the next position to consider for a match test + */ + + private void move() throws IOException + { + if (min == max) + { + // If we were at a match just do next on all terms + // They all must move on + doNextOnAll(); + } + else + { + // We are in a range - try and skip to the max position on all terms + // Only some need to move on - some may move past the current max and set a new target + skipToMax(); + } + } + + /* + * Go through all the term positions and try and move to next document. Any + * failure measn we have no more. + * + * This can be used at initialisation and when moving away from an existing + * match. + * + * This will set min, max, more and rootDoc + * + */ + private void doNextOnAll() throws IOException + { + if(root == null) + { + more = false; + max = NO_MORE_DOCS; + return; + } + // Do the terms + int current; + boolean first = true; + for (int i = 0, l = positions.length; i < l; i++) + { + if (positions[i].getCachingTermPositions() != null) + { + if (positions[i].getCachingTermPositions().nextDoc() != NO_MORE_DOCS) + + { + current = positions[i].getCachingTermPositions().docID(); + adjustMinMax(current, first); + first = false; + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + } + } + + // Do the root term - it must always exists as the path could well have multiple entries + // If an entry in the index does not have a root terminal it is broken + if (root.nextDoc() != NO_MORE_DOCS) + { + rootDoc = root.docID(); + if(first) + { + adjustMinMax(rootDoc, first); + } + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + if (root.docID() < max) + { + if (root.advance(max) != NO_MORE_DOCS) + { + rootDoc = root.docID(); + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + } + } + + /* + * Try and skip all those term positions at documents less than the current + * max up to value. This is quite likely to fail and leave us with (min != + * max) but that is OK, we try again. + * + * It is possible that max increases as we process terms, this is OK. We + * just failed to skip to a given value of max and start doing the next. + */ + private void skipToMax() throws IOException + { + // Do the terms + int current; + for (int i = 0, l = positions.length; i < l; i++) + { + if (i == 0) + { + min = max; + } + if (positions[i].getCachingTermPositions() != null) + { + if (positions[i].getCachingTermPositions().docID() < max) + { + if (positions[i].getCachingTermPositions().advance(max) != NO_MORE_DOCS) + { + current = positions[i].getCachingTermPositions().docID(); + adjustMinMax(current, false); + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + } + } + } + + // Do the root + if (root.docID() < max) + { + if (root.advance(max) != NO_MORE_DOCS) + { + rootDoc = root.docID(); + } + else + { + more = false; + max = NO_MORE_DOCS; + return; + } + } + } + + /* + * Adjust the min and max values Convenience boolean to set or adjust the + * minimum. + */ + private void adjustMinMax(int doc, boolean setMin) + { + + if (max < doc) + { + max = doc; + } + + if (setMin) + { + min = doc; + } + else if (min > doc) + { + min = doc; + } + } + + + + /* (non-Javadoc) + * @see org.apache.lucene.search.DocIdSetIterator#docID() + */ + @Override + public int docID() + { + return max; + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.DocIdSetIterator#nextDoc() + */ + @Override + public int nextDoc() throws IOException + { + boolean found = next(); + if(found) + { + return docID(); + } + else + { + return NO_MORE_DOCS; + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.DocIdSetIterator#advance(int) + */ + @Override + public int advance(int target) throws IOException + { + if(target == NO_MORE_DOCS) + { + more = false; + max = NO_MORE_DOCS; + return NO_MORE_DOCS; + } + + max = target; + boolean found = findNext(); + if(found) + { + return docID(); + } + else + { + return NO_MORE_DOCS; + } + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.DocIdSetIterator#cost() + */ + @Override + public long cost() + { + return 1; + } + } + +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedQuery.java index 3f2899f13..8c2fd2523 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedQuery.java @@ -1,90 +1,90 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * - * @author Matt Ward - */ -public class SolrDeniedQuery extends AbstractAuthorityQuery -{ - public SolrDeniedQuery(String authority) - { - super(authority); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_DENIED).append(':'); - stringBuilder.append(authority); - return stringBuilder.toString(); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean requiresScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrDenyQueryWeight((SolrIndexSearcher)searcher, this, authority); - } - - private class SolrDenyQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrDenyQueryWeight(SolrIndexSearcher searcher, Query query, String reader) throws IOException - { - super(searcher, false, query, QueryConstants.FIELD_DENIED, reader); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrDeniedScorer.createDenyScorer(this, context, searcher, authority); - } - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_DENIED, authority)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * + * @author Matt Ward + */ +public class SolrDeniedQuery extends AbstractAuthorityQuery +{ + public SolrDeniedQuery(String authority) + { + super(authority); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_DENIED).append(':'); + stringBuilder.append(authority); + return stringBuilder.toString(); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean requiresScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrDenyQueryWeight((SolrIndexSearcher)searcher, this, authority); + } + + private class SolrDenyQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrDenyQueryWeight(SolrIndexSearcher searcher, Query query, String reader) throws IOException + { + super(searcher, false, query, QueryConstants.FIELD_DENIED, reader); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrDeniedScorer.createDenyScorer(this, context, searcher, authority); + } + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_DENIED, authority)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedScorer.java index 6a86623fc..4b5d29682 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDeniedScorer.java @@ -1,89 +1,89 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Find the set of docs denied to an authority. - * - * @author Matt Ward - */ -public class SolrDeniedScorer extends AbstractSolrCachingScorer -{ - SolrDeniedScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrDeniedScorer createDenyScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException - { - DocSet deniedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_DENIED_CACHE, authority); - - if (deniedDocs == null) - { - // Cache miss: query the index for ACL docs where the denial matches the authority. - DocSet aclDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_DENIED, authority))); - - // Allocate a bitset to store the results. - deniedDocs = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - // Translate from ACL docs to real docs - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); - SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); - Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); - // Find real docs that match the ACL ID - DocSet docsForAclId = searcher.getDocSet(query); - deniedDocs = deniedDocs.union(docsForAclId); - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - deniedDocs = deniedDocs.andNot(aclDocs); - } - - searcher.cacheInsert(CacheConstants.ALFRESCO_DENIED_CACHE, authority, deniedDocs); - } - return new SolrDeniedScorer(weight, deniedDocs, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Find the set of docs denied to an authority. + * + * @author Matt Ward + */ +public class SolrDeniedScorer extends AbstractSolrCachingScorer +{ + SolrDeniedScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrDeniedScorer createDenyScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException + { + DocSet deniedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_DENIED_CACHE, authority); + + if (deniedDocs == null) + { + // Cache miss: query the index for ACL docs where the denial matches the authority. + DocSet aclDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_DENIED, authority))); + + // Allocate a bitset to store the results. + deniedDocs = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + // Translate from ACL docs to real docs + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); + SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); + Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); + // Find real docs that match the ACL ID + DocSet docsForAclId = searcher.getDocSet(query); + deniedDocs = deniedDocs.union(docsForAclId); + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + deniedDocs = deniedDocs.andNot(aclDocs); + } + + searcher.cacheInsert(CacheConstants.ALFRESCO_DENIED_CACHE, authority, deniedDocs); + } + return new SolrDeniedScorer(weight, deniedDocs, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetQuery.java index be6999a80..e7f6329b3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetQuery.java @@ -1,156 +1,156 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DelegatingCollector; -import org.apache.solr.search.PostFilter; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Query for a set of denied authorities. - * - * @author Joel Bernstein - */ -public class SolrDenySetQuery extends AbstractAuthoritySetQuery implements PostFilter -{ - public SolrDenySetQuery(String authorities) - { - super(authorities); - } - - public int getCost() - { - return 201; - } - - public void setCost(int cost) - { - - } - - public void setCache(boolean cache) - { - - } - - public boolean getCache() { - return true; - } - - public boolean getCacheSep() - { - return false; - } - - public void setCacheSep(boolean cacheSep) - { - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean requiresScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - BitsFilter denyFilter = getACLFilter(auths, QueryConstants.FIELD_DENIED, (SolrIndexSearcher) searcher); - return new ConstantScoreQuery(denyFilter).createWeight(searcher, false); - } - - public DelegatingCollector getFilterCollector(IndexSearcher searcher) - { - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - try - { - HybridBitSet denySet = getACLSet(auths, QueryConstants.FIELD_DENIED, (SolrIndexSearcher) searcher); - if(denySet instanceof EmptyHybridBitSet) - { - return new AllAccessCollector(); - } - else - { - return new AccessControlCollector(denySet); - } - } - catch(Exception e) - { - throw new RuntimeException(e); - } - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_DENYSET).append(':'); - stringBuilder.append(authorities); - return stringBuilder.toString(); - } - - class AccessControlCollector extends DelegatingCollector - { - private HybridBitSet aclIds; - private NumericDocValues fieldValues; - - public AccessControlCollector(HybridBitSet aclIds) - { - this.aclIds=aclIds; - } - - public boolean acceptsDocsOutOfOrder() - { - return false; - } - - public void doSetNextReader(LeafReaderContext context) throws IOException - { - super.doSetNextReader(context); - this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); - } - - public void collect(int doc) throws IOException{ - - long aclId = this.fieldValues.get(doc); - - if(!aclIds.get(aclId)) - { - super.collect(doc); - } - - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DelegatingCollector; +import org.apache.solr.search.PostFilter; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Query for a set of denied authorities. + * + * @author Joel Bernstein + */ +public class SolrDenySetQuery extends AbstractAuthoritySetQuery implements PostFilter +{ + public SolrDenySetQuery(String authorities) + { + super(authorities); + } + + public int getCost() + { + return 201; + } + + public void setCost(int cost) + { + + } + + public void setCache(boolean cache) + { + + } + + public boolean getCache() { + return true; + } + + public boolean getCacheSep() + { + return false; + } + + public void setCacheSep(boolean cacheSep) + { + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean requiresScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + BitsFilter denyFilter = getACLFilter(auths, QueryConstants.FIELD_DENIED, (SolrIndexSearcher) searcher); + return new ConstantScoreQuery(denyFilter).createWeight(searcher, false); + } + + public DelegatingCollector getFilterCollector(IndexSearcher searcher) + { + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + try + { + HybridBitSet denySet = getACLSet(auths, QueryConstants.FIELD_DENIED, (SolrIndexSearcher) searcher); + if(denySet instanceof EmptyHybridBitSet) + { + return new AllAccessCollector(); + } + else + { + return new AccessControlCollector(denySet); + } + } + catch(Exception e) + { + throw new RuntimeException(e); + } + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_DENYSET).append(':'); + stringBuilder.append(authorities); + return stringBuilder.toString(); + } + + class AccessControlCollector extends DelegatingCollector + { + private HybridBitSet aclIds; + private NumericDocValues fieldValues; + + public AccessControlCollector(HybridBitSet aclIds) + { + this.aclIds=aclIds; + } + + public boolean acceptsDocsOutOfOrder() + { + return false; + } + + public void doSetNextReader(LeafReaderContext context) throws IOException + { + super.doSetNextReader(context); + this.fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, context.reader()); + } + + public void collect(int doc) throws IOException{ + + long aclId = this.fieldValues.get(doc); + + if(!aclIds.get(aclId)) + { + super.collect(doc); + } + + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer.java index 6d21b23d9..398480b2a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer2.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer2.java index 62f991b78..fe951a5c7 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer2.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrDenySetScorer2.java @@ -1,125 +1,125 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.HashSet; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -public class SolrDenySetScorer2 extends AbstractSolrCachingScorer -{ - - SolrDenySetScorer2(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrDenySetScorer2 createDenySetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException - { - DocSet deniedDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_DENIED_CACHE, authorities); - - if (deniedDocSet == null) - { - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - deniedDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - for(String current : auths) - { - bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_DENIED, current)), Occur.SHOULD); - } - WrappedQuery wrapped = new WrappedQuery(bQuery.build()); - wrapped.setCache(false); - - DocSet aclDocs = searcher.getDocSet(wrapped); - - HashSet aclsFound = new HashSet(aclDocs.size()); - NumericDocValues aclDocValues = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID); - - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = aclDocValues.get(docID); - aclsFound.add(getLong(aclID)); - } - - if(aclsFound.size() > 0) - { - for(LeafReaderContext readerContext : searcher.getSlowAtomicReader().leaves() ) - { - int maxDoc = readerContext.reader().maxDoc(); - NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, readerContext.reader()); - if(fieldValues != null) - { - for(int i = 0; i < maxDoc; i++) - { - long aclID = fieldValues.get(i); - Long key = getLong(aclID); - if(aclsFound.contains(key)) - { - deniedDocSet.add(readerContext.docBase + i); - } - } - } - - } - } - - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - deniedDocSet = deniedDocSet.andNot(aclDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_DENIED_CACHE, authorities, deniedDocSet); - } - - // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) - // plus check of course, for presence in cache at start of method. - return new SolrDenySetScorer2(weight, deniedDocSet, context, searcher); - - - - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.HashSet; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +public class SolrDenySetScorer2 extends AbstractSolrCachingScorer +{ + + SolrDenySetScorer2(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrDenySetScorer2 createDenySetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException + { + DocSet deniedDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_DENIED_CACHE, authorities); + + if (deniedDocSet == null) + { + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + deniedDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + for(String current : auths) + { + bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_DENIED, current)), Occur.SHOULD); + } + WrappedQuery wrapped = new WrappedQuery(bQuery.build()); + wrapped.setCache(false); + + DocSet aclDocs = searcher.getDocSet(wrapped); + + HashSet aclsFound = new HashSet(aclDocs.size()); + NumericDocValues aclDocValues = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID); + + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = aclDocValues.get(docID); + aclsFound.add(getLong(aclID)); + } + + if(aclsFound.size() > 0) + { + for(LeafReaderContext readerContext : searcher.getSlowAtomicReader().leaves() ) + { + int maxDoc = readerContext.reader().maxDoc(); + NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, readerContext.reader()); + if(fieldValues != null) + { + for(int i = 0; i < maxDoc; i++) + { + long aclID = fieldValues.get(i); + Long key = getLong(aclID); + if(aclsFound.contains(key)) + { + deniedDocSet.add(readerContext.docBase + i); + } + } + } + + } + } + + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + deniedDocSet = deniedDocSet.andNot(aclDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_DENIED_CACHE, authorities, deniedDocSet); + } + + // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) + // plus check of course, for presence in cache at start of method. + return new SolrDenySetScorer2(weight, deniedDocSet, context, searcher); + + + + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerQuery.java index 5884ba683..153b8c824 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerQuery.java @@ -1,127 +1,127 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Collector; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.LeafCollector; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * @author Andy - * - */ -public class SolrOwnerQuery extends AbstractAuthorityQuery -{ - public SolrOwnerQuery(String authority) - { - super(authority); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - - BitsFilter ownerFilter = getOwnerFilter(authority, (SolrIndexSearcher)searcher); - return new ConstantScoreQuery(ownerFilter).createWeight(searcher, false); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_OWNER).append(':'); - stringBuilder.append(authority); - return stringBuilder.toString(); - } - - private BitsFilter getOwnerFilter(String owner, SolrIndexSearcher searcher) throws IOException - { - Query query = new TermQuery(new Term(QueryConstants.FIELD_OWNER, owner)); - BitsFilterCollector collector = new BitsFilterCollector(searcher.getTopReaderContext().leaves().size()); - searcher.search(query, collector); - return collector.getBitsFilter(); - } - - class BitsFilterCollector implements Collector, LeafCollector - { - private List sets; - private FixedBitSet set; - - public BitsFilterCollector(int leafCount) - { - this.sets = new ArrayList(leafCount); - } - - public BitsFilter getBitsFilter() { - return new BitsFilter(sets); - } - - public boolean acceptsDocsOutOfOrder() { - return false; - } - - public void setScorer(Scorer scorer) { - - } - - public void collect(int doc) { - set.set(doc); - } - - @Override - public LeafCollector getLeafCollector(LeafReaderContext context) - throws IOException { - set = new FixedBitSet(context.reader().maxDoc()); - sets.add(set); - return this; - } - - @Override - public boolean needsScores() { - // TODO Auto-generated method stub - return false; - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.LeafCollector; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * @author Andy + * + */ +public class SolrOwnerQuery extends AbstractAuthorityQuery +{ + public SolrOwnerQuery(String authority) + { + super(authority); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + + BitsFilter ownerFilter = getOwnerFilter(authority, (SolrIndexSearcher)searcher); + return new ConstantScoreQuery(ownerFilter).createWeight(searcher, false); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_OWNER).append(':'); + stringBuilder.append(authority); + return stringBuilder.toString(); + } + + private BitsFilter getOwnerFilter(String owner, SolrIndexSearcher searcher) throws IOException + { + Query query = new TermQuery(new Term(QueryConstants.FIELD_OWNER, owner)); + BitsFilterCollector collector = new BitsFilterCollector(searcher.getTopReaderContext().leaves().size()); + searcher.search(query, collector); + return collector.getBitsFilter(); + } + + class BitsFilterCollector implements Collector, LeafCollector + { + private List sets; + private FixedBitSet set; + + public BitsFilterCollector(int leafCount) + { + this.sets = new ArrayList(leafCount); + } + + public BitsFilter getBitsFilter() { + return new BitsFilter(sets); + } + + public boolean acceptsDocsOutOfOrder() { + return false; + } + + public void setScorer(Scorer scorer) { + + } + + public void collect(int doc) { + set.set(doc); + } + + @Override + public LeafCollector getLeafCollector(LeafReaderContext context) + throws IOException { + set = new FixedBitSet(context.reader().maxDoc()); + sets.add(set); + return this; + } + + @Override + public boolean needsScores() { + // TODO Auto-generated method stub + return false; + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerScorer.java index d175adddb..7138b1e47 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerScorer.java @@ -1,75 +1,75 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Find the set of documents for which the specified authority is the owner. - * If the authority is not a user then an empty set is returned. - * - * @author Matt Ward - */ -public class SolrOwnerScorer extends AbstractSolrCachingScorer -{ - SolrOwnerScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrOwnerScorer createOwnerScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException - { - if (AuthorityType.getAuthorityType(authority) == AuthorityType.USER) - { - DocSet ownedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authority); - - if (ownedDocs == null) - { - // Cache miss: query the index for docs where the owner matches the authority. - ownedDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_OWNER, authority))); - searcher.cacheInsert(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authority, ownedDocs); - } - return new SolrOwnerScorer(weight, ownedDocs, context, searcher); - } - - // Return an empty doc set, as the authority isn't a user. - return new SolrOwnerScorer(weight, new BitDocSet(new FixedBitSet(0)), context, searcher); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Find the set of documents for which the specified authority is the owner. + * If the authority is not a user then an empty set is returned. + * + * @author Matt Ward + */ +public class SolrOwnerScorer extends AbstractSolrCachingScorer +{ + SolrOwnerScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrOwnerScorer createOwnerScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException + { + if (AuthorityType.getAuthorityType(authority) == AuthorityType.USER) + { + DocSet ownedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authority); + + if (ownedDocs == null) + { + // Cache miss: query the index for docs where the owner matches the authority. + ownedDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_OWNER, authority))); + searcher.cacheInsert(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authority, ownedDocs); + } + return new SolrOwnerScorer(weight, ownedDocs, context, searcher); + } + + // Return an empty doc set, as the authority isn't a user. + return new SolrOwnerScorer(weight, new BitDocSet(new FixedBitSet(0)), context, searcher); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetQuery.java index b7be3b8f6..dcb31e3d5 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetQuery.java @@ -1,92 +1,92 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * @author Andy - * - */ -public class SolrOwnerSetQuery extends AbstractAuthoritySetQuery -{ - public SolrOwnerSetQuery(String authorities) - { - super(authorities); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrOwnerSetQueryWeight((SolrIndexSearcher)searcher, this, authorities); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_OWNERSET).append(':'); - stringBuilder.append(authorities); - return stringBuilder.toString(); - } - - - private class SolrOwnerSetQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrOwnerSetQueryWeight(SolrIndexSearcher searcher, Query query, String authorities) throws IOException - { - super(searcher, false, query, QueryConstants.FIELD_OWNERSET, authorities); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrOwnerSetScorer.createOwnerSetScorer(this, context, searcher, authorities); - } - - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_OWNERSET, authorities)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * @author Andy + * + */ +public class SolrOwnerSetQuery extends AbstractAuthoritySetQuery +{ + public SolrOwnerSetQuery(String authorities) + { + super(authorities); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrOwnerSetQueryWeight((SolrIndexSearcher)searcher, this, authorities); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_OWNERSET).append(':'); + stringBuilder.append(authorities); + return stringBuilder.toString(); + } + + + private class SolrOwnerSetQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrOwnerSetQueryWeight(SolrIndexSearcher searcher, Query query, String authorities) throws IOException + { + super(searcher, false, query, QueryConstants.FIELD_OWNERSET, authorities); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrOwnerSetScorer.createOwnerSetScorer(this, context, searcher, authorities); + } + + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_OWNERSET, authorities)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetScorer.java index 76a426ed5..e88a7c3af 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrOwnerSetScorer.java @@ -1,92 +1,92 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -/** - * Find the set of documents owned by the specified set of authorities, - * for those authorities that are users (e.g. we're not interested in groups etc.) - * - * @author Matt Ward - */ -public class SolrOwnerSetScorer extends AbstractSolrCachingScorer -{ - /** - * Package private constructor. - * @param acceptDocs - */ - SolrOwnerSetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrOwnerSetScorer createOwnerSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities) throws IOException - { - - DocSet authorityOwnedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authorities); - - if(authorityOwnedDocs == null) - { - // Split the authorities. The first character in the authorities String - // specifies the separator, e.g. ",jbloggs,abeecher" - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - for(String current : auths) - { - if (AuthorityType.getAuthorityType(current) == AuthorityType.USER) - { - bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_OWNER, current)), Occur.SHOULD); - } - } - - WrappedQuery wrapped = new WrappedQuery(bQuery.build()); - wrapped.setCache(false); - authorityOwnedDocs = searcher.getDocSet(wrapped); - - searcher.cacheInsert(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authorities, authorityOwnedDocs); - } - - // TODO: Cache the final set? e.g. searcher.cacheInsert(authorities, authorityOwnedDocs) - return new SolrOwnerSetScorer(weight, authorityOwnedDocs, context, searcher); - - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +/** + * Find the set of documents owned by the specified set of authorities, + * for those authorities that are users (e.g. we're not interested in groups etc.) + * + * @author Matt Ward + */ +public class SolrOwnerSetScorer extends AbstractSolrCachingScorer +{ + /** + * Package private constructor. + * @param acceptDocs + */ + SolrOwnerSetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrOwnerSetScorer createOwnerSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities) throws IOException + { + + DocSet authorityOwnedDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authorities); + + if(authorityOwnedDocs == null) + { + // Split the authorities. The first character in the authorities String + // specifies the separator, e.g. ",jbloggs,abeecher" + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + for(String current : auths) + { + if (AuthorityType.getAuthorityType(current) == AuthorityType.USER) + { + bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_OWNER, current)), Occur.SHOULD); + } + } + + WrappedQuery wrapped = new WrappedQuery(bQuery.build()); + wrapped.setCache(false); + authorityOwnedDocs = searcher.getDocSet(wrapped); + + searcher.cacheInsert(CacheConstants.ALFRESCO_OWNERLOOKUP_CACHE, authorities, authorityOwnedDocs); + } + + // TODO: Cache the final set? e.g. searcher.cacheInsert(authorities, authorityOwnedDocs) + return new SolrOwnerSetScorer(weight, authorityOwnedDocs, context, searcher); + + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathQuery.java index b25f2dbc2..051231996 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathQuery.java @@ -1,337 +1,337 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; - -/** - * An extension to the Lucene query set. This query supports structured queries against paths. The field must have been - * tokenised using the path tokeniser. This class manages linking together an ordered chain of absolute and relative - * positional queries. - * - * @author Andy Hind - */ -public class SolrPathQuery extends Query -{ - private String pathField = "PATH"; - - private int unitSize = 2; - - private List pathStructuredFieldPositions = new ArrayList(); - - private DictionaryService dictionaryService; - - private boolean repeats = false; - - /** - * The base query - * - * @param dictionaryService DictionaryService - */ - - public SolrPathQuery(DictionaryService dictionaryService) - { - super(); - this.dictionaryService = dictionaryService; - } - - public void setQuery(List path) - { - pathStructuredFieldPositions.clear(); - if (path.size() % unitSize != 0) - { - throw new UnsupportedOperationException(); - } - pathStructuredFieldPositions.addAll(path); - } - - public void appendQuery(List sfps) - { - if (sfps.size() != unitSize) - { - throw new UnsupportedOperationException(); - } - - StructuredFieldPosition last = null; - StructuredFieldPosition next = sfps.get(0); - - if (pathStructuredFieldPositions.size() > 0) - { - last = pathStructuredFieldPositions.get(pathStructuredFieldPositions.size() - 1); - } - - if ((last != null) && next.linkParent() && !last.allowslinkingByParent()) - { - return; - } - - if ((last != null) && next.linkSelf() && !last.allowsLinkingBySelf()) - { - return; - } - - pathStructuredFieldPositions.addAll(sfps); - } - - public String getPathField() - { - return pathField; - } - - public void setPathField(String pathField) - { - this.pathField = pathField; - } - - public Term getPathRootTerm() - { - return new Term(getPathField(), ";"); - } - - ArrayList getTerms() - { - ArrayList answer = new ArrayList(pathStructuredFieldPositions.size()); - for (StructuredFieldPosition sfp : pathStructuredFieldPositions) - { - if (sfp.getTermText() != null) - { - Term term = new Term(pathField, sfp.getTermText()); - answer.add(term); - } - } - return answer; - } - - /* - * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) - */ - public Weight createWeight(IndexSearcher searcher, boolean needsScore) - { - return new StructuredFieldWeight(); - } - - /* - * @see java.lang.Object#toString() - */ - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("PATH:"); - for(int i = 0; i < pathStructuredFieldPositions.size(); i+=2) - { - if(pathStructuredFieldPositions.get(i) instanceof AbsoluteStructuredFieldPosition) - { - stringBuilder.append("/"); - if((pathStructuredFieldPositions.get(i).getTermText() == null) && (pathStructuredFieldPositions.get(i+1).getTermText() == null)) - { - stringBuilder.append("*"); - } - else - { - stringBuilder.append("{"); - stringBuilder.append(pathStructuredFieldPositions.get(i).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i).getTermText()); - stringBuilder.append("}:"); - stringBuilder.append(pathStructuredFieldPositions.get(i+1).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i+1).getTermText()); - - } - } - else if(pathStructuredFieldPositions.get(i) instanceof DescendantAndSelfStructuredFieldPosition) - { - stringBuilder.append("//"); - } - else if(pathStructuredFieldPositions.get(i) instanceof RelativeStructuredFieldPosition) - { - stringBuilder.append("/"); - if((pathStructuredFieldPositions.get(i).getTermText() == null) && (pathStructuredFieldPositions.get(i+1).getTermText() == null)) - { - stringBuilder.append("*"); - } - else - { - stringBuilder.append(pathStructuredFieldPositions.get(i).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i).getTermText()); - stringBuilder.append(":"); - stringBuilder.append(pathStructuredFieldPositions.get(i+1).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i+1).getTermText()); - - } - } - else if(pathStructuredFieldPositions.get(i) instanceof SelfAxisStructuredFieldPosition) - { - stringBuilder.append("."); - } - } - return stringBuilder.toString(); - } - - private class StructuredFieldWeight extends Weight - { - public StructuredFieldWeight() - { - super(SolrPathQuery.this); - } - - - /* (non-Javadoc) - * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.AtomicReaderContext, int) - */ - @Override - public Explanation explain(LeafReaderContext context, int doc) throws IOException - { - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.Weight#getValueForNormalization() - */ - @Override - public float getValueForNormalization() throws IOException - { - return 1.0f; - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.Weight#normalize(float, float) - */ - @Override - public void normalize(float norm, float topLevelBoost) - { - - - } - - /* (non-Javadoc) - * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.AtomicReaderContext, org.apache.lucene.util.Bits) - */ - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrPathScorer.createPathScorer(SolrPathQuery.this, context, this, dictionaryService, repeats); - } - - - @Override - public void extractTerms(Set terms) { - throw new UnsupportedOperationException(); - - } - } - - public void removeDescendantAndSelf() - { - while ((getLast() != null) && getLast().linkSelf()) - { - removeLast(); - removeLast(); - } - } - - private StructuredFieldPosition getLast() - - { - if (pathStructuredFieldPositions.size() > 0) - { - return pathStructuredFieldPositions.get(pathStructuredFieldPositions.size() - 1); - } - else - { - return null; - } - } - - private void removeLast() - { - pathStructuredFieldPositions.remove(pathStructuredFieldPositions.size() - 1); - } - - public boolean isEmpty() - { - return pathStructuredFieldPositions.size() == 0; - } - - public List getPathStructuredFieldPositions() - { - return pathStructuredFieldPositions; - } - - public void setRepeats(boolean repeats) - { - this.repeats = repeats; - } - - @Override - public int hashCode() { - int result = pathField != null ? pathField.hashCode() : 0; - result = 31 * result + unitSize; - result = 31 * result + (pathStructuredFieldPositions != null ? pathStructuredFieldPositions.hashCode() : 0); - result = 31 * result + (repeats ? 1 : 0); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (getClass() != obj.getClass()) - return false; - SolrPathQuery other = (SolrPathQuery) obj; - if (pathField == null) - { - if (other.pathField != null) - return false; - } - else if (!pathField.equals(other.pathField)) - return false; - if (pathStructuredFieldPositions == null) - { - if (other.pathStructuredFieldPositions != null) - return false; - } - else if (!pathStructuredFieldPositions.equals(other.pathStructuredFieldPositions)) - return false; - if (repeats != other.repeats) - return false; - if (unitSize != other.unitSize) - return false; - return true; - } - - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; + +/** + * An extension to the Lucene query set. This query supports structured queries against paths. The field must have been + * tokenised using the path tokeniser. This class manages linking together an ordered chain of absolute and relative + * positional queries. + * + * @author Andy Hind + */ +public class SolrPathQuery extends Query +{ + private String pathField = "PATH"; + + private int unitSize = 2; + + private List pathStructuredFieldPositions = new ArrayList(); + + private DictionaryService dictionaryService; + + private boolean repeats = false; + + /** + * The base query + * + * @param dictionaryService DictionaryService + */ + + public SolrPathQuery(DictionaryService dictionaryService) + { + super(); + this.dictionaryService = dictionaryService; + } + + public void setQuery(List path) + { + pathStructuredFieldPositions.clear(); + if (path.size() % unitSize != 0) + { + throw new UnsupportedOperationException(); + } + pathStructuredFieldPositions.addAll(path); + } + + public void appendQuery(List sfps) + { + if (sfps.size() != unitSize) + { + throw new UnsupportedOperationException(); + } + + StructuredFieldPosition last = null; + StructuredFieldPosition next = sfps.get(0); + + if (pathStructuredFieldPositions.size() > 0) + { + last = pathStructuredFieldPositions.get(pathStructuredFieldPositions.size() - 1); + } + + if ((last != null) && next.linkParent() && !last.allowslinkingByParent()) + { + return; + } + + if ((last != null) && next.linkSelf() && !last.allowsLinkingBySelf()) + { + return; + } + + pathStructuredFieldPositions.addAll(sfps); + } + + public String getPathField() + { + return pathField; + } + + public void setPathField(String pathField) + { + this.pathField = pathField; + } + + public Term getPathRootTerm() + { + return new Term(getPathField(), ";"); + } + + ArrayList getTerms() + { + ArrayList answer = new ArrayList(pathStructuredFieldPositions.size()); + for (StructuredFieldPosition sfp : pathStructuredFieldPositions) + { + if (sfp.getTermText() != null) + { + Term term = new Term(pathField, sfp.getTermText()); + answer.add(term); + } + } + return answer; + } + + /* + * @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher) + */ + public Weight createWeight(IndexSearcher searcher, boolean needsScore) + { + return new StructuredFieldWeight(); + } + + /* + * @see java.lang.Object#toString() + */ + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("PATH:"); + for(int i = 0; i < pathStructuredFieldPositions.size(); i+=2) + { + if(pathStructuredFieldPositions.get(i) instanceof AbsoluteStructuredFieldPosition) + { + stringBuilder.append("/"); + if((pathStructuredFieldPositions.get(i).getTermText() == null) && (pathStructuredFieldPositions.get(i+1).getTermText() == null)) + { + stringBuilder.append("*"); + } + else + { + stringBuilder.append("{"); + stringBuilder.append(pathStructuredFieldPositions.get(i).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i).getTermText()); + stringBuilder.append("}:"); + stringBuilder.append(pathStructuredFieldPositions.get(i+1).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i+1).getTermText()); + + } + } + else if(pathStructuredFieldPositions.get(i) instanceof DescendantAndSelfStructuredFieldPosition) + { + stringBuilder.append("//"); + } + else if(pathStructuredFieldPositions.get(i) instanceof RelativeStructuredFieldPosition) + { + stringBuilder.append("/"); + if((pathStructuredFieldPositions.get(i).getTermText() == null) && (pathStructuredFieldPositions.get(i+1).getTermText() == null)) + { + stringBuilder.append("*"); + } + else + { + stringBuilder.append(pathStructuredFieldPositions.get(i).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i).getTermText()); + stringBuilder.append(":"); + stringBuilder.append(pathStructuredFieldPositions.get(i+1).getTermText() == null ? "*" : pathStructuredFieldPositions.get(i+1).getTermText()); + + } + } + else if(pathStructuredFieldPositions.get(i) instanceof SelfAxisStructuredFieldPosition) + { + stringBuilder.append("."); + } + } + return stringBuilder.toString(); + } + + private class StructuredFieldWeight extends Weight + { + public StructuredFieldWeight() + { + super(SolrPathQuery.this); + } + + + /* (non-Javadoc) + * @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.AtomicReaderContext, int) + */ + @Override + public Explanation explain(LeafReaderContext context, int doc) throws IOException + { + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.Weight#getValueForNormalization() + */ + @Override + public float getValueForNormalization() throws IOException + { + return 1.0f; + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.Weight#normalize(float, float) + */ + @Override + public void normalize(float norm, float topLevelBoost) + { + + + } + + /* (non-Javadoc) + * @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.AtomicReaderContext, org.apache.lucene.util.Bits) + */ + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrPathScorer.createPathScorer(SolrPathQuery.this, context, this, dictionaryService, repeats); + } + + + @Override + public void extractTerms(Set terms) { + throw new UnsupportedOperationException(); + + } + } + + public void removeDescendantAndSelf() + { + while ((getLast() != null) && getLast().linkSelf()) + { + removeLast(); + removeLast(); + } + } + + private StructuredFieldPosition getLast() + + { + if (pathStructuredFieldPositions.size() > 0) + { + return pathStructuredFieldPositions.get(pathStructuredFieldPositions.size() - 1); + } + else + { + return null; + } + } + + private void removeLast() + { + pathStructuredFieldPositions.remove(pathStructuredFieldPositions.size() - 1); + } + + public boolean isEmpty() + { + return pathStructuredFieldPositions.size() == 0; + } + + public List getPathStructuredFieldPositions() + { + return pathStructuredFieldPositions; + } + + public void setRepeats(boolean repeats) + { + this.repeats = repeats; + } + + @Override + public int hashCode() { + int result = pathField != null ? pathField.hashCode() : 0; + result = 31 * result + unitSize; + result = 31 * result + (pathStructuredFieldPositions != null ? pathStructuredFieldPositions.hashCode() : 0); + result = 31 * result + (repeats ? 1 : 0); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (getClass() != obj.getClass()) + return false; + SolrPathQuery other = (SolrPathQuery) obj; + if (pathField == null) + { + if (other.pathField != null) + return false; + } + else if (!pathField.equals(other.pathField)) + return false; + if (pathStructuredFieldPositions == null) + { + if (other.pathStructuredFieldPositions != null) + return false; + } + else if (!pathStructuredFieldPositions.equals(other.pathStructuredFieldPositions)) + return false; + if (repeats != other.repeats) + return false; + if (unitSize != other.unitSize) + return false; + return true; + } + + + + +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathScorer.java index 157e8dce3..d8e409b27 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrPathScorer.java @@ -1,128 +1,128 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.ArrayList; - -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; - -public class SolrPathScorer extends Scorer -{ - Scorer scorer; - - SolrPathScorer(Weight weight, Scorer scorer) - { - super(weight); - this.scorer = scorer; - } - - - public static SolrPathScorer createPathScorer(SolrPathQuery solrPathQuery, LeafReaderContext context, Weight weight, DictionaryService dictionarySertvice, boolean repeat) throws IOException - { - -// StructuredFieldPosition last = null; -// if(solrPathQuery.getPathStructuredFieldPositions().size() > 0) -// { -// last = solrPathQuery.getPathStructuredFieldPositions().get(solrPathQuery.getPathStructuredFieldPositions().size() - 1); -// } - - - if (solrPathQuery.getPathStructuredFieldPositions().size() == 0) - { - ArrayList answer = new ArrayList(2); - answer.add(new SelfAxisStructuredFieldPosition()); - answer.add(new SelfAxisStructuredFieldPosition()); - - solrPathQuery.appendQuery(answer); - } - - - for (StructuredFieldPosition sfp : solrPathQuery.getPathStructuredFieldPositions()) - { - if (sfp.getTermText() != null) - { - PostingsEnum p = context.reader().postings(new Term(solrPathQuery.getPathField(), sfp.getTermText()), PostingsEnum.POSITIONS); - if (p == null) - return null; - CachingTermPositions ctp = new CachingTermPositions(p); - sfp.setCachingTermPositions(ctp); - } - } - - SolrContainerScorer cs = null; - - PostingsEnum rootContainerPositions = null; - if (solrPathQuery.getPathRootTerm() != null) - { - rootContainerPositions = context.reader().postings(solrPathQuery.getPathRootTerm(), PostingsEnum.POSITIONS); - } - - if (solrPathQuery.getPathStructuredFieldPositions().size() > 0) - { - cs = new SolrContainerScorer(weight, rootContainerPositions, (StructuredFieldPosition[]) solrPathQuery.getPathStructuredFieldPositions().toArray(new StructuredFieldPosition[] {})); - } - - - return new SolrPathScorer(weight, cs); - } - - @Override - public float score() throws IOException - { - return scorer.score(); - } - - @Override - public int freq() throws IOException - { - return scorer.freq(); - } - - @Override - public int docID() - { - return scorer.docID(); - } - - - @Override - public DocIdSetIterator iterator() - { - return scorer.iterator(); - } - - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.ArrayList; + +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.PostingsEnum; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; + +public class SolrPathScorer extends Scorer +{ + Scorer scorer; + + SolrPathScorer(Weight weight, Scorer scorer) + { + super(weight); + this.scorer = scorer; + } + + + public static SolrPathScorer createPathScorer(SolrPathQuery solrPathQuery, LeafReaderContext context, Weight weight, DictionaryService dictionarySertvice, boolean repeat) throws IOException + { + +// StructuredFieldPosition last = null; +// if(solrPathQuery.getPathStructuredFieldPositions().size() > 0) +// { +// last = solrPathQuery.getPathStructuredFieldPositions().get(solrPathQuery.getPathStructuredFieldPositions().size() - 1); +// } + + + if (solrPathQuery.getPathStructuredFieldPositions().size() == 0) + { + ArrayList answer = new ArrayList(2); + answer.add(new SelfAxisStructuredFieldPosition()); + answer.add(new SelfAxisStructuredFieldPosition()); + + solrPathQuery.appendQuery(answer); + } + + + for (StructuredFieldPosition sfp : solrPathQuery.getPathStructuredFieldPositions()) + { + if (sfp.getTermText() != null) + { + PostingsEnum p = context.reader().postings(new Term(solrPathQuery.getPathField(), sfp.getTermText()), PostingsEnum.POSITIONS); + if (p == null) + return null; + CachingTermPositions ctp = new CachingTermPositions(p); + sfp.setCachingTermPositions(ctp); + } + } + + SolrContainerScorer cs = null; + + PostingsEnum rootContainerPositions = null; + if (solrPathQuery.getPathRootTerm() != null) + { + rootContainerPositions = context.reader().postings(solrPathQuery.getPathRootTerm(), PostingsEnum.POSITIONS); + } + + if (solrPathQuery.getPathStructuredFieldPositions().size() > 0) + { + cs = new SolrContainerScorer(weight, rootContainerPositions, (StructuredFieldPosition[]) solrPathQuery.getPathStructuredFieldPositions().toArray(new StructuredFieldPosition[] {})); + } + + + return new SolrPathScorer(weight, cs); + } + + @Override + public float score() throws IOException + { + return scorer.score(); + } + + @Override + public int freq() throws IOException + { + return scorer.freq(); + } + + @Override + public int docID() + { + return scorer.docID(); + } + + + @Override + public DocIdSetIterator iterator() + { + return scorer.iterator(); + } + + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderQuery.java index fee314665..a3e5178b7 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderQuery.java @@ -1,91 +1,91 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * TODO: comment me! - * @author Matt Ward - */ -public class SolrReaderQuery extends AbstractAuthorityQuery -{ - public SolrReaderQuery(String authority) - { - super(authority); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_READER).append(':'); - stringBuilder.append(authority); - return stringBuilder.toString(); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrReaderQueryWeight((SolrIndexSearcher)searcher, this, authority); - } - - - private class SolrReaderQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrReaderQueryWeight(SolrIndexSearcher searcher, Query query, String reader) throws IOException - { - super(searcher, false, query, QueryConstants.FIELD_READER, reader); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - return SolrReaderScorer.createReaderScorer(this, context, searcher, authority); - } - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_READER, authority)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * TODO: comment me! + * @author Matt Ward + */ +public class SolrReaderQuery extends AbstractAuthorityQuery +{ + public SolrReaderQuery(String authority) + { + super(authority); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_READER).append(':'); + stringBuilder.append(authority); + return stringBuilder.toString(); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrReaderQueryWeight((SolrIndexSearcher)searcher, this, authority); + } + + + private class SolrReaderQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrReaderQueryWeight(SolrIndexSearcher searcher, Query query, String reader) throws IOException + { + super(searcher, false, query, QueryConstants.FIELD_READER, reader); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + return SolrReaderScorer.createReaderScorer(this, context, searcher, authority); + } + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_READER, authority)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderScorer.java index 443c4640c..70d0a0ea8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderScorer.java @@ -1,89 +1,89 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * Find the set of readable documents for a given authority. - * - * @author Matt Ward - */ -public class SolrReaderScorer extends AbstractSolrCachingScorer -{ - SolrReaderScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrReaderScorer createReaderScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException - { - DocSet readableDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authority); - - if (readableDocs == null) - { - // Cache miss: query the index for ACL docs where the reader matches the authority. - DocSet aclDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_READER, authority))); - - // Allocate a bitset to store the results. - readableDocs = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - // Translate from ACL docs to real docs - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); - SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); - Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); - DocSet docsForAclId = searcher.getDocSet(query); - readableDocs = readableDocs.union(docsForAclId); - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - readableDocs = readableDocs.andNot(aclDocs); - } - - searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authority, readableDocs); - } - - return new SolrReaderScorer(weight, readableDocs, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * Find the set of readable documents for a given authority. + * + * @author Matt Ward + */ +public class SolrReaderScorer extends AbstractSolrCachingScorer +{ + SolrReaderScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrReaderScorer createReaderScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authority) throws IOException + { + DocSet readableDocs = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authority); + + if (readableDocs == null) + { + // Cache miss: query the index for ACL docs where the reader matches the authority. + DocSet aclDocs = searcher.getDocSet(new TermQuery(new Term(QueryConstants.FIELD_READER, authority))); + + // Allocate a bitset to store the results. + readableDocs = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + // Translate from ACL docs to real docs + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); + SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); + Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); + DocSet docsForAclId = searcher.getDocSet(query); + readableDocs = readableDocs.union(docsForAclId); + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + readableDocs = readableDocs.andNot(aclDocs); + } + + searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authority, readableDocs); + } + + return new SolrReaderScorer(weight, readableDocs, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetQuery.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetQuery.java index dd0199203..15c2de074 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetQuery.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetQuery.java @@ -1,92 +1,92 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.Set; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.solr.search.SolrIndexSearcher; - -/** - * @author Andy - * @author Matt Ward - */ -public class SolrReaderSetQuery extends AbstractAuthoritySetQuery -{ - public SolrReaderSetQuery(String authorities) - { - super(authorities); - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException - { - if(!(searcher instanceof SolrIndexSearcher)) - { - throw new IllegalStateException("Must have a SolrIndexSearcher"); - } - return new SolrReaderSetQueryWeight((SolrIndexSearcher)searcher, this, authorities); - } - - @Override - public String toString(String field) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(QueryConstants.FIELD_READERSET).append(':'); - stringBuilder.append(authorities); - return stringBuilder.toString(); - } - - private class SolrReaderSetQueryWeight extends AbstractAuthorityQueryWeight - { - public SolrReaderSetQueryWeight(SolrIndexSearcher searcher, Query query, String readers) throws IOException - { - super(searcher, false, query, QueryConstants.FIELD_READERSET, readers); - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException - { - LeafReader reader = context.reader(); - return SolrReaderSetScorer2.createReaderSetScorer(this, context, searcher, authorities, reader); - } - - @Override - public void extractTerms(Set terms) - { - terms.add(new Term(QueryConstants.FIELD_READERSET, authorities)); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.Set; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.solr.search.SolrIndexSearcher; + +/** + * @author Andy + * @author Matt Ward + */ +public class SolrReaderSetQuery extends AbstractAuthoritySetQuery +{ + public SolrReaderSetQuery(String authorities) + { + super(authorities); + } + + @Override + public Weight createWeight(IndexSearcher searcher, boolean needsScore) throws IOException + { + if(!(searcher instanceof SolrIndexSearcher)) + { + throw new IllegalStateException("Must have a SolrIndexSearcher"); + } + return new SolrReaderSetQueryWeight((SolrIndexSearcher)searcher, this, authorities); + } + + @Override + public String toString(String field) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(QueryConstants.FIELD_READERSET).append(':'); + stringBuilder.append(authorities); + return stringBuilder.toString(); + } + + private class SolrReaderSetQueryWeight extends AbstractAuthorityQueryWeight + { + public SolrReaderSetQueryWeight(SolrIndexSearcher searcher, Query query, String readers) throws IOException + { + super(searcher, false, query, QueryConstants.FIELD_READERSET, readers); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException + { + LeafReader reader = context.reader(); + return SolrReaderSetScorer2.createReaderSetScorer(this, context, searcher, authorities, reader); + } + + @Override + public void extractTerms(Set terms) + { + terms.add(new Term(QueryConstants.FIELD_READERSET, authorities)); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer.java index e27a374f8..19b14b6a7 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer.java @@ -1,104 +1,104 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; - -public class SolrReaderSetScorer extends AbstractSolrCachingScorer -{ - - SolrReaderSetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static SolrReaderSetScorer createReaderSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException - { - - DocSet readableDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authorities); - - if (readableDocSet == null) - { - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - readableDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - for(String current : auths) - { - bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_READER, current)), Occur.SHOULD); - } - - DocSet aclDocs = searcher.getDocSet(bQuery.build()); - - BooleanQuery.Builder aQuery = new BooleanQuery.Builder(); - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); - SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); - Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); - aQuery.add(query, Occur.SHOULD); - - if((aQuery.build().clauses().size() > 999) || !it.hasNext()) - { - DocSet docsForAclId = searcher.getDocSet(aQuery.build()); - readableDocSet = readableDocSet.union(docsForAclId); - - aQuery = new BooleanQuery.Builder(); - } - } - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - readableDocSet = readableDocSet.andNot(aclDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authorities, readableDocSet); - } - - // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) - // plus check of course, for presence in cache at start of method. - return new SolrReaderSetScorer(weight, readableDocSet, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; + +public class SolrReaderSetScorer extends AbstractSolrCachingScorer +{ + + SolrReaderSetScorer(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static SolrReaderSetScorer createReaderSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException + { + + DocSet readableDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authorities); + + if (readableDocSet == null) + { + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + readableDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + for(String current : auths) + { + bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_READER, current)), Occur.SHOULD); + } + + DocSet aclDocs = searcher.getDocSet(bQuery.build()); + + BooleanQuery.Builder aQuery = new BooleanQuery.Builder(); + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID).get(docID); + SchemaField schemaField = searcher.getSchema().getField(QueryConstants.FIELD_ACLID); + Query query = schemaField.getType().getFieldQuery(null, schemaField, Long.toString(aclID)); + aQuery.add(query, Occur.SHOULD); + + if((aQuery.build().clauses().size() > 999) || !it.hasNext()) + { + DocSet docsForAclId = searcher.getDocSet(aQuery.build()); + readableDocSet = readableDocSet.union(docsForAclId); + + aQuery = new BooleanQuery.Builder(); + } + } + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + readableDocSet = readableDocSet.andNot(aclDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authorities, readableDocSet); + } + + // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) + // plus check of course, for presence in cache at start of method. + return new SolrReaderSetScorer(weight, readableDocSet, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer2.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer2.java index 027f99a26..5b451c8a8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer2.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrReaderSetScorer2.java @@ -1,121 +1,121 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; -import java.util.HashSet; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.cache.CacheConstants; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; -import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.search.BitDocSet; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocSet; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.WrappedQuery; - -public class SolrReaderSetScorer2 extends AbstractSolrCachingScorer -{ - SolrReaderSetScorer2(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) - { - super(weight, in, context, searcher); - } - - public static AbstractSolrCachingScorer createReaderSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException - { - - DocSet readableDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authorities); - - if (readableDocSet == null) - { - - String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); - - readableDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); - - BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); - for(String current : auths) - { - bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_READER, current)), Occur.SHOULD); - } - WrappedQuery wrapped = new WrappedQuery(bQuery.build()); - wrapped.setCache(false); - - DocSet aclDocs = searcher.getDocSet(wrapped); - - HashSet aclsFound = new HashSet(aclDocs.size()); - NumericDocValues aclDocValues = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID); - - for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) - { - int docID = it.nextDoc(); - // Obtain the ACL ID for this ACL doc. - long aclID = aclDocValues.get(docID); - aclsFound.add(getLong(aclID)); - } - - if(aclsFound.size() > 0) - { - for(LeafReaderContext readerContext : searcher.getSlowAtomicReader().leaves() ) - { - int maxDoc = readerContext.reader().maxDoc(); - NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, readerContext.reader()); - if(fieldValues != null) - { - for(int i = 0; i < maxDoc ; i++) - { - long aclID = fieldValues.get(i); - Long key = getLong(aclID); - if(aclsFound.contains(key)) - { - readableDocSet.add(readerContext.docBase + i); - } - } - } - - } - } - - // Exclude the ACL docs from the results, we only want real docs that match. - // Probably not very efficient, what we really want is remove(docID) - readableDocSet = readableDocSet.andNot(aclDocs); - searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authorities, readableDocSet); - } - - // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) - // plus check of course, for presence in cache at start of method. - return new SolrReaderSetScorer2(weight, readableDocSet, context, searcher); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; +import java.util.HashSet; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.cache.CacheConstants; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Weight; +import org.apache.lucene.util.FixedBitSet; +import org.apache.solr.search.BitDocSet; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.WrappedQuery; + +public class SolrReaderSetScorer2 extends AbstractSolrCachingScorer +{ + SolrReaderSetScorer2(Weight weight, DocSet in, LeafReaderContext context, SolrIndexSearcher searcher) + { + super(weight, in, context, searcher); + } + + public static AbstractSolrCachingScorer createReaderSetScorer(Weight weight, LeafReaderContext context, SolrIndexSearcher searcher, String authorities, LeafReader reader) throws IOException + { + + DocSet readableDocSet = (DocSet) searcher.cacheLookup(CacheConstants.ALFRESCO_READER_CACHE, authorities); + + if (readableDocSet == null) + { + + String[] auths = authorities.substring(1).split(authorities.substring(0, 1)); + + readableDocSet = new BitDocSet(new FixedBitSet(searcher.maxDoc())); + + BooleanQuery.Builder bQuery = new BooleanQuery.Builder(); + for(String current : auths) + { + bQuery.add(new TermQuery(new Term(QueryConstants.FIELD_READER, current)), Occur.SHOULD); + } + WrappedQuery wrapped = new WrappedQuery(bQuery.build()); + wrapped.setCache(false); + + DocSet aclDocs = searcher.getDocSet(wrapped); + + HashSet aclsFound = new HashSet(aclDocs.size()); + NumericDocValues aclDocValues = searcher.getSlowAtomicReader().getNumericDocValues(QueryConstants.FIELD_ACLID); + + for (DocIterator it = aclDocs.iterator(); it.hasNext(); /**/) + { + int docID = it.nextDoc(); + // Obtain the ACL ID for this ACL doc. + long aclID = aclDocValues.get(docID); + aclsFound.add(getLong(aclID)); + } + + if(aclsFound.size() > 0) + { + for(LeafReaderContext readerContext : searcher.getSlowAtomicReader().leaves() ) + { + int maxDoc = readerContext.reader().maxDoc(); + NumericDocValues fieldValues = DocValuesCache.getNumericDocValues(QueryConstants.FIELD_ACLID, readerContext.reader()); + if(fieldValues != null) + { + for(int i = 0; i < maxDoc ; i++) + { + long aclID = fieldValues.get(i); + Long key = getLong(aclID); + if(aclsFound.contains(key)) + { + readableDocSet.add(readerContext.docBase + i); + } + } + } + + } + } + + // Exclude the ACL docs from the results, we only want real docs that match. + // Probably not very efficient, what we really want is remove(docID) + readableDocSet = readableDocSet.andNot(aclDocs); + searcher.cacheInsert(CacheConstants.ALFRESCO_READER_CACHE, authorities, readableDocSet); + } + + // TODO: cache the full set? e.g. searcher.cacheInsert(CacheConstants.ALFRESCO_READERSET_CACHE, authorities, readableDocSet) + // plus check of course, for presence in cache at start of method. + return new SolrReaderSetScorer2(weight, readableDocSet, context, searcher); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrXPathHandler.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrXPathHandler.java index 110155e35..4f0cd4991 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrXPathHandler.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/SolrXPathHandler.java @@ -1,533 +1,533 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.util.ArrayList; - -import org.alfresco.repo.search.impl.lucene.analysis.PathTokenFilter; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.ISO9075; -import org.jaxen.saxpath.Axis; -import org.jaxen.saxpath.Operator; -import org.jaxen.saxpath.SAXPathException; -import org.jaxen.saxpath.XPathHandler; -/** - * XPATH handler for lucene - currently only does basic paths - * - * @author andyh - * - */ -public class SolrXPathHandler implements XPathHandler -{ - private SolrPathQuery query; - - private boolean isAbsolutePath = true; - - int absolutePosition = 0; - - private NamespacePrefixResolver namespacePrefixResolver; - - private DictionaryService dictionaryService; - - /** - * - */ - public SolrXPathHandler() - { - super(); - } - - /** - * Get the resulting query - * @return - the query - */ - public SolrPathQuery getQuery() - { - return this.query; - } - - public void endAbsoluteLocationPath() throws SAXPathException - { - // No action - } - - public void endAdditiveExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.ADD: - case Operator.SUBTRACT: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endAllNodeStep() throws SAXPathException - { - // Nothing to do - // Todo: Predicates - } - - public void endAndExpr(boolean create) throws SAXPathException - { - if (create) - { - throw new UnsupportedOperationException(); - } - } - - public void endCommentNodeStep() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endEqualityExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.EQUALS: - case Operator.NOT_EQUALS: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endFilterExpr() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endFunction() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endMultiplicativeExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.MULTIPLY: - case Operator.DIV: - case Operator.MOD: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endNameStep() throws SAXPathException - { - // Do nothing at the moment - // Could have repdicates - } - - public void endOrExpr(boolean create) throws SAXPathException - { - if (create) - { - throw new UnsupportedOperationException(); - } - } - - public void endPathExpr() throws SAXPathException - { - // Already built - } - - public void endPredicate() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endProcessingInstructionNodeStep() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endRelationalExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.GREATER_THAN: - case Operator.GREATER_THAN_EQUALS: - case Operator.LESS_THAN: - case Operator.LESS_THAN_EQUALS: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endRelativeLocationPath() throws SAXPathException - { - // No action - } - - public void endTextNodeStep() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void endUnaryExpr(int op) throws SAXPathException - { - switch (op) - { - case Operator.NO_OP: - break; - case Operator.NEGATIVE: - throw new UnsupportedOperationException(); - default: - throw new UnsupportedOperationException("Unknown operation " + op); - } - } - - public void endUnionExpr(boolean create) throws SAXPathException - { - if (create) - { - throw new UnsupportedOperationException(); - } - } - - public void endXPath() throws SAXPathException - { - // Do nothing at the moment - } - - public void literal(String arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void number(double arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void number(int arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startAbsoluteLocationPath() throws SAXPathException - { - if (!isAbsolutePath) - { - throw new IllegalStateException(); - } - - } - - public void startAdditiveExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startAllNodeStep(int axis) throws SAXPathException - { - switch (axis) - { - case Axis.CHILD: - if (isAbsolutePath) - { - // addAbsolute(null, null); - // We can always do relative stuff - addRelative(null, null); - } - else - { - addRelative(null, null); - } - break; - case Axis.DESCENDANT_OR_SELF: - query.appendQuery(getArrayList(new DescendantAndSelfStructuredFieldPosition(), new DescendantAndSelfStructuredFieldPosition())); - break; - case Axis.SELF: - query.appendQuery(getArrayList(new SelfAxisStructuredFieldPosition(), new SelfAxisStructuredFieldPosition())); - break; - default: - throw new UnsupportedOperationException(); - } - } - - private ArrayList getArrayList(StructuredFieldPosition one, StructuredFieldPosition two) - { - ArrayList answer = new ArrayList(2); - answer.add(one); - answer.add(two); - return answer; - } - - public void startAndExpr() throws SAXPathException - { - // Do nothing - } - - public void startCommentNodeStep(int arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startEqualityExpr() throws SAXPathException - { - // Do nothing - } - - public void startFilterExpr() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startFunction(String arg0, String arg1) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startMultiplicativeExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startNameStep(int axis, String nameSpace, String localName) throws SAXPathException - { - switch (axis) - { - case Axis.CHILD: - if (isAbsolutePath) - { - // addAbsolute(nameSpace, localName); - // we can always do relative stuff - addRelative(nameSpace, localName); - } - else - { - addRelative(nameSpace, localName); - } - break; - default: - throw new UnsupportedOperationException(); - } - - } - - @SuppressWarnings("unused") - private void addAbsolute(String nameSpace, String localName) - { - ArrayList answer = new ArrayList(2); - // TODO: Resolve name space - absolutePosition++; - if ((nameSpace == null) || (nameSpace.length() == 0)) - { - - if(localName.equals("*")) - { - answer.add(new AbsoluteStructuredFieldPosition("*", absolutePosition)); - } - else if (namespacePrefixResolver.getNamespaceURI("") == null) - { - answer.add(new AbsoluteStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT, absolutePosition)); - } - else - { - answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(""), absolutePosition)); - } - - } - else - { - answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace), absolutePosition)); - } - - absolutePosition++; - if ((localName == null) || (localName.length() == 0)) - { - answer.add(new AbsoluteStructuredFieldPosition("*", absolutePosition)); - } - else - { - if(localName.equals("*")) - { - answer.add(new AbsoluteStructuredFieldPosition(localName, absolutePosition)); - } - else - { - answer.add(new AbsoluteStructuredFieldPosition(ISO9075.encode(QName.createValidLocalName(ISO9075.decode(localName))), absolutePosition)); - } - } - query.appendQuery(answer); - - } - - private void addRelative(String nameSpace, String localName) - { - ArrayList answer = new ArrayList(2); - if ((nameSpace == null) || (nameSpace.length() == 0)) - { - if(localName.equals("*")) - { - answer.add(new RelativeStructuredFieldPosition("*")); - } - else if (namespacePrefixResolver.getNamespaceURI("") == null) - { - answer.add(new RelativeStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT)); - } - else if (namespacePrefixResolver.getNamespaceURI("").equals("")) - { - answer.add(new RelativeStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT)); - } - else - { - answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(""))); - } - } - else - { - answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace))); - } - - if ((localName == null) || (localName.length() == 0)) - { - answer.add(new RelativeStructuredFieldPosition("*")); - } - else - { - if(localName.equals("*")) - { - answer.add(new RelativeStructuredFieldPosition(localName)); - } - else - { - answer.add(new RelativeStructuredFieldPosition(ISO9075.encode(QName.createValidLocalName(ISO9075.decode(localName))))); - } - } - query.appendQuery(answer); - } - - public void startOrExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startPathExpr() throws SAXPathException - { - // Just need one! - } - - public void startPredicate() throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startProcessingInstructionNodeStep(int arg0, String arg1) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startRelationalExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startRelativeLocationPath() throws SAXPathException - { - isAbsolutePath = false; - } - - public void startTextNodeStep(int arg0) throws SAXPathException - { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); - } - - public void startUnaryExpr() throws SAXPathException - { - // Do nothing for now - } - - public void startUnionExpr() throws SAXPathException - { - // Do nothing at the moment - } - - public void startXPath() throws SAXPathException - { - query = new SolrPathQuery(dictionaryService); - } - - public void variableReference(String uri, String localName) throws SAXPathException - { - - } - - /** - * Set the service to resolve name space prefixes - * @param namespacePrefixResolver - */ - public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) - { - this.namespacePrefixResolver = namespacePrefixResolver; - } - - /** - * Set the dictionary service. - * @param dictionaryService - */ - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.util.ArrayList; + +import org.alfresco.repo.search.impl.lucene.analysis.PathTokenFilter; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.jaxen.saxpath.Axis; +import org.jaxen.saxpath.Operator; +import org.jaxen.saxpath.SAXPathException; +import org.jaxen.saxpath.XPathHandler; +/** + * XPATH handler for lucene - currently only does basic paths + * + * @author andyh + * + */ +public class SolrXPathHandler implements XPathHandler +{ + private SolrPathQuery query; + + private boolean isAbsolutePath = true; + + int absolutePosition = 0; + + private NamespacePrefixResolver namespacePrefixResolver; + + private DictionaryService dictionaryService; + + /** + * + */ + public SolrXPathHandler() + { + super(); + } + + /** + * Get the resulting query + * @return - the query + */ + public SolrPathQuery getQuery() + { + return this.query; + } + + public void endAbsoluteLocationPath() throws SAXPathException + { + // No action + } + + public void endAdditiveExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.ADD: + case Operator.SUBTRACT: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endAllNodeStep() throws SAXPathException + { + // Nothing to do + // Todo: Predicates + } + + public void endAndExpr(boolean create) throws SAXPathException + { + if (create) + { + throw new UnsupportedOperationException(); + } + } + + public void endCommentNodeStep() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endEqualityExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.EQUALS: + case Operator.NOT_EQUALS: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endFilterExpr() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endFunction() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endMultiplicativeExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.MULTIPLY: + case Operator.DIV: + case Operator.MOD: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endNameStep() throws SAXPathException + { + // Do nothing at the moment + // Could have repdicates + } + + public void endOrExpr(boolean create) throws SAXPathException + { + if (create) + { + throw new UnsupportedOperationException(); + } + } + + public void endPathExpr() throws SAXPathException + { + // Already built + } + + public void endPredicate() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endProcessingInstructionNodeStep() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endRelationalExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.GREATER_THAN: + case Operator.GREATER_THAN_EQUALS: + case Operator.LESS_THAN: + case Operator.LESS_THAN_EQUALS: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endRelativeLocationPath() throws SAXPathException + { + // No action + } + + public void endTextNodeStep() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void endUnaryExpr(int op) throws SAXPathException + { + switch (op) + { + case Operator.NO_OP: + break; + case Operator.NEGATIVE: + throw new UnsupportedOperationException(); + default: + throw new UnsupportedOperationException("Unknown operation " + op); + } + } + + public void endUnionExpr(boolean create) throws SAXPathException + { + if (create) + { + throw new UnsupportedOperationException(); + } + } + + public void endXPath() throws SAXPathException + { + // Do nothing at the moment + } + + public void literal(String arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void number(double arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void number(int arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startAbsoluteLocationPath() throws SAXPathException + { + if (!isAbsolutePath) + { + throw new IllegalStateException(); + } + + } + + public void startAdditiveExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startAllNodeStep(int axis) throws SAXPathException + { + switch (axis) + { + case Axis.CHILD: + if (isAbsolutePath) + { + // addAbsolute(null, null); + // We can always do relative stuff + addRelative(null, null); + } + else + { + addRelative(null, null); + } + break; + case Axis.DESCENDANT_OR_SELF: + query.appendQuery(getArrayList(new DescendantAndSelfStructuredFieldPosition(), new DescendantAndSelfStructuredFieldPosition())); + break; + case Axis.SELF: + query.appendQuery(getArrayList(new SelfAxisStructuredFieldPosition(), new SelfAxisStructuredFieldPosition())); + break; + default: + throw new UnsupportedOperationException(); + } + } + + private ArrayList getArrayList(StructuredFieldPosition one, StructuredFieldPosition two) + { + ArrayList answer = new ArrayList(2); + answer.add(one); + answer.add(two); + return answer; + } + + public void startAndExpr() throws SAXPathException + { + // Do nothing + } + + public void startCommentNodeStep(int arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startEqualityExpr() throws SAXPathException + { + // Do nothing + } + + public void startFilterExpr() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startFunction(String arg0, String arg1) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startMultiplicativeExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startNameStep(int axis, String nameSpace, String localName) throws SAXPathException + { + switch (axis) + { + case Axis.CHILD: + if (isAbsolutePath) + { + // addAbsolute(nameSpace, localName); + // we can always do relative stuff + addRelative(nameSpace, localName); + } + else + { + addRelative(nameSpace, localName); + } + break; + default: + throw new UnsupportedOperationException(); + } + + } + + @SuppressWarnings("unused") + private void addAbsolute(String nameSpace, String localName) + { + ArrayList answer = new ArrayList(2); + // TODO: Resolve name space + absolutePosition++; + if ((nameSpace == null) || (nameSpace.length() == 0)) + { + + if(localName.equals("*")) + { + answer.add(new AbsoluteStructuredFieldPosition("*", absolutePosition)); + } + else if (namespacePrefixResolver.getNamespaceURI("") == null) + { + answer.add(new AbsoluteStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT, absolutePosition)); + } + else + { + answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(""), absolutePosition)); + } + + } + else + { + answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace), absolutePosition)); + } + + absolutePosition++; + if ((localName == null) || (localName.length() == 0)) + { + answer.add(new AbsoluteStructuredFieldPosition("*", absolutePosition)); + } + else + { + if(localName.equals("*")) + { + answer.add(new AbsoluteStructuredFieldPosition(localName, absolutePosition)); + } + else + { + answer.add(new AbsoluteStructuredFieldPosition(ISO9075.encode(QName.createValidLocalName(ISO9075.decode(localName))), absolutePosition)); + } + } + query.appendQuery(answer); + + } + + private void addRelative(String nameSpace, String localName) + { + ArrayList answer = new ArrayList(2); + if ((nameSpace == null) || (nameSpace.length() == 0)) + { + if(localName.equals("*")) + { + answer.add(new RelativeStructuredFieldPosition("*")); + } + else if (namespacePrefixResolver.getNamespaceURI("") == null) + { + answer.add(new RelativeStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT)); + } + else if (namespacePrefixResolver.getNamespaceURI("").equals("")) + { + answer.add(new RelativeStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT)); + } + else + { + answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(""))); + } + } + else + { + answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace))); + } + + if ((localName == null) || (localName.length() == 0)) + { + answer.add(new RelativeStructuredFieldPosition("*")); + } + else + { + if(localName.equals("*")) + { + answer.add(new RelativeStructuredFieldPosition(localName)); + } + else + { + answer.add(new RelativeStructuredFieldPosition(ISO9075.encode(QName.createValidLocalName(ISO9075.decode(localName))))); + } + } + query.appendQuery(answer); + } + + public void startOrExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startPathExpr() throws SAXPathException + { + // Just need one! + } + + public void startPredicate() throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startProcessingInstructionNodeStep(int arg0, String arg1) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startRelationalExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startRelativeLocationPath() throws SAXPathException + { + isAbsolutePath = false; + } + + public void startTextNodeStep(int arg0) throws SAXPathException + { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public void startUnaryExpr() throws SAXPathException + { + // Do nothing for now + } + + public void startUnionExpr() throws SAXPathException + { + // Do nothing at the moment + } + + public void startXPath() throws SAXPathException + { + query = new SolrPathQuery(dictionaryService); + } + + public void variableReference(String uri, String localName) throws SAXPathException + { + + } + + /** + * Set the service to resolve name space prefixes + * @param namespacePrefixResolver + */ + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } + + /** + * Set the dictionary service. + * @param dictionaryService + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldPosition.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldPosition.java index 23d1e4fa2..5479de3ec 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldPosition.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldPosition.java @@ -1,124 +1,124 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.io.IOException; - -/** - * Elements used to test agains path and Qname - * - * @author andyh - */ -public interface StructuredFieldPosition -{ - - /** - * Does this element match - * - * @param start - - * the start postion of the paths terms - * @param end - - * the end position of the paths terms - * @param offset - - * the current offset in the path - * @return returns the next match position (usually offset + 1) or -1 if it - * does not match. - * @throws IOException - */ - public int matches(int start, int end, int offset) throws IOException; - - /** - * If this position is last in the chain and it is terminal it will ensure - * it is an exact match for the length of the chain found. If false, it will - * effectively allow prefix mathces for the likes of descendant-and-below - * style queries. - * - * @return boolean - */ - public boolean isTerminal(); - - /** - * Is this an absolute element; that is, it knows its exact position. - * - * @return boolean - */ - public boolean isAbsolute(); - - /** - * This element only knows its position relative to the previous element. - * - * @return boolean - */ - public boolean isRelative(); - - /** - * Get the test to search for in the term query. This may be null if it - * should not have a term query - * - * @return String - */ - public String getTermText(); - - /** - * If absolute return the position. If relative we could compute the - * position knowing the previous term unless this element is preceded by a - * descendat and below style element - * - * @return int - */ - public int getPosition(); - - /** - * A reference to the caching term positions this element uses. This may be - * null which indicates all terms match, in that case there is no action - * against the index - * - * @param tps CachingTermPositions - */ - public void setCachingTermPositions(CachingTermPositions tps); - - public CachingTermPositions getCachingTermPositions(); - - /** - * Normally paths would require onlt parent chaining. for some it is parent - * and child chaining. - * - * @return boolean - */ - - public boolean linkSelf(); - - public boolean linkParent(); - - public boolean allowslinkingByParent(); - - public boolean allowsLinkingBySelf(); - - public boolean isDescendant(); - - public boolean matchesAll(); -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import java.io.IOException; + +/** + * Elements used to test agains path and Qname + * + * @author andyh + */ +public interface StructuredFieldPosition +{ + + /** + * Does this element match + * + * @param start - + * the start postion of the paths terms + * @param end - + * the end position of the paths terms + * @param offset - + * the current offset in the path + * @return returns the next match position (usually offset + 1) or -1 if it + * does not match. + * @throws IOException + */ + public int matches(int start, int end, int offset) throws IOException; + + /** + * If this position is last in the chain and it is terminal it will ensure + * it is an exact match for the length of the chain found. If false, it will + * effectively allow prefix mathces for the likes of descendant-and-below + * style queries. + * + * @return boolean + */ + public boolean isTerminal(); + + /** + * Is this an absolute element; that is, it knows its exact position. + * + * @return boolean + */ + public boolean isAbsolute(); + + /** + * This element only knows its position relative to the previous element. + * + * @return boolean + */ + public boolean isRelative(); + + /** + * Get the test to search for in the term query. This may be null if it + * should not have a term query + * + * @return String + */ + public String getTermText(); + + /** + * If absolute return the position. If relative we could compute the + * position knowing the previous term unless this element is preceded by a + * descendat and below style element + * + * @return int + */ + public int getPosition(); + + /** + * A reference to the caching term positions this element uses. This may be + * null which indicates all terms match, in that case there is no action + * against the index + * + * @param tps CachingTermPositions + */ + public void setCachingTermPositions(CachingTermPositions tps); + + public CachingTermPositions getCachingTermPositions(); + + /** + * Normally paths would require onlt parent chaining. for some it is parent + * and child chaining. + * + * @return boolean + */ + + public boolean linkSelf(); + + public boolean linkParent(); + + public boolean allowslinkingByParent(); + + public boolean allowsLinkingBySelf(); + + public boolean isDescendant(); + + public boolean matchesAll(); +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldTerm.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldTerm.java index 9fc0075dc..d6fe47d09 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldTerm.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/query/StructuredFieldTerm.java @@ -1,65 +1,65 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import org.apache.lucene.index.Term; - -/** - * @author andyh - */ -public class StructuredFieldTerm -{ - - private Term term; - - private StructuredFieldPosition sfp; - - /** - * - */ - public StructuredFieldTerm(Term term, StructuredFieldPosition sfp) - { - this.term = term; - this.sfp = sfp; - } - - /** - * @return Returns the sfp. - */ - public StructuredFieldPosition getSfp() - { - return sfp; - } - - /** - * @return Returns the term. - */ - public Term getTerm() - { - return term; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import org.apache.lucene.index.Term; + +/** + * @author andyh + */ +public class StructuredFieldTerm +{ + + private Term term; + + private StructuredFieldPosition sfp; + + /** + * + */ + public StructuredFieldTerm(Term term, StructuredFieldPosition sfp) + { + this.term = term; + this.sfp = sfp; + } + + /** + * @return Returns the sfp. + */ + public StructuredFieldPosition getSfp() + { + return sfp; + } + + /** + * @return Returns the term. + */ + public Term getTerm() + { + return term; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/CompositeReader.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/CompositeReader.java index 95648bc0b..ebcb8428d 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/CompositeReader.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/CompositeReader.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -83,4 +83,4 @@ class CompositeReader extends Reader { exhausted.forEach(Utils::silentyClose); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextAnalyzer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextAnalyzer.java index 5ef46d1fa..15c872157 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextAnalyzer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextAnalyzer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -71,4 +71,4 @@ public class LanguagePrefixedTextAnalyzer extends Analyzer return new TokenStreamComponents(decorator, decorator); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextField.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextField.java index efdb13e4c..f321fcc39 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextField.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTextField.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -61,4 +61,4 @@ public class LanguagePrefixedTextField extends TextField setIndexAnalyzer(new LanguagePrefixedTextAnalyzer(schema, AlfrescoAnalyzerWrapper.Mode.INDEX)); setQueryAnalyzer(new LanguagePrefixedTextAnalyzer(schema, AlfrescoAnalyzerWrapper.Mode.QUERY)); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStream.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStream.java index 9b9330e05..bc3d6f080 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStream.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStream.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -373,4 +373,4 @@ public final class LanguagePrefixedTokenStream extends Tokenizer { return language.map(String::length).map(length -> length + 2).orElse(0); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/package-info.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/package-info.java index 1a370e428..0751a8e82 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/package-info.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/schema/highlight/package-info.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -72,4 +72,4 @@ * * */ -package org.alfresco.solr.schema.highlight; +package org.alfresco.solr.schema.highlight; \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/servlet/Solr4X509ServletFilter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/servlet/Solr4X509ServletFilter.java index 7a83308be..3286482f3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/servlet/Solr4X509ServletFilter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/servlet/Solr4X509ServletFilter.java @@ -1,289 +1,289 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.servlet; - -import org.alfresco.web.scripts.servlet.X509ServletFilterBase; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.solr.core.SolrResourceLoader; - -import javax.management.MBeanServer; -import javax.management.MBeanServerFactory; -import javax.management.ObjectName; -import javax.management.Query; -import javax.management.QueryExp; -import javax.servlet.ServletContext; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.Properties; -import java.util.Set; -import java.util.function.Function; - -import static java.util.Optional.ofNullable; - -/** - * The Solr4X509ServletFilter implements the checkEnforce method of the X509ServletFilterBase. - * This allows the configuration of X509 authentication to be toggled on/off through a - * configuration outside of the web.xml. - **/ - -public class Solr4X509ServletFilter extends X509ServletFilterBase -{ - - private static final String SECURE_COMMS = "alfresco.secureComms"; - - private static Log logger = LogFactory.getLog(Solr4X509ServletFilter.class); - - static final int NOT_FOUND_HTTPS_PORT_NUMBER = -1; - - private Function extractPortNumber = name -> - { - try - { - return ofNullable(mxServer().getAttribute(name, "Port")) - .map(Number.class::cast) - .map(Number::intValue) - .orElse(NOT_FOUND_HTTPS_PORT_NUMBER); - } - catch(final Exception exception) - { - logger.error("Error getting https port from MBean " + name, exception); - return NOT_FOUND_HTTPS_PORT_NUMBER; - } - }; - - @Override - protected boolean checkEnforce(ServletContext context) throws IOException - { - /* - * Rely on the SolrResourceLoader to locate the solr home directory. - */ - - int httpsPort = getHttpsPort(); - - if(httpsPort > -1) - { - setHttpsPort(httpsPort); - } - - String solrHome = SolrResourceLoader.locateSolrHome().toString(); - - if(logger.isDebugEnabled()) - { - logger.debug("solrHome:"+solrHome); - } - - /* - * Find the active cores. - */ - List cores = new ArrayList(); - findCores(new File(solrHome), cores); - - /* - * Get the alfresco.secureComms value for each core. - */ - Set secureCommsSet = new HashSet(); - for(File core : cores) - { - collectSecureComms(core, secureCommsSet); - } - - /* - * alfresco.secureComms values should be in sync for each core - */ - - if(secureCommsSet.size() > 1) - { - StringBuilder buf = new StringBuilder(); - int i = 0; - for(String s : secureCommsSet) - { - if(i > 0) - { - buf.append(" | "); - } - buf.append(s); - i++; - } - - throw new IOException("More then one distinct value found for alfresco.secureComms:"+ buf.toString()+ - ". All alfresco.secureComms values must be set to the same value."); - } - - if(secureCommsSet.size() == 0) - { - //No secureComms were found. - return false; - } - - - String secureComms = secureCommsSet.iterator().next(); - - if(logger.isDebugEnabled()) - { - logger.debug("secureComms:"+secureComms); - } - - if("none".equals(secureComms)) - { - return false; - } - else - { - return true; - } - } - - - private void findCores(File dir, List cores) - { - File[] files = dir.listFiles(); - if (files != null) - { - for (File file : files) - { - if (file.isDirectory()) - { - findCores(file, cores); - } - else - { - if ("core.properties".equals(file.getName())) - { - if (logger.isDebugEnabled()) - { - logger.debug("Found core:" + dir.getAbsolutePath()); - } - - cores.add(dir); - } - } - } - } - } - /* - * Scan through all the files and folders under a dir looking for solrcore.properties file. - * Gather the SECURE_COMMS property when found. - */ - - private void collectSecureComms(File base, Set secureCommsSet) throws IOException - { - File[] files = base.listFiles(); - if (files != null) - { - for (File file : files) - { - if (file.isDirectory()) - { - collectSecureComms(file, secureCommsSet); - } - else - { - - if (logger.isDebugEnabled()) - { - logger.debug("scanning file:" + file.getAbsolutePath()); - } - - if ("solrcore.properties".equals(file.getName())) - { - FileReader propReader = null; - Properties props = new Properties(); - try - { - propReader = new FileReader(file); - props.load(propReader); - String prop = props.getProperty(SECURE_COMMS); - - if (prop != null) - { - if (logger.isDebugEnabled()) - { - logger.debug("Found alfresco.secureComms in:" + file.getAbsolutePath() + " : " + prop); - } - secureCommsSet.add(prop); - } - else - { - secureCommsSet.add("none"); - } - } - finally - { - if (propReader != null) - { - propReader.close(); - } - } - } - } - } - } - } - - int getHttpsPort() - { - return connectorMBeanName().map(extractPortNumber).orElse(NOT_FOUND_HTTPS_PORT_NUMBER); - } - - Optional connectorMBeanName() - { - try - { - final QueryExp query = Query.eq(Query.attr("Scheme"), Query.value("https")); - final Set connectors = mxServer().queryNames(null, query); - return connectors - .stream() - .findFirst(); - } - catch (final Exception exception) - { - logger.error("Error getting the Connector MBean.", exception); - return Optional.empty(); - } - } - - MBeanServer mxServer() - { - return ofNullable((MBeanServerFactory.findMBeanServer(null))) - .filter(servers -> !servers.isEmpty()) - .map(Collection::iterator) - .map(Iterator::next) - .orElseThrow(NoSuchElementException::new); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.servlet; + +import org.alfresco.web.scripts.servlet.X509ServletFilterBase; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.solr.core.SolrResourceLoader; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; +import javax.management.Query; +import javax.management.QueryExp; +import javax.servlet.ServletContext; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import java.util.function.Function; + +import static java.util.Optional.ofNullable; + +/** + * The Solr4X509ServletFilter implements the checkEnforce method of the X509ServletFilterBase. + * This allows the configuration of X509 authentication to be toggled on/off through a + * configuration outside of the web.xml. + **/ + +public class Solr4X509ServletFilter extends X509ServletFilterBase +{ + + private static final String SECURE_COMMS = "alfresco.secureComms"; + + private static Log logger = LogFactory.getLog(Solr4X509ServletFilter.class); + + static final int NOT_FOUND_HTTPS_PORT_NUMBER = -1; + + private Function extractPortNumber = name -> + { + try + { + return ofNullable(mxServer().getAttribute(name, "Port")) + .map(Number.class::cast) + .map(Number::intValue) + .orElse(NOT_FOUND_HTTPS_PORT_NUMBER); + } + catch(final Exception exception) + { + logger.error("Error getting https port from MBean " + name, exception); + return NOT_FOUND_HTTPS_PORT_NUMBER; + } + }; + + @Override + protected boolean checkEnforce(ServletContext context) throws IOException + { + /* + * Rely on the SolrResourceLoader to locate the solr home directory. + */ + + int httpsPort = getHttpsPort(); + + if(httpsPort > -1) + { + setHttpsPort(httpsPort); + } + + String solrHome = SolrResourceLoader.locateSolrHome().toString(); + + if(logger.isDebugEnabled()) + { + logger.debug("solrHome:"+solrHome); + } + + /* + * Find the active cores. + */ + List cores = new ArrayList(); + findCores(new File(solrHome), cores); + + /* + * Get the alfresco.secureComms value for each core. + */ + Set secureCommsSet = new HashSet(); + for(File core : cores) + { + collectSecureComms(core, secureCommsSet); + } + + /* + * alfresco.secureComms values should be in sync for each core + */ + + if(secureCommsSet.size() > 1) + { + StringBuilder buf = new StringBuilder(); + int i = 0; + for(String s : secureCommsSet) + { + if(i > 0) + { + buf.append(" | "); + } + buf.append(s); + i++; + } + + throw new IOException("More then one distinct value found for alfresco.secureComms:"+ buf.toString()+ + ". All alfresco.secureComms values must be set to the same value."); + } + + if(secureCommsSet.size() == 0) + { + //No secureComms were found. + return false; + } + + + String secureComms = secureCommsSet.iterator().next(); + + if(logger.isDebugEnabled()) + { + logger.debug("secureComms:"+secureComms); + } + + if("none".equals(secureComms)) + { + return false; + } + else + { + return true; + } + } + + + private void findCores(File dir, List cores) + { + File[] files = dir.listFiles(); + if (files != null) + { + for (File file : files) + { + if (file.isDirectory()) + { + findCores(file, cores); + } + else + { + if ("core.properties".equals(file.getName())) + { + if (logger.isDebugEnabled()) + { + logger.debug("Found core:" + dir.getAbsolutePath()); + } + + cores.add(dir); + } + } + } + } + } + /* + * Scan through all the files and folders under a dir looking for solrcore.properties file. + * Gather the SECURE_COMMS property when found. + */ + + private void collectSecureComms(File base, Set secureCommsSet) throws IOException + { + File[] files = base.listFiles(); + if (files != null) + { + for (File file : files) + { + if (file.isDirectory()) + { + collectSecureComms(file, secureCommsSet); + } + else + { + + if (logger.isDebugEnabled()) + { + logger.debug("scanning file:" + file.getAbsolutePath()); + } + + if ("solrcore.properties".equals(file.getName())) + { + FileReader propReader = null; + Properties props = new Properties(); + try + { + propReader = new FileReader(file); + props.load(propReader); + String prop = props.getProperty(SECURE_COMMS); + + if (prop != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("Found alfresco.secureComms in:" + file.getAbsolutePath() + " : " + prop); + } + secureCommsSet.add(prop); + } + else + { + secureCommsSet.add("none"); + } + } + finally + { + if (propReader != null) + { + propReader.close(); + } + } + } + } + } + } + } + + int getHttpsPort() + { + return connectorMBeanName().map(extractPortNumber).orElse(NOT_FOUND_HTTPS_PORT_NUMBER); + } + + Optional connectorMBeanName() + { + try + { + final QueryExp query = Query.eq(Query.attr("Scheme"), Query.value("https")); + final Set connectors = mxServer().queryNames(null, query); + return connectors + .stream() + .findFirst(); + } + catch (final Exception exception) + { + logger.error("Error getting the Connector MBean.", exception); + return Optional.empty(); + } + } + + MBeanServer mxServer() + { + return ofNullable((MBeanServerFactory.findMBeanServer(null))) + .filter(servers -> !servers.isEmpty()) + .map(Collection::iterator) + .map(Iterator::next) + .orElseThrow(NoSuchElementException::new); + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDModRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDModRouter.java index a62ec7de6..3a7dd790c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDModRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDModRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -67,4 +67,4 @@ public class ACLIDModRouter implements DocRouter { return id % shardCount == shardInstance; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDMurmurRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDMurmurRouter.java index bbed8310a..cc6fa85ec 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDMurmurRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ACLIDMurmurRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -61,4 +61,4 @@ public class ACLIDMurmurRouter implements DocRouter String value = Long.toString(id); return (Math.abs(Hash.murmurhash3_x86_32(value, 0, value.length(), 77)) % numShards) == shardInstance; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java index 82821adad..f020f71a5 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java @@ -1,472 +1,472 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker; - -import static java.util.Optional.ofNullable; - -import static org.alfresco.repo.index.shard.ShardMethodEnum.DB_ID; -import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_KEY_KEY; - -import java.net.ConnectException; -import java.net.SocketTimeoutException; -import java.util.Optional; -import java.util.Properties; -import java.util.concurrent.Semaphore; - -import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.repo.index.shard.ShardMethodEnum; -import org.alfresco.repo.search.impl.QueryParserUtils; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.IndexTrackingShutdownException; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.NodeReport; -import org.alfresco.solr.TrackerState; -import org.alfresco.solr.client.SOLRAPIClient; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Abstract base class that provides common {@link Tracker} behaviour. - * - * @author Matt Ward - */ -public abstract class AbstractTracker implements Tracker -{ - static final long TIME_STEP_32_DAYS_IN_MS = 1000 * 60 * 60 * 24 * 32L; - static final long TIME_STEP_1_HR_IN_MS = 60 * 60 * 1000L; - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTracker.class); - - protected Properties props; - protected SOLRAPIClient client; - InformationServer infoSrv; - protected String coreName; - StoreRef storeRef; - long batchCount; - TrackerStats trackerStats; - boolean runPostModelLoadInit = true; - private int maxLiveSearchers; - private volatile boolean shutdown = false; - - protected volatile TrackerState state; - protected int shardCount; - protected int shardInstance; - ShardMethodEnum shardMethod; - protected boolean transformContent; - String shardTemplate; - protected volatile boolean rollback; - /** - * When rollback is set, original error is also gathered in order to provide detailed logging. - */ - protected Throwable rollbackCausedBy; - protected final Type type; - protected final String trackerId; - - DocRouter docRouter; - - /** - * The property to use for determining the shard. - * Note that this property is not used by all trackers, it is actually managed by the {@link ShardStatePublisher} and - * {@link MetadataTracker}. We put this property here because otherwise we should introduce another supertype layer - * for those two trackers. - */ - protected Optional shardProperty = Optional.empty(); - - /** - * The string representation of the shard key. - * Note that this property is not used by all trackers, it is actually managed by the {@link ShardStatePublisher} and - * {@link MetadataTracker}. We put this property here because otherwise we should introduce another supertype layer - * for those two trackers. - */ - protected Optional shardKey; - - /** - * Default constructor, strictly for testing. - */ - protected AbstractTracker(Type type) - { - this.type = type; - this.trackerId = type + "@" + hashCode(); - } - - protected AbstractTracker(Properties p, SOLRAPIClient client, String coreName, InformationServer informationServer,Type type) - { - this.props = p; - this.client = client; - this.coreName = coreName; - this.infoSrv = informationServer; - - storeRef = new StoreRef(p.getProperty("alfresco.stores", "workspace://SpacesStore")); - batchCount = Integer.parseInt(p.getProperty("alfresco.batch.count", "5000")); - maxLiveSearchers = Integer.parseInt(p.getProperty("alfresco.maxLiveSearchers", "2")); - - shardCount = Integer.parseInt(p.getProperty("shard.count", "1")); - shardInstance = Integer.parseInt(p.getProperty("shard.instance", "0")); - shardMethod = ShardMethodEnum.getShardMethod(p.getProperty("shard.method", DB_ID.name())); - - shardTemplate = p.getProperty("alfresco.template", ""); - - transformContent = Boolean.parseBoolean(p.getProperty("alfresco.index.transformContent", "true")); - - this.trackerStats = this.infoSrv.getTrackerStats(); - - this.type = type; - - this.trackerId = type + "@" + hashCode(); - - shardKey = ofNullable(p.getProperty(SHARD_KEY_KEY)); - - firstUpdateShardProperty(); - - docRouter = DocRouterFactory.getRouter(p, shardMethod); - } - - /** - * Subclasses must implement behaviour that completes the following steps, in order: - * - *

    - *
  1. Purge
  2. - *
  3. Reindex
  4. - *
  5. Index
  6. - *
  7. Track repository
  8. - *
- * - * @param iterationId an identifier which is uniquely associated with a given iteration. - */ - protected abstract void doTrack(String iterationId) throws Throwable; - - - private boolean assertTrackerStateRemainsNull() { - - /* - * This assertion is added to accommodate DistributedAlfrescoSolrTrackerRaceTest. - * The sleep is needed to allow the test case to add a txn into the queue before - * the tracker makes its call to pull transactions from the test repo client. - */ - - try - { - Thread.sleep(5000); - } - catch(Exception e) - { - // Ignore - } - - - /* - * This ensures that getTrackerState does not have the side effect of setting the - * state instance variable. This allows classes outside of the tracker framework - * to safely call getTrackerState without interfering with the trackers design. - */ - - getTrackerState(); - - return state == null; - - } - /** - * Template method - subclasses must implement the {@link Tracker}-specific indexing - * by implementing the abstract method {@link #doTrack(String)}. - */ - @Override - public void track() - { - String iterationId = "IT #" + System.currentTimeMillis(); - - if(getRunLock().availablePermits() == 0) - { - LOGGER.info("[{} / {} / {}] Tracker already registered.", coreName, trackerId, iterationId); - return; - } - - try - { - /* - * The runLock ensures that for each tracker type (metadata, content, commit, cascade) only one tracker will - * be running at a time. - */ - getRunLock().acquire(); - - if (state==null && Boolean.parseBoolean(System.getProperty("alfresco.test", "false"))) - { - assert(assertTrackerStateRemainsNull()); - } - - if(this.state == null) - { - this.state = getTrackerState(); - - LOGGER.debug("[{} / {} / {}] Global Tracker State set to: {}", coreName, trackerId, iterationId, this.state.toString()); - this.state.setRunning(true); - } - else - { - continueState(); - this.state.setRunning(true); - } - - infoSrv.registerTrackerThread(); - - try - { - doTrack(iterationId); - } - catch(IndexTrackingShutdownException t) - { - setRollback(true, t); - LOGGER.info("[{} / {} / {}] Tracking cycle stopped. See the stacktrace below for further details.", coreName, trackerId, iterationId, t); - } - catch(Throwable t) - { - setRollback(true, t); - if (t instanceof SocketTimeoutException || t instanceof ConnectException) - { - LOGGER.warn("[{} / {} / {}] Tracking communication timed out. See the stacktrace below for further details.", coreName, trackerId, iterationId); - LOGGER.debug("[{} / {} / {}] Stack trace", coreName, trackerId, iterationId, t); - } - else - { - LOGGER.error("[{} / {} / {}] Tracking failure. See the stacktrace below for further details.", coreName, trackerId, iterationId, t); - } - } - } - catch (Exception exception) - { - LOGGER.error("[{} / {} / {}] Some problem was detected while resetting the Tracker State. See the stracktrace below for further details." - , coreName, trackerId, iterationId, exception); - } - finally - { - infoSrv.unregisterTrackerThread(); - - ofNullable(state).ifPresent(this::turnOff); - - getRunLock().release(); - } - } - - /** - * At the end of the tracking method, the {@link TrackerState} should be turned off. - * However, during a rollback (that could be started by another tracker) the {@link TrackerState} instance - * could be set to null, even after passing a null check we could get a NPE. - * For that reason, this method turns off the tracker state and ignore any {@link NullPointerException} (actually - * any exception) that could be thrown. - */ - private void turnOff(TrackerState state) { - try - { - state.setRunning(false); - state.setCheck(false); - } catch (Exception exception) - { - LOGGER.error("Unable to properly turn off the TrackerState instance. See the stacktrace below for further details.", exception); - } - } - - public boolean getRollback() - { - return this.rollback; - } - - public Throwable getRollbackCausedBy() - { - return this.rollbackCausedBy; - } - - public void setRollback(boolean rollback, Throwable rollbackCausedBy) - { - this.rollback = rollback; - this.rollbackCausedBy = rollbackCausedBy; - } - - private void continueState() - { - infoSrv.continueState(state); - state.incrementTrackerCycles(); - } - - public synchronized void invalidateState() - { - state = null; - } - - @Override - public synchronized TrackerState getTrackerState() - { - if(this.state != null) - { - return this.state; - } - else - { - return this.infoSrv.getTrackerInitialState(); - } - } - - int getMaxLiveSearchers() - { - return maxLiveSearchers; - } - - void checkShutdown() - { - if(shutdown) - { - throw new IndexTrackingShutdownException(); - } - } - - @Override - public boolean isAlreadyInShutDownMode() - { - return shutdown; - } - - @Override - public void setShutdown(boolean shutdown) - { - this.shutdown = shutdown; - } - - @Override - public void shutdown() - { - setShutdown(true); - } - - /** - * Trackers implementing this method should decide if the Write Lock is applied - * globally for every Tracker Thread (static) or locally for each running Thread - */ - public abstract Semaphore getWriteLock(); - - /** - * Trackers implementing this method should decide if the Run Lock is applied - * globally for every Tracker Thread (static) or locally for each running Thread - */ - public abstract Semaphore getRunLock(); - - public Properties getProps() - { - return props; - } - - public Type getType() - { - return type; - } - - /** - * Set the shard property using the shard key. - */ - void updateShardProperty() - { - shardKey.ifPresent(shardKeyName -> { - Optional updatedShardProperty = getShardProperty(shardKeyName); - if (!shardProperty.equals(updatedShardProperty)) - { - if (updatedShardProperty.isEmpty()) - { - LOGGER.warn("The model defining {} property has been disabled", shardKeyName); - } - else - { - LOGGER.info("New {} property found for {}", SHARD_KEY_KEY, shardKeyName); - } - } - shardProperty = updatedShardProperty; - }); - } - - /** - * Given the field name, returns the name of the property definition. - * If the property definition is not found, Empty optional is returned. - * - * @param field the field name. - * @return the name of the associated property definition if present, Optional.Empty() otherwise - */ - static Optional getShardProperty(String field) - { - if (StringUtils.isBlank(field)) - { - throw new IllegalArgumentException("Sharding property " + SHARD_KEY_KEY + " has not been set."); - } - - AlfrescoSolrDataModel dataModel = AlfrescoSolrDataModel.getInstance(); - NamespaceDAO namespaceDAO = dataModel.getNamespaceDAO(); - DictionaryService dictionaryService = dataModel.getDictionaryService(CMISStrictDictionaryService.DEFAULT); - PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition("http://www.alfresco.org/model/content/1.0", - namespaceDAO, - dictionaryService, - field); - - return ofNullable(propertyDef).map(PropertyDefinition::getName); - } - - /** - * Returns information about the {@link org.alfresco.solr.client.Node} associated with the given dbid. - * - * @param dbid the node identifier. - * @return the {@link org.alfresco.solr.client.Node} associated with the given dbid. - */ - public NodeReport checkNode(Long dbid) - { - NodeReport nodeReport = new NodeReport(); - nodeReport.setDbid(dbid); - - this.infoSrv.addCommonNodeReportInfo(nodeReport); - - return nodeReport; - } - - /** - * Returns the {@link DocRouter} instance in use on this node. - * - * @return the {@link DocRouter} instance in use on this node. - */ - public DocRouter getDocRouter() - { - return this.docRouter; - } - - private void firstUpdateShardProperty() - { - shardKey.ifPresent( shardKeyName -> { - updateShardProperty(); - if (shardProperty.isEmpty()) - { - LOGGER.warn("Sharding property {} was set to {}, but no such property was found.", SHARD_KEY_KEY, shardKeyName); - } - }); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker; + +import static java.util.Optional.ofNullable; + +import static org.alfresco.repo.index.shard.ShardMethodEnum.DB_ID; +import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_KEY_KEY; + +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.Semaphore; + +import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.index.shard.ShardMethodEnum; +import org.alfresco.repo.search.impl.QueryParserUtils; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.IndexTrackingShutdownException; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.NodeReport; +import org.alfresco.solr.TrackerState; +import org.alfresco.solr.client.SOLRAPIClient; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract base class that provides common {@link Tracker} behaviour. + * + * @author Matt Ward + */ +public abstract class AbstractTracker implements Tracker +{ + static final long TIME_STEP_32_DAYS_IN_MS = 1000 * 60 * 60 * 24 * 32L; + static final long TIME_STEP_1_HR_IN_MS = 60 * 60 * 1000L; + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTracker.class); + + protected Properties props; + protected SOLRAPIClient client; + InformationServer infoSrv; + protected String coreName; + StoreRef storeRef; + long batchCount; + TrackerStats trackerStats; + boolean runPostModelLoadInit = true; + private int maxLiveSearchers; + private volatile boolean shutdown = false; + + protected volatile TrackerState state; + protected int shardCount; + protected int shardInstance; + ShardMethodEnum shardMethod; + protected boolean transformContent; + String shardTemplate; + protected volatile boolean rollback; + /** + * When rollback is set, original error is also gathered in order to provide detailed logging. + */ + protected Throwable rollbackCausedBy; + protected final Type type; + protected final String trackerId; + + DocRouter docRouter; + + /** + * The property to use for determining the shard. + * Note that this property is not used by all trackers, it is actually managed by the {@link ShardStatePublisher} and + * {@link MetadataTracker}. We put this property here because otherwise we should introduce another supertype layer + * for those two trackers. + */ + protected Optional shardProperty = Optional.empty(); + + /** + * The string representation of the shard key. + * Note that this property is not used by all trackers, it is actually managed by the {@link ShardStatePublisher} and + * {@link MetadataTracker}. We put this property here because otherwise we should introduce another supertype layer + * for those two trackers. + */ + protected Optional shardKey; + + /** + * Default constructor, strictly for testing. + */ + protected AbstractTracker(Type type) + { + this.type = type; + this.trackerId = type + "@" + hashCode(); + } + + protected AbstractTracker(Properties p, SOLRAPIClient client, String coreName, InformationServer informationServer,Type type) + { + this.props = p; + this.client = client; + this.coreName = coreName; + this.infoSrv = informationServer; + + storeRef = new StoreRef(p.getProperty("alfresco.stores", "workspace://SpacesStore")); + batchCount = Integer.parseInt(p.getProperty("alfresco.batch.count", "5000")); + maxLiveSearchers = Integer.parseInt(p.getProperty("alfresco.maxLiveSearchers", "2")); + + shardCount = Integer.parseInt(p.getProperty("shard.count", "1")); + shardInstance = Integer.parseInt(p.getProperty("shard.instance", "0")); + shardMethod = ShardMethodEnum.getShardMethod(p.getProperty("shard.method", DB_ID.name())); + + shardTemplate = p.getProperty("alfresco.template", ""); + + transformContent = Boolean.parseBoolean(p.getProperty("alfresco.index.transformContent", "true")); + + this.trackerStats = this.infoSrv.getTrackerStats(); + + this.type = type; + + this.trackerId = type + "@" + hashCode(); + + shardKey = ofNullable(p.getProperty(SHARD_KEY_KEY)); + + firstUpdateShardProperty(); + + docRouter = DocRouterFactory.getRouter(p, shardMethod); + } + + /** + * Subclasses must implement behaviour that completes the following steps, in order: + * + *
    + *
  1. Purge
  2. + *
  3. Reindex
  4. + *
  5. Index
  6. + *
  7. Track repository
  8. + *
+ * + * @param iterationId an identifier which is uniquely associated with a given iteration. + */ + protected abstract void doTrack(String iterationId) throws Throwable; + + + private boolean assertTrackerStateRemainsNull() { + + /* + * This assertion is added to accommodate DistributedAlfrescoSolrTrackerRaceTest. + * The sleep is needed to allow the test case to add a txn into the queue before + * the tracker makes its call to pull transactions from the test repo client. + */ + + try + { + Thread.sleep(5000); + } + catch(Exception e) + { + // Ignore + } + + + /* + * This ensures that getTrackerState does not have the side effect of setting the + * state instance variable. This allows classes outside of the tracker framework + * to safely call getTrackerState without interfering with the trackers design. + */ + + getTrackerState(); + + return state == null; + + } + /** + * Template method - subclasses must implement the {@link Tracker}-specific indexing + * by implementing the abstract method {@link #doTrack(String)}. + */ + @Override + public void track() + { + String iterationId = "IT #" + System.currentTimeMillis(); + + if(getRunLock().availablePermits() == 0) + { + LOGGER.info("[{} / {} / {}] Tracker already registered.", coreName, trackerId, iterationId); + return; + } + + try + { + /* + * The runLock ensures that for each tracker type (metadata, content, commit, cascade) only one tracker will + * be running at a time. + */ + getRunLock().acquire(); + + if (state==null && Boolean.parseBoolean(System.getProperty("alfresco.test", "false"))) + { + assert(assertTrackerStateRemainsNull()); + } + + if(this.state == null) + { + this.state = getTrackerState(); + + LOGGER.debug("[{} / {} / {}] Global Tracker State set to: {}", coreName, trackerId, iterationId, this.state.toString()); + this.state.setRunning(true); + } + else + { + continueState(); + this.state.setRunning(true); + } + + infoSrv.registerTrackerThread(); + + try + { + doTrack(iterationId); + } + catch(IndexTrackingShutdownException t) + { + setRollback(true, t); + LOGGER.info("[{} / {} / {}] Tracking cycle stopped. See the stacktrace below for further details.", coreName, trackerId, iterationId, t); + } + catch(Throwable t) + { + setRollback(true, t); + if (t instanceof SocketTimeoutException || t instanceof ConnectException) + { + LOGGER.warn("[{} / {} / {}] Tracking communication timed out. See the stacktrace below for further details.", coreName, trackerId, iterationId); + LOGGER.debug("[{} / {} / {}] Stack trace", coreName, trackerId, iterationId, t); + } + else + { + LOGGER.error("[{} / {} / {}] Tracking failure. See the stacktrace below for further details.", coreName, trackerId, iterationId, t); + } + } + } + catch (Exception exception) + { + LOGGER.error("[{} / {} / {}] Some problem was detected while resetting the Tracker State. See the stracktrace below for further details." + , coreName, trackerId, iterationId, exception); + } + finally + { + infoSrv.unregisterTrackerThread(); + + ofNullable(state).ifPresent(this::turnOff); + + getRunLock().release(); + } + } + + /** + * At the end of the tracking method, the {@link TrackerState} should be turned off. + * However, during a rollback (that could be started by another tracker) the {@link TrackerState} instance + * could be set to null, even after passing a null check we could get a NPE. + * For that reason, this method turns off the tracker state and ignore any {@link NullPointerException} (actually + * any exception) that could be thrown. + */ + private void turnOff(TrackerState state) { + try + { + state.setRunning(false); + state.setCheck(false); + } catch (Exception exception) + { + LOGGER.error("Unable to properly turn off the TrackerState instance. See the stacktrace below for further details.", exception); + } + } + + public boolean getRollback() + { + return this.rollback; + } + + public Throwable getRollbackCausedBy() + { + return this.rollbackCausedBy; + } + + public void setRollback(boolean rollback, Throwable rollbackCausedBy) + { + this.rollback = rollback; + this.rollbackCausedBy = rollbackCausedBy; + } + + private void continueState() + { + infoSrv.continueState(state); + state.incrementTrackerCycles(); + } + + public synchronized void invalidateState() + { + state = null; + } + + @Override + public synchronized TrackerState getTrackerState() + { + if(this.state != null) + { + return this.state; + } + else + { + return this.infoSrv.getTrackerInitialState(); + } + } + + int getMaxLiveSearchers() + { + return maxLiveSearchers; + } + + void checkShutdown() + { + if(shutdown) + { + throw new IndexTrackingShutdownException(); + } + } + + @Override + public boolean isAlreadyInShutDownMode() + { + return shutdown; + } + + @Override + public void setShutdown(boolean shutdown) + { + this.shutdown = shutdown; + } + + @Override + public void shutdown() + { + setShutdown(true); + } + + /** + * Trackers implementing this method should decide if the Write Lock is applied + * globally for every Tracker Thread (static) or locally for each running Thread + */ + public abstract Semaphore getWriteLock(); + + /** + * Trackers implementing this method should decide if the Run Lock is applied + * globally for every Tracker Thread (static) or locally for each running Thread + */ + public abstract Semaphore getRunLock(); + + public Properties getProps() + { + return props; + } + + public Type getType() + { + return type; + } + + /** + * Set the shard property using the shard key. + */ + void updateShardProperty() + { + shardKey.ifPresent(shardKeyName -> { + Optional updatedShardProperty = getShardProperty(shardKeyName); + if (!shardProperty.equals(updatedShardProperty)) + { + if (updatedShardProperty.isEmpty()) + { + LOGGER.warn("The model defining {} property has been disabled", shardKeyName); + } + else + { + LOGGER.info("New {} property found for {}", SHARD_KEY_KEY, shardKeyName); + } + } + shardProperty = updatedShardProperty; + }); + } + + /** + * Given the field name, returns the name of the property definition. + * If the property definition is not found, Empty optional is returned. + * + * @param field the field name. + * @return the name of the associated property definition if present, Optional.Empty() otherwise + */ + static Optional getShardProperty(String field) + { + if (StringUtils.isBlank(field)) + { + throw new IllegalArgumentException("Sharding property " + SHARD_KEY_KEY + " has not been set."); + } + + AlfrescoSolrDataModel dataModel = AlfrescoSolrDataModel.getInstance(); + NamespaceDAO namespaceDAO = dataModel.getNamespaceDAO(); + DictionaryService dictionaryService = dataModel.getDictionaryService(CMISStrictDictionaryService.DEFAULT); + PropertyDefinition propertyDef = QueryParserUtils.matchPropertyDefinition("http://www.alfresco.org/model/content/1.0", + namespaceDAO, + dictionaryService, + field); + + return ofNullable(propertyDef).map(PropertyDefinition::getName); + } + + /** + * Returns information about the {@link org.alfresco.solr.client.Node} associated with the given dbid. + * + * @param dbid the node identifier. + * @return the {@link org.alfresco.solr.client.Node} associated with the given dbid. + */ + public NodeReport checkNode(Long dbid) + { + NodeReport nodeReport = new NodeReport(); + nodeReport.setDbid(dbid); + + this.infoSrv.addCommonNodeReportInfo(nodeReport); + + return nodeReport; + } + + /** + * Returns the {@link DocRouter} instance in use on this node. + * + * @return the {@link DocRouter} instance in use on this node. + */ + public DocRouter getDocRouter() + { + return this.docRouter; + } + + private void firstUpdateShardProperty() + { + shardKey.ifPresent( shardKeyName -> { + updateShardProperty(); + if (shardProperty.isEmpty()) + { + LOGGER.warn("Sharding property {} was set to {}, but no such property was found.", SHARD_KEY_KEY, shardKeyName); + } + }); + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractWorker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractWorker.java index 0ed19ab02..1d013f18f 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractWorker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractWorker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java index 35ff9cc17..bc08e05b8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java @@ -1,939 +1,939 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import com.google.common.collect.Lists; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.repo.index.shard.ShardMethodEnum; -import org.alfresco.solr.AclReport; -import org.alfresco.solr.BoundedDeque; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.TrackerState; -import org.alfresco.solr.adapters.IOpenBitSet; -import org.alfresco.solr.client.Acl; -import org.alfresco.solr.client.AclChangeSet; -import org.alfresco.solr.client.AclChangeSets; -import org.alfresco.solr.client.AclReaders; -import org.alfresco.solr.client.SOLRAPIClient; -import org.json.JSONException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Multithreaded ACL {@link Tracker} implementation. - * - * @author Matt Ward - **/ - -public class AclTracker extends ActivatableTracker -{ - protected final static Logger LOGGER = LoggerFactory.getLogger(AclTracker.class); - - private static final int ACL_CHANGE_SETS_FOUND_QUEUE_SIZE = 100; - private static final int DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE = 2000; - private static final int DEFAULT_ACL_BATCH_SIZE = 100; - private static final int DEFAULT_ACL_TRACKER_MAX_PARALLELISM = 32; - private static final long DEFAULT_ACL_TRACKER_TIMESTEP = TIME_STEP_1_HR_IN_MS; - - private static final long INITIAL_MAX_ACL_CHANGE_SET_ID = 2000L; - private static final int MAX_NUMBER_OF_ACL_CHANGE_SETS = 2000; - - private static final long MAX_TIME_STEP = TIME_STEP_32_DAYS_IN_MS; - - // Repository Remote API doesn't accept more than 512 aclChangeSetIds by invocation - private static final int MAX_ACL_CHANGE_SET_BATCH_SIZE = 512; - - private int aclTrackerParallelism; - - private int changeSetAclsBatchSize; - private int aclBatchSize; - private long timeStep; - private int maxNumberOfAclChangeSets; - - private ConcurrentLinkedQueue aclChangeSetsToReindex = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclChangeSetsToIndex = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclChangeSetsToPurge = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclsToReindex = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclsToIndex = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue aclsToPurge = new ConcurrentLinkedQueue<>(); - private DocRouter docRouter; - - private ForkJoinPool forkJoinPool; - - // Share run and write locks across all AclTracker threads - private static Map RUN_LOCK_BY_CORE = new ConcurrentHashMap<>(); - private static Map WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>(); - @Override - public Semaphore getWriteLock() - { - return WRITE_LOCK_BY_CORE.get(coreName); - } - @Override - public Semaphore getRunLock() - { - return RUN_LOCK_BY_CORE.get(coreName); - } - - - /** - * Default constructor, for testing. - */ - AclTracker() - { - super(Tracker.Type.ACL); - } - - public AclTracker(Properties p, SOLRAPIClient client, - String coreName, InformationServer informationServer) - { - super(p, client, coreName, informationServer, Tracker.Type.ACL); - changeSetAclsBatchSize = Integer.parseInt(p.getProperty("alfresco.changeSetAclsBatchSize", - String.valueOf(DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE))); - if (changeSetAclsBatchSize > MAX_ACL_CHANGE_SET_BATCH_SIZE) - { - LOGGER.warn("Max value for 'alfresco.changeSetAclsBatchSize' is 512. " - + "This value is being taken instead of the one specified in 'solrcore.properties': " + changeSetAclsBatchSize); - changeSetAclsBatchSize = MAX_ACL_CHANGE_SET_BATCH_SIZE; - } - aclBatchSize = Integer.parseInt(p.getProperty("alfresco.aclBatchSize", - String.valueOf(DEFAULT_ACL_BATCH_SIZE))); - docRouter = DocRouterFactory.getRouter(p, shardMethod); - - aclTrackerParallelism = Integer.parseInt(p.getProperty("alfresco.acl.tracker.maxParallelism", - String.valueOf(DEFAULT_ACL_TRACKER_MAX_PARALLELISM))); - forkJoinPool = new ForkJoinPool(aclTrackerParallelism); - - timeStep = Long.parseLong(p.getProperty("alfresco.acl.tracker.timestep", - String.valueOf(DEFAULT_ACL_TRACKER_TIMESTEP))); - - maxNumberOfAclChangeSets = Integer.parseInt(p.getProperty("alfresco.acl.tracker.maxNumberOfAclChangeSets", - String.valueOf(MAX_NUMBER_OF_ACL_CHANGE_SETS))); - - RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); - WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); - } - - @Override - protected void doTrack(String iterationId) throws Throwable - { - trackRepository(); - } - - public void maintenance() throws Exception - { - purgeAclChangeSets(); - purgeAcls(); - reindexAclChangeSets(); - reindexAcls(); - indexAclChangeSets(); - indexAcls(); - } - - - public boolean hasMaintenance() - { - return aclChangeSetsToReindex.size() > 0 || - aclChangeSetsToIndex.size() > 0 || - aclChangeSetsToPurge.size() > 0 || - aclsToReindex.size() > 0 || - aclsToIndex.size() > 0 || - aclsToPurge.size() > 0; - } - - protected void indexAclChangeSets() throws AuthenticationException, IOException, JSONException - { - boolean requiresCommit = false; - while (aclChangeSetsToIndex.peek() != null) - { - Long aclChangeSetId = aclChangeSetsToIndex.poll(); - if (aclChangeSetId != null) - { - AclChangeSets aclChangeSets = client.getAclChangeSets(null, aclChangeSetId, - null, aclChangeSetId+1, 1); - if ((aclChangeSets.getAclChangeSets().size() > 0) && - aclChangeSetId.equals(aclChangeSets.getAclChangeSets().get(0).getId())) - { - AclChangeSet changeSet = aclChangeSets.getAclChangeSets().get(0); - List acls = client.getAcls(Collections.singletonList(changeSet), null, Integer.MAX_VALUE); - for (Acl acl : acls) - { - List readers = client.getAclReaders(Collections.singletonList(acl)); - indexAcl(readers, false); - } - this.infoSrv.indexAclTransaction(changeSet, false); - LOGGER.info("[CORE {}] - INDEX ACTION - AclChangeSetId {} has been indexed", coreName, aclChangeSetId); - requiresCommit = true; - } - else - { - LOGGER.info("[CORE {}] - INDEX ACTION - AclChangeSetId {} was not found in database, " + - "it has NOT been reindexed", coreName, aclChangeSetId); - } - } - checkShutdown(); - } - if(requiresCommit) - { - checkShutdown(); - //this.infoSrv.commit(); - } - } - - protected void indexAcls() throws AuthenticationException, IOException, JSONException - { - while (aclsToIndex.peek() != null) - { - Long aclId = aclsToIndex.poll(); - if (aclId != null) - { - //System.out.println("############## Indexing ACL ID:"+aclId); - Acl acl = new Acl(0, aclId); - List readers = client.getAclReaders(Collections.singletonList(acl)); - //AclReaders r = readers.get(0); - //System.out.println("############## READERS ID:"+r.getId()+":"+r.getReaders()); - indexAcl(readers, false); - LOGGER.info("[CORE {}] - INDEX ACTION - AclId {} has been indexed", coreName, aclId); - } - checkShutdown(); - } - } - - protected void reindexAclChangeSets() throws AuthenticationException, IOException, JSONException - { - boolean requiresCommit = false; - while (aclChangeSetsToReindex.peek() != null) - { - Long aclChangeSetId = aclChangeSetsToReindex.poll(); - if (aclChangeSetId != null) - { - this.infoSrv.deleteByAclChangeSetId(aclChangeSetId); - - AclChangeSets aclChangeSets = client.getAclChangeSets(null, aclChangeSetId, - null, aclChangeSetId+1, 1); - if ((aclChangeSets.getAclChangeSets().size() > 0) && - aclChangeSetId.equals(aclChangeSets.getAclChangeSets().get(0).getId())) - { - AclChangeSet changeSet = aclChangeSets.getAclChangeSets().get(0); - List acls = client.getAcls(Collections.singletonList(changeSet), - null, Integer.MAX_VALUE); - for (Acl acl : acls) - { - List readers = client.getAclReaders(Collections.singletonList(acl)); - indexAcl(readers, true); - } - - this.infoSrv.indexAclTransaction(changeSet, true); - LOGGER.info("[CORE {}] - REINDEX ACTION - AclChangeSetId {} has been reindexed", - coreName, aclChangeSetId); - requiresCommit = true; - } - else - { - LOGGER.info("[CORE {}] - REINDEX ACTION - AclChangeSetId {} was not found in database, " + - "it has NOT been reindexed", coreName, aclChangeSetId); - } - } - checkShutdown(); - } - if(requiresCommit) - { - checkShutdown(); - //this.infoSrv.commit(); - } - } - - protected void reindexAcls() throws AuthenticationException, IOException, JSONException - { - boolean requiresCommit = false; - while (aclsToReindex.peek() != null) - { - Long aclId = aclsToReindex.poll(); - if (aclId != null) - { - this.infoSrv.deleteByAclId(aclId); - - Acl acl = new Acl(0, aclId); - List readers = client.getAclReaders(Collections.singletonList(acl)); - indexAcl(readers, true); - LOGGER.info("[CORE {}] - REINDEX ACTION - aclId {} has been reindexed", coreName, aclId); - requiresCommit = true; - } - checkShutdown(); - } - if(requiresCommit) - { - checkShutdown(); - //this.infoSrv.commit(); - } - } - - protected void purgeAclChangeSets() throws IOException, JSONException - { - while (aclChangeSetsToPurge.peek() != null) - { - Long aclChangeSetId = aclChangeSetsToPurge.poll(); - if (aclChangeSetId != null) - { - this.infoSrv.deleteByAclChangeSetId(aclChangeSetId); - LOGGER.info("[CORE {}] - PURGE ACTION - Purged aclChangeSetId {}", coreName, aclChangeSetId); - } - checkShutdown(); - } - } - - protected void purgeAcls() throws IOException, JSONException - { - while (aclsToPurge.peek() != null) - { - Long aclId = aclsToPurge.poll(); - if (aclId != null) - { - this.infoSrv.deleteByAclId(aclId); - LOGGER.info("[CORE {}] - PURGE ACTION - Purged aclId {}", coreName, aclId); - } - checkShutdown(); - } - } - - - // ACL change sets - - public void addAclChangeSetToReindex(Long aclChangeSetToReindex) - { - aclChangeSetsToReindex.offer(aclChangeSetToReindex); - } - - public void addAclChangeSetToIndex(Long aclChangeSetToIndex) - { - aclChangeSetsToIndex.offer(aclChangeSetToIndex); - } - - public void addAclChangeSetToPurge(Long aclChangeSetToPurge) - { - aclChangeSetsToPurge.offer(aclChangeSetToPurge); - } - - // ACLs - - public void addAclToReindex(Long aclToReindex) - { - aclsToReindex.offer(aclToReindex); - } - - public void addAclToIndex(Long aclToIndex) - { - aclsToIndex.offer(aclToIndex); - } - - public void addAclToPurge(Long aclToPurge) - { - aclsToPurge.offer(aclToPurge); - } - - @Override - protected void clearScheduledMaintenanceWork() - { - logAndClear(aclChangeSetsToIndex, "ACL ChangeSets to be indexed"); - logAndClear(aclsToIndex, "ACLs to be indexed"); - - logAndClear(aclChangeSetsToReindex, "ACL ChangeSets to be re-indexed"); - logAndClear(aclsToReindex, "ACLs to be re-indexed"); - - logAndClear(aclChangeSetsToPurge, "ACL ChangeSets to be purged"); - logAndClear(aclsToPurge, "ACLs to be purged"); - } - - protected void trackRepository() throws IOException, AuthenticationException, JSONException - { - checkShutdown(); - - TrackerState state = super.getTrackerState(); - - // Check we are tracking the correct repository - if(state.getTrackerCycles() == 0) - { - //We have a new tracker state so do the checks. - checkRepoAndIndexConsistency(state); - } - - checkShutdown(); - trackAclChangeSets(); - } - - /** - * Checks the first and last TX time - */ - private void checkRepoAndIndexConsistency(TrackerState state) throws AuthenticationException, IOException, JSONException - { - AclChangeSets firstChangeSets = null; - if (state.getLastGoodChangeSetCommitTimeInIndex() == 0) - { - state.setCheckedLastAclTransactionTime(true); - state.setCheckedFirstAclTransactionTime(true); - LOGGER.info("[CORE {}] - No acl transactions found - no verification required", coreName); - - firstChangeSets = client.getAclChangeSets(null, 0L, - null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); - if (!firstChangeSets.getAclChangeSets().isEmpty()) - { - AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0); - long firstChangeSetCommitTime = firstChangeSet.getCommitTimeMs(); - state.setLastGoodChangeSetCommitTimeInIndex(firstChangeSetCommitTime); - setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state); - } - } - - if (!state.isCheckedFirstAclTransactionTime()) - { - firstChangeSets = client.getAclChangeSets(null, 0L, - null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); - if (!firstChangeSets.getAclChangeSets().isEmpty()) - { - AclChangeSet firstAclChangeSet= firstChangeSets.getAclChangeSets().get(0); - long firstAclTxId = firstAclChangeSet.getId(); - long firstAclTxCommitTime = firstAclChangeSet.getCommitTimeMs(); - int setSize = this.infoSrv.getAclTxDocsSize(Long.toString(firstAclTxId), - Long.toString(firstAclTxCommitTime)); - - if (setSize == 0) - { - LOGGER.error("[CORE {}] First acl transaction was not found with the correct timestamp.", coreName); - LOGGER.error("SOLR has successfully connected to your repository " + - "however the SOLR indexes and repository database do not match."); - LOGGER.error("If this is a new or rebuilt database your SOLR indexes " + - "also need to be re-built to match the database."); - LOGGER.error("You can also check your SOLR connection details in solrcore.properties."); - throw new AlfrescoRuntimeException("Initial acl transaction not found with correct timestamp"); - } - else if (setSize == 1) - { - state.setCheckedFirstTransactionTime(true); - LOGGER.info("[CORE {}] Verified first acl transaction and timestamp in index", coreName); - } - else - { - LOGGER.warn("[CORE {}] Duplicate initial acl transaction found with correct timestamp", coreName); - } - } - } - - // Checks that the last aclTxId in solr is <= last aclTxId in repo - if (!state.isCheckedLastAclTransactionTime()) - { - if (firstChangeSets == null) - { - firstChangeSets = client.getAclChangeSets(null, 0L, - null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); - } - - setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state); - Long maxChangeSetCommitTimeInRepo = firstChangeSets.getMaxChangeSetCommitTime(); - Long maxChangeSetIdInRepo = firstChangeSets.getMaxChangeSetId(); - if (maxChangeSetCommitTimeInRepo != null && maxChangeSetIdInRepo != null) - { - AclChangeSet maxAclTxInIndex = this.infoSrv.getMaxAclChangeSetIdAndCommitTimeInIndex(); - if (maxAclTxInIndex.getCommitTimeMs() > maxChangeSetCommitTimeInRepo) - { - LOGGER.error("[CORE {}] Last acl transaction was found in index with " + - "timestamp later than that of repository.", coreName); - LOGGER.error("Max Acl Tx In Index: " + maxAclTxInIndex.getId() + ", In Repo: " + maxChangeSetIdInRepo); - LOGGER.error("Max Acl Tx Commit Time In Index: " + maxAclTxInIndex.getCommitTimeMs() + ", In Repo: " - + maxChangeSetCommitTimeInRepo); - LOGGER.error("SOLR has successfully connected to your repository" + - " however the SOLR indexes and repository database do not match."); - LOGGER.error("If this is a new or rebuilt database your SOLR indexes " + - "also need to be re-built to match the database."); - LOGGER.error("You can also check your SOLR connection details in solrcore.properties."); - throw new AlfrescoRuntimeException("Last acl transaction found in index with incorrect timestamp"); - } - else - { - state.setCheckedLastAclTransactionTime(true); - LOGGER.info("[CORE {}] - Verified last acl transaction timestamp in index less" + - " than or equal to that of repository.", coreName); - } - } - } - } - - /** - * @param changeSetsFound BoundedDeque - * @param lastGoodChangeSetCommitTimeInIndex long - * @return Long - */ - protected Long getChangeSetFromCommitTime(BoundedDeque changeSetsFound, long lastGoodChangeSetCommitTimeInIndex) - { - if(changeSetsFound.size() > 0) - { - return changeSetsFound.getLast().getCommitTimeMs(); - } - else - { - return lastGoodChangeSetCommitTimeInIndex; - } - } - - protected AclChangeSets getSomeAclChangeSets(BoundedDeque changeSetsFound, Long fromCommitTime, - long timeStep, int maxResults, long endTime) - throws AuthenticationException, IOException, JSONException - { - long actualTimeStep = timeStep; - - AclChangeSets aclChangeSets; - // step forward in time until we find something or hit the time bound - // max id unbounded - Long startTime = fromCommitTime == null ? Long.valueOf(0L) : fromCommitTime; - do - { - aclChangeSets = client.getAclChangeSets(startTime, null, - startTime + actualTimeStep, null, maxResults); - startTime += actualTimeStep; - actualTimeStep *= 2; - if(actualTimeStep > MAX_TIME_STEP) - { - actualTimeStep = MAX_TIME_STEP; - } - } - while( ((aclChangeSets.getAclChangeSets().size() == 0) && (startTime < endTime)) || - ((aclChangeSets.getAclChangeSets().size() > 0) && alreadyFoundChangeSets(changeSetsFound, aclChangeSets))); - - return aclChangeSets; - - } - - private boolean alreadyFoundChangeSets(BoundedDeque changeSetsFound, AclChangeSets aclChangeSets) - { - if(changeSetsFound.size() == 0) - { - return false; - } - - if(aclChangeSets.getAclChangeSets().size() == 1) - { - return aclChangeSets.getAclChangeSets().get(0).getId() == changeSetsFound.getLast().getId(); - } - else - { - HashSet alreadyFound = new HashSet<>(changeSetsFound.getDeque()); - for(AclChangeSet aclChangeSet : aclChangeSets.getAclChangeSets()) - { - if(!alreadyFound.contains(aclChangeSet)) - { - return false; - } - } - return true; - } - } - - /** - * @param aclReaderList List - * @param overwrite boolean - */ - protected void indexAcl(List aclReaderList, boolean overwrite) throws IOException - { - long time = this.infoSrv.indexAcl(aclReaderList, overwrite); - trackerStats.addAclTime(time); - } - - public IndexHealthReport checkIndex(Long toAclTx, Long fromTime, Long toTime) - throws AuthenticationException, IOException, JSONException - { - // DB ACL TX Count - long firstChangeSetCommitTimex = 0; - AclChangeSets firstChangeSets = client.getAclChangeSets(null, 0L, - null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); - if(firstChangeSets.getAclChangeSets().size() > 0) - { - AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0); - firstChangeSetCommitTimex = firstChangeSet.getCommitTimeMs(); - } - - IOpenBitSet aclTxIdsInDb = infoSrv.getOpenBitSetInstance(); - long lastAclTxCommitTime = firstChangeSetCommitTimex; - if (fromTime != null) - { - lastAclTxCommitTime = fromTime; - } - - long maxAclTxId = 0; - Long minAclTxId = null; - long endTime = System.currentTimeMillis() + infoSrv.getHoleRetention(); - AclChangeSets aclTransactions; - BoundedDeque changeSetsFound = new BoundedDeque<>(ACL_CHANGE_SETS_FOUND_QUEUE_SIZE); - DO: do - { - aclTransactions = getSomeAclChangeSets(changeSetsFound, - lastAclTxCommitTime, timeStep, maxNumberOfAclChangeSets, endTime); - for (AclChangeSet set : aclTransactions.getAclChangeSets()) - { - // include - if (toTime != null) - { - if (set.getCommitTimeMs() > toTime) - { - break DO; - } - } - if (toAclTx != null) - { - if (set.getId() > toAclTx) - { - break DO; - } - } - - // bounds for later loops - if (minAclTxId == null) - { - minAclTxId = set.getId(); - } - if (maxAclTxId < set.getId()) - { - maxAclTxId = set.getId(); - } - - lastAclTxCommitTime = set.getCommitTimeMs(); - aclTxIdsInDb.set(set.getId()); - changeSetsFound.add(set); - } - } - while (aclTransactions.getAclChangeSets().size() > 0); - - return this.infoSrv.reportAclTransactionsInIndex(minAclTxId, aclTxIdsInDb, maxAclTxId); - } - - public List getAclsForDbAclTransaction(Long acltxid) - { - try - { - ArrayList answer = new ArrayList<>(); - AclChangeSets changeSet = client.getAclChangeSets(null, acltxid, null, - acltxid+1, 1); - List acls = client.getAcls(changeSet.getAclChangeSets(), null, Integer.MAX_VALUE); - for (Acl acl : acls) - { - answer.add(acl.getId()); - } - return answer; - } - catch (IOException | JSONException | AuthenticationException e) - { - throw new AlfrescoRuntimeException("Failed to get acls", e); - } - } - - public AclReport checkAcl(Long aclid) - { - AclReport aclReport = new AclReport(); - aclReport.setAclId(aclid); - - // In DB - - try - { - List readers = client.getAclReaders(Collections.singletonList(new Acl(0, aclid))); - aclReport.setExistsInDb(readers.size() == 1); - } - catch (IOException | JSONException | AuthenticationException e) - { - aclReport.setExistsInDb(false); - } - - // In Index - return this.infoSrv.checkAclInIndex(aclid, aclReport); - } - - /** - * Every ACL Change Set contains a list of ACLs to be indexed. - * This method gets ACL Change Sets from Alfresco Repository to be indexed. - * - * The indexing is performed in batches of ACL Change Sets and the Tracker Status - * is updated in batched of ACLs. - * - * Tracker Status contains the Commit Time from the latest ACL Change Set indexed, - * so new operations can be retrieved from Repository starting with that time. - */ - protected void trackAclChangeSets() throws AuthenticationException, IOException, JSONException - { - - long startElapsed = System.nanoTime(); - - AclChangeSets aclChangeSets; - BoundedDeque changeSetsFound = new BoundedDeque<>(ACL_CHANGE_SETS_FOUND_QUEUE_SIZE); - long totalAclCount = 0; - int aclCount; - - LOGGER.info("{}-[CORE {}] Tracking ACLs", Thread.currentThread().getId(), coreName); - - do - { - try - { - getWriteLock().acquire(); - - /* - * We acquire the tracker state again here and set it globally. This is because the - * tracker state could have been invalidated due to a rollback by the CommitTracker. - * In this case the state will revert to the last transaction state record in the index. - */ - - this.state = getTrackerState(); - - Long fromCommitTime = getChangeSetFromCommitTime(changeSetsFound, - state.getLastChangeSetCommitTimeOnServer() == 0 ? state.getLastGoodChangeSetCommitTimeInIndex() - : state.getLastChangeSetCommitTimeOnServer()); - aclChangeSets = getSomeAclChangeSets(changeSetsFound, fromCommitTime, timeStep, maxNumberOfAclChangeSets, - state.getTimeToStopIndexing()); - - if (aclChangeSets.getAclChangeSets().size() > 0) - { - LOGGER.info("{}-[CORE {}] Found {} ACL change sets after lastTxCommitTime {}, ACL Change Sets from {} to {}", - Thread.currentThread().getId(), - coreName, - aclChangeSets.getAclChangeSets().size(), - fromCommitTime, - aclChangeSets.getAclChangeSets().get(0), - aclChangeSets.getAclChangeSets().get(aclChangeSets.getAclChangeSets().size() - 1)); - } - else - { - LOGGER.info("{}-[CORE {}] No ACL change set found after lastTxCommitTime {}", - Thread.currentThread().getId(), coreName, fromCommitTime); - } - - - // Make sure we do not go ahead of where we started - we will check the holes here - // correctly next time - if (aclChangeSets.getAclChangeSets() - .stream() - .anyMatch(changeSet -> changeSet.getCommitTimeMs() > state.getTimeToStopIndexing())) - { - break; - } - - final AtomicInteger counter = new AtomicInteger(); - Collection> changeSetBatches = aclChangeSets.getAclChangeSets().stream() - .peek(changeSetsFound::add) - .filter(this::isAclChangeSetAlreadyIndexed) - .collect(Collectors.groupingBy(it -> counter.getAndAdd(1) / changeSetAclsBatchSize)) - .values(); - - - for (List changeSetBatch : changeSetBatches) - { - aclCount = indexBatchOfChangeSets(changeSetBatch); - // Update last committed transactions - setLastChangeSetIdAndCommitTimeInTrackerState(changeSetBatch, state); - indexAclChangeSetAfterWorker(changeSetBatch, state); - - long endElapsed = System.nanoTime(); - trackerStats.addElapsedAclTime(aclCount, endElapsed-startElapsed); - startElapsed = endElapsed; - totalAclCount += aclCount; - } - - } - catch(InterruptedException | ExecutionException e) - { - throw new IOException(e); - } - finally - { - getWriteLock().release(); - } - - } - while ((aclChangeSets.getAclChangeSets().size() > 0)); - - LOGGER.info("{}-[CORE {}] Tracked {} ACLs", Thread.currentThread().getId(), coreName, totalAclCount); - - } - - private boolean isAclChangeSetAlreadyIndexed(AclChangeSet changeSet) - { - - try - { - boolean isInIndex = (changeSet.getCommitTimeMs() <= state.getLastIndexedChangeSetCommitTime() && - infoSrv.aclChangeSetInIndex(changeSet.getId(), true)); - if (LOGGER.isTraceEnabled()) - { - LOGGER.trace("{}-[CORE {}] Skipping change Set Id {} as it was already indexed", - Thread.currentThread().getId(), coreName, changeSet.getId()); - } - return !isInIndex; - } - catch (IOException e) - { - LOGGER.warn( - "{}-[CORE {}] Error catched while checking if ACL Change Set {} was in index", - Thread.currentThread().getId(), coreName, changeSet.getId(), e); - return true; - } - - } - - private void setLastChangeSetIdAndCommitTimeInTrackerState(List aclChangeSets, TrackerState state) - { - - if (!aclChangeSets.isEmpty()) - { - long maxChangeSetCommitTime = - aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getCommitTimeMs)).orElseThrow().getCommitTimeMs(); - state.setLastChangeSetCommitTimeOnServer(maxChangeSetCommitTime); - - long maxChangeSetId = aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getId)).orElseThrow().getId(); - state.setLastChangeSetIdOnServer(maxChangeSetId); - } - - } - - /** - * Index ACL Change Set transaction after ACLs has been indexed by the worker - */ - private void indexAclChangeSetAfterWorker(Collection changeSetsIndexed, TrackerState state) - throws IOException - { - for (AclChangeSet set : changeSetsIndexed) - { - infoSrv.indexAclTransaction(set, true); - // Acl change sets are ordered by commit time and tie-broken by id - if (set.getCommitTimeMs() > state.getLastIndexedChangeSetCommitTime() - || set.getCommitTimeMs() == state.getLastIndexedChangeSetCommitTime() - && set.getId() > state.getLastIndexedChangeSetId()) - { - state.setLastIndexedChangeSetCommitTime(set.getCommitTimeMs()); - state.setLastIndexedChangeSetId(set.getId()); - } - trackerStats.addChangeSetAcls(set.getAclCount()); - } - } - - - /** - * Index ACLs from ACL Change Sets contained in changeSetBatch - * When total ACL indexed count is greater than the specified for a single execution - * (maxAclsPerExecution), no more ACL Change Sets are processed. - * - * @param changeSetBatch List of ACL Change Sets to be indexed - * @return List of ACL Change Set indexed and Count of ACL indexed - */ - private int indexBatchOfChangeSets(List changeSetBatch) - throws AuthenticationException, IOException, JSONException, ExecutionException, InterruptedException { - // Exclude ACL Change Set with no ACLs inside - List nonEmptyChangeSets = changeSetBatch.stream() - .filter(set -> set.getAclCount() > 0) - .collect(Collectors.toList()); - - List acls = client.getAcls(nonEmptyChangeSets, null, Integer.MAX_VALUE); - - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug("{}-[CORE {}] Found {} Acls from Acl Change Sets: {}", Thread.currentThread().getId(), - coreName, acls.size(), nonEmptyChangeSets); - } - - List> aclBatches = Lists.partition(acls, aclBatchSize); - - return forkJoinPool.submit(() -> - aclBatches.parallelStream().map(batch -> { - new AclIndexWorker(batch).run(); - return batch.size(); - }).reduce(0, Integer::sum) - ).get(); - } - - - /** - * ACL Indexer - */ - class AclIndexWorker extends AbstractWorker - { - List acls; - - AclIndexWorker(List acls) - { - this.acls = acls; - } - - @Override - protected void doWork() throws IOException, AuthenticationException, JSONException - { - List filteredAcls = filterAcls(acls); - if(filteredAcls.size() > 0) - { - List readers = client.getAclReaders(filteredAcls); - indexAcl(readers, true); - } - } - - @Override - protected void onFail(Throwable failCausedBy) - { - setRollback(true, failCausedBy); - } - - private List filterAcls(List acls) - { - ArrayList filteredList = new ArrayList<>(acls.size()); - for(Acl acl : acls) - { - if(docRouter.routeAcl(shardCount, shardInstance, acl)) - { - filteredList.add(acl); - } - } - return filteredList; - } - } - - - public void invalidateState() - { - super.invalidateState(); - infoSrv.clearProcessedAclChangeSets(); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import com.google.common.collect.Lists; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.repo.index.shard.ShardMethodEnum; +import org.alfresco.solr.AclReport; +import org.alfresco.solr.BoundedDeque; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.TrackerState; +import org.alfresco.solr.adapters.IOpenBitSet; +import org.alfresco.solr.client.Acl; +import org.alfresco.solr.client.AclChangeSet; +import org.alfresco.solr.client.AclChangeSets; +import org.alfresco.solr.client.AclReaders; +import org.alfresco.solr.client.SOLRAPIClient; +import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Multithreaded ACL {@link Tracker} implementation. + * + * @author Matt Ward + **/ + +public class AclTracker extends ActivatableTracker +{ + protected final static Logger LOGGER = LoggerFactory.getLogger(AclTracker.class); + + private static final int ACL_CHANGE_SETS_FOUND_QUEUE_SIZE = 100; + private static final int DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE = 2000; + private static final int DEFAULT_ACL_BATCH_SIZE = 100; + private static final int DEFAULT_ACL_TRACKER_MAX_PARALLELISM = 32; + private static final long DEFAULT_ACL_TRACKER_TIMESTEP = TIME_STEP_1_HR_IN_MS; + + private static final long INITIAL_MAX_ACL_CHANGE_SET_ID = 2000L; + private static final int MAX_NUMBER_OF_ACL_CHANGE_SETS = 2000; + + private static final long MAX_TIME_STEP = TIME_STEP_32_DAYS_IN_MS; + + // Repository Remote API doesn't accept more than 512 aclChangeSetIds by invocation + private static final int MAX_ACL_CHANGE_SET_BATCH_SIZE = 512; + + private int aclTrackerParallelism; + + private int changeSetAclsBatchSize; + private int aclBatchSize; + private long timeStep; + private int maxNumberOfAclChangeSets; + + private ConcurrentLinkedQueue aclChangeSetsToReindex = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclChangeSetsToIndex = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclChangeSetsToPurge = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclsToReindex = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclsToIndex = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue aclsToPurge = new ConcurrentLinkedQueue<>(); + private DocRouter docRouter; + + private ForkJoinPool forkJoinPool; + + // Share run and write locks across all AclTracker threads + private static Map RUN_LOCK_BY_CORE = new ConcurrentHashMap<>(); + private static Map WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>(); + @Override + public Semaphore getWriteLock() + { + return WRITE_LOCK_BY_CORE.get(coreName); + } + @Override + public Semaphore getRunLock() + { + return RUN_LOCK_BY_CORE.get(coreName); + } + + + /** + * Default constructor, for testing. + */ + AclTracker() + { + super(Tracker.Type.ACL); + } + + public AclTracker(Properties p, SOLRAPIClient client, + String coreName, InformationServer informationServer) + { + super(p, client, coreName, informationServer, Tracker.Type.ACL); + changeSetAclsBatchSize = Integer.parseInt(p.getProperty("alfresco.changeSetAclsBatchSize", + String.valueOf(DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE))); + if (changeSetAclsBatchSize > MAX_ACL_CHANGE_SET_BATCH_SIZE) + { + LOGGER.warn("Max value for 'alfresco.changeSetAclsBatchSize' is 512. " + + "This value is being taken instead of the one specified in 'solrcore.properties': " + changeSetAclsBatchSize); + changeSetAclsBatchSize = MAX_ACL_CHANGE_SET_BATCH_SIZE; + } + aclBatchSize = Integer.parseInt(p.getProperty("alfresco.aclBatchSize", + String.valueOf(DEFAULT_ACL_BATCH_SIZE))); + docRouter = DocRouterFactory.getRouter(p, shardMethod); + + aclTrackerParallelism = Integer.parseInt(p.getProperty("alfresco.acl.tracker.maxParallelism", + String.valueOf(DEFAULT_ACL_TRACKER_MAX_PARALLELISM))); + forkJoinPool = new ForkJoinPool(aclTrackerParallelism); + + timeStep = Long.parseLong(p.getProperty("alfresco.acl.tracker.timestep", + String.valueOf(DEFAULT_ACL_TRACKER_TIMESTEP))); + + maxNumberOfAclChangeSets = Integer.parseInt(p.getProperty("alfresco.acl.tracker.maxNumberOfAclChangeSets", + String.valueOf(MAX_NUMBER_OF_ACL_CHANGE_SETS))); + + RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); + WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); + } + + @Override + protected void doTrack(String iterationId) throws Throwable + { + trackRepository(); + } + + public void maintenance() throws Exception + { + purgeAclChangeSets(); + purgeAcls(); + reindexAclChangeSets(); + reindexAcls(); + indexAclChangeSets(); + indexAcls(); + } + + + public boolean hasMaintenance() + { + return aclChangeSetsToReindex.size() > 0 || + aclChangeSetsToIndex.size() > 0 || + aclChangeSetsToPurge.size() > 0 || + aclsToReindex.size() > 0 || + aclsToIndex.size() > 0 || + aclsToPurge.size() > 0; + } + + protected void indexAclChangeSets() throws AuthenticationException, IOException, JSONException + { + boolean requiresCommit = false; + while (aclChangeSetsToIndex.peek() != null) + { + Long aclChangeSetId = aclChangeSetsToIndex.poll(); + if (aclChangeSetId != null) + { + AclChangeSets aclChangeSets = client.getAclChangeSets(null, aclChangeSetId, + null, aclChangeSetId+1, 1); + if ((aclChangeSets.getAclChangeSets().size() > 0) && + aclChangeSetId.equals(aclChangeSets.getAclChangeSets().get(0).getId())) + { + AclChangeSet changeSet = aclChangeSets.getAclChangeSets().get(0); + List acls = client.getAcls(Collections.singletonList(changeSet), null, Integer.MAX_VALUE); + for (Acl acl : acls) + { + List readers = client.getAclReaders(Collections.singletonList(acl)); + indexAcl(readers, false); + } + this.infoSrv.indexAclTransaction(changeSet, false); + LOGGER.info("[CORE {}] - INDEX ACTION - AclChangeSetId {} has been indexed", coreName, aclChangeSetId); + requiresCommit = true; + } + else + { + LOGGER.info("[CORE {}] - INDEX ACTION - AclChangeSetId {} was not found in database, " + + "it has NOT been reindexed", coreName, aclChangeSetId); + } + } + checkShutdown(); + } + if(requiresCommit) + { + checkShutdown(); + //this.infoSrv.commit(); + } + } + + protected void indexAcls() throws AuthenticationException, IOException, JSONException + { + while (aclsToIndex.peek() != null) + { + Long aclId = aclsToIndex.poll(); + if (aclId != null) + { + //System.out.println("############## Indexing ACL ID:"+aclId); + Acl acl = new Acl(0, aclId); + List readers = client.getAclReaders(Collections.singletonList(acl)); + //AclReaders r = readers.get(0); + //System.out.println("############## READERS ID:"+r.getId()+":"+r.getReaders()); + indexAcl(readers, false); + LOGGER.info("[CORE {}] - INDEX ACTION - AclId {} has been indexed", coreName, aclId); + } + checkShutdown(); + } + } + + protected void reindexAclChangeSets() throws AuthenticationException, IOException, JSONException + { + boolean requiresCommit = false; + while (aclChangeSetsToReindex.peek() != null) + { + Long aclChangeSetId = aclChangeSetsToReindex.poll(); + if (aclChangeSetId != null) + { + this.infoSrv.deleteByAclChangeSetId(aclChangeSetId); + + AclChangeSets aclChangeSets = client.getAclChangeSets(null, aclChangeSetId, + null, aclChangeSetId+1, 1); + if ((aclChangeSets.getAclChangeSets().size() > 0) && + aclChangeSetId.equals(aclChangeSets.getAclChangeSets().get(0).getId())) + { + AclChangeSet changeSet = aclChangeSets.getAclChangeSets().get(0); + List acls = client.getAcls(Collections.singletonList(changeSet), + null, Integer.MAX_VALUE); + for (Acl acl : acls) + { + List readers = client.getAclReaders(Collections.singletonList(acl)); + indexAcl(readers, true); + } + + this.infoSrv.indexAclTransaction(changeSet, true); + LOGGER.info("[CORE {}] - REINDEX ACTION - AclChangeSetId {} has been reindexed", + coreName, aclChangeSetId); + requiresCommit = true; + } + else + { + LOGGER.info("[CORE {}] - REINDEX ACTION - AclChangeSetId {} was not found in database, " + + "it has NOT been reindexed", coreName, aclChangeSetId); + } + } + checkShutdown(); + } + if(requiresCommit) + { + checkShutdown(); + //this.infoSrv.commit(); + } + } + + protected void reindexAcls() throws AuthenticationException, IOException, JSONException + { + boolean requiresCommit = false; + while (aclsToReindex.peek() != null) + { + Long aclId = aclsToReindex.poll(); + if (aclId != null) + { + this.infoSrv.deleteByAclId(aclId); + + Acl acl = new Acl(0, aclId); + List readers = client.getAclReaders(Collections.singletonList(acl)); + indexAcl(readers, true); + LOGGER.info("[CORE {}] - REINDEX ACTION - aclId {} has been reindexed", coreName, aclId); + requiresCommit = true; + } + checkShutdown(); + } + if(requiresCommit) + { + checkShutdown(); + //this.infoSrv.commit(); + } + } + + protected void purgeAclChangeSets() throws IOException, JSONException + { + while (aclChangeSetsToPurge.peek() != null) + { + Long aclChangeSetId = aclChangeSetsToPurge.poll(); + if (aclChangeSetId != null) + { + this.infoSrv.deleteByAclChangeSetId(aclChangeSetId); + LOGGER.info("[CORE {}] - PURGE ACTION - Purged aclChangeSetId {}", coreName, aclChangeSetId); + } + checkShutdown(); + } + } + + protected void purgeAcls() throws IOException, JSONException + { + while (aclsToPurge.peek() != null) + { + Long aclId = aclsToPurge.poll(); + if (aclId != null) + { + this.infoSrv.deleteByAclId(aclId); + LOGGER.info("[CORE {}] - PURGE ACTION - Purged aclId {}", coreName, aclId); + } + checkShutdown(); + } + } + + + // ACL change sets + + public void addAclChangeSetToReindex(Long aclChangeSetToReindex) + { + aclChangeSetsToReindex.offer(aclChangeSetToReindex); + } + + public void addAclChangeSetToIndex(Long aclChangeSetToIndex) + { + aclChangeSetsToIndex.offer(aclChangeSetToIndex); + } + + public void addAclChangeSetToPurge(Long aclChangeSetToPurge) + { + aclChangeSetsToPurge.offer(aclChangeSetToPurge); + } + + // ACLs + + public void addAclToReindex(Long aclToReindex) + { + aclsToReindex.offer(aclToReindex); + } + + public void addAclToIndex(Long aclToIndex) + { + aclsToIndex.offer(aclToIndex); + } + + public void addAclToPurge(Long aclToPurge) + { + aclsToPurge.offer(aclToPurge); + } + + @Override + protected void clearScheduledMaintenanceWork() + { + logAndClear(aclChangeSetsToIndex, "ACL ChangeSets to be indexed"); + logAndClear(aclsToIndex, "ACLs to be indexed"); + + logAndClear(aclChangeSetsToReindex, "ACL ChangeSets to be re-indexed"); + logAndClear(aclsToReindex, "ACLs to be re-indexed"); + + logAndClear(aclChangeSetsToPurge, "ACL ChangeSets to be purged"); + logAndClear(aclsToPurge, "ACLs to be purged"); + } + + protected void trackRepository() throws IOException, AuthenticationException, JSONException + { + checkShutdown(); + + TrackerState state = super.getTrackerState(); + + // Check we are tracking the correct repository + if(state.getTrackerCycles() == 0) + { + //We have a new tracker state so do the checks. + checkRepoAndIndexConsistency(state); + } + + checkShutdown(); + trackAclChangeSets(); + } + + /** + * Checks the first and last TX time + */ + private void checkRepoAndIndexConsistency(TrackerState state) throws AuthenticationException, IOException, JSONException + { + AclChangeSets firstChangeSets = null; + if (state.getLastGoodChangeSetCommitTimeInIndex() == 0) + { + state.setCheckedLastAclTransactionTime(true); + state.setCheckedFirstAclTransactionTime(true); + LOGGER.info("[CORE {}] - No acl transactions found - no verification required", coreName); + + firstChangeSets = client.getAclChangeSets(null, 0L, + null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); + if (!firstChangeSets.getAclChangeSets().isEmpty()) + { + AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0); + long firstChangeSetCommitTime = firstChangeSet.getCommitTimeMs(); + state.setLastGoodChangeSetCommitTimeInIndex(firstChangeSetCommitTime); + setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state); + } + } + + if (!state.isCheckedFirstAclTransactionTime()) + { + firstChangeSets = client.getAclChangeSets(null, 0L, + null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); + if (!firstChangeSets.getAclChangeSets().isEmpty()) + { + AclChangeSet firstAclChangeSet= firstChangeSets.getAclChangeSets().get(0); + long firstAclTxId = firstAclChangeSet.getId(); + long firstAclTxCommitTime = firstAclChangeSet.getCommitTimeMs(); + int setSize = this.infoSrv.getAclTxDocsSize(Long.toString(firstAclTxId), + Long.toString(firstAclTxCommitTime)); + + if (setSize == 0) + { + LOGGER.error("[CORE {}] First acl transaction was not found with the correct timestamp.", coreName); + LOGGER.error("SOLR has successfully connected to your repository " + + "however the SOLR indexes and repository database do not match."); + LOGGER.error("If this is a new or rebuilt database your SOLR indexes " + + "also need to be re-built to match the database."); + LOGGER.error("You can also check your SOLR connection details in solrcore.properties."); + throw new AlfrescoRuntimeException("Initial acl transaction not found with correct timestamp"); + } + else if (setSize == 1) + { + state.setCheckedFirstTransactionTime(true); + LOGGER.info("[CORE {}] Verified first acl transaction and timestamp in index", coreName); + } + else + { + LOGGER.warn("[CORE {}] Duplicate initial acl transaction found with correct timestamp", coreName); + } + } + } + + // Checks that the last aclTxId in solr is <= last aclTxId in repo + if (!state.isCheckedLastAclTransactionTime()) + { + if (firstChangeSets == null) + { + firstChangeSets = client.getAclChangeSets(null, 0L, + null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); + } + + setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state); + Long maxChangeSetCommitTimeInRepo = firstChangeSets.getMaxChangeSetCommitTime(); + Long maxChangeSetIdInRepo = firstChangeSets.getMaxChangeSetId(); + if (maxChangeSetCommitTimeInRepo != null && maxChangeSetIdInRepo != null) + { + AclChangeSet maxAclTxInIndex = this.infoSrv.getMaxAclChangeSetIdAndCommitTimeInIndex(); + if (maxAclTxInIndex.getCommitTimeMs() > maxChangeSetCommitTimeInRepo) + { + LOGGER.error("[CORE {}] Last acl transaction was found in index with " + + "timestamp later than that of repository.", coreName); + LOGGER.error("Max Acl Tx In Index: " + maxAclTxInIndex.getId() + ", In Repo: " + maxChangeSetIdInRepo); + LOGGER.error("Max Acl Tx Commit Time In Index: " + maxAclTxInIndex.getCommitTimeMs() + ", In Repo: " + + maxChangeSetCommitTimeInRepo); + LOGGER.error("SOLR has successfully connected to your repository" + + " however the SOLR indexes and repository database do not match."); + LOGGER.error("If this is a new or rebuilt database your SOLR indexes " + + "also need to be re-built to match the database."); + LOGGER.error("You can also check your SOLR connection details in solrcore.properties."); + throw new AlfrescoRuntimeException("Last acl transaction found in index with incorrect timestamp"); + } + else + { + state.setCheckedLastAclTransactionTime(true); + LOGGER.info("[CORE {}] - Verified last acl transaction timestamp in index less" + + " than or equal to that of repository.", coreName); + } + } + } + } + + /** + * @param changeSetsFound BoundedDeque + * @param lastGoodChangeSetCommitTimeInIndex long + * @return Long + */ + protected Long getChangeSetFromCommitTime(BoundedDeque changeSetsFound, long lastGoodChangeSetCommitTimeInIndex) + { + if(changeSetsFound.size() > 0) + { + return changeSetsFound.getLast().getCommitTimeMs(); + } + else + { + return lastGoodChangeSetCommitTimeInIndex; + } + } + + protected AclChangeSets getSomeAclChangeSets(BoundedDeque changeSetsFound, Long fromCommitTime, + long timeStep, int maxResults, long endTime) + throws AuthenticationException, IOException, JSONException + { + long actualTimeStep = timeStep; + + AclChangeSets aclChangeSets; + // step forward in time until we find something or hit the time bound + // max id unbounded + Long startTime = fromCommitTime == null ? Long.valueOf(0L) : fromCommitTime; + do + { + aclChangeSets = client.getAclChangeSets(startTime, null, + startTime + actualTimeStep, null, maxResults); + startTime += actualTimeStep; + actualTimeStep *= 2; + if(actualTimeStep > MAX_TIME_STEP) + { + actualTimeStep = MAX_TIME_STEP; + } + } + while( ((aclChangeSets.getAclChangeSets().size() == 0) && (startTime < endTime)) || + ((aclChangeSets.getAclChangeSets().size() > 0) && alreadyFoundChangeSets(changeSetsFound, aclChangeSets))); + + return aclChangeSets; + + } + + private boolean alreadyFoundChangeSets(BoundedDeque changeSetsFound, AclChangeSets aclChangeSets) + { + if(changeSetsFound.size() == 0) + { + return false; + } + + if(aclChangeSets.getAclChangeSets().size() == 1) + { + return aclChangeSets.getAclChangeSets().get(0).getId() == changeSetsFound.getLast().getId(); + } + else + { + HashSet alreadyFound = new HashSet<>(changeSetsFound.getDeque()); + for(AclChangeSet aclChangeSet : aclChangeSets.getAclChangeSets()) + { + if(!alreadyFound.contains(aclChangeSet)) + { + return false; + } + } + return true; + } + } + + /** + * @param aclReaderList List + * @param overwrite boolean + */ + protected void indexAcl(List aclReaderList, boolean overwrite) throws IOException + { + long time = this.infoSrv.indexAcl(aclReaderList, overwrite); + trackerStats.addAclTime(time); + } + + public IndexHealthReport checkIndex(Long toAclTx, Long fromTime, Long toTime) + throws AuthenticationException, IOException, JSONException + { + // DB ACL TX Count + long firstChangeSetCommitTimex = 0; + AclChangeSets firstChangeSets = client.getAclChangeSets(null, 0L, + null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1); + if(firstChangeSets.getAclChangeSets().size() > 0) + { + AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0); + firstChangeSetCommitTimex = firstChangeSet.getCommitTimeMs(); + } + + IOpenBitSet aclTxIdsInDb = infoSrv.getOpenBitSetInstance(); + long lastAclTxCommitTime = firstChangeSetCommitTimex; + if (fromTime != null) + { + lastAclTxCommitTime = fromTime; + } + + long maxAclTxId = 0; + Long minAclTxId = null; + long endTime = System.currentTimeMillis() + infoSrv.getHoleRetention(); + AclChangeSets aclTransactions; + BoundedDeque changeSetsFound = new BoundedDeque<>(ACL_CHANGE_SETS_FOUND_QUEUE_SIZE); + DO: do + { + aclTransactions = getSomeAclChangeSets(changeSetsFound, + lastAclTxCommitTime, timeStep, maxNumberOfAclChangeSets, endTime); + for (AclChangeSet set : aclTransactions.getAclChangeSets()) + { + // include + if (toTime != null) + { + if (set.getCommitTimeMs() > toTime) + { + break DO; + } + } + if (toAclTx != null) + { + if (set.getId() > toAclTx) + { + break DO; + } + } + + // bounds for later loops + if (minAclTxId == null) + { + minAclTxId = set.getId(); + } + if (maxAclTxId < set.getId()) + { + maxAclTxId = set.getId(); + } + + lastAclTxCommitTime = set.getCommitTimeMs(); + aclTxIdsInDb.set(set.getId()); + changeSetsFound.add(set); + } + } + while (aclTransactions.getAclChangeSets().size() > 0); + + return this.infoSrv.reportAclTransactionsInIndex(minAclTxId, aclTxIdsInDb, maxAclTxId); + } + + public List getAclsForDbAclTransaction(Long acltxid) + { + try + { + ArrayList answer = new ArrayList<>(); + AclChangeSets changeSet = client.getAclChangeSets(null, acltxid, null, + acltxid+1, 1); + List acls = client.getAcls(changeSet.getAclChangeSets(), null, Integer.MAX_VALUE); + for (Acl acl : acls) + { + answer.add(acl.getId()); + } + return answer; + } + catch (IOException | JSONException | AuthenticationException e) + { + throw new AlfrescoRuntimeException("Failed to get acls", e); + } + } + + public AclReport checkAcl(Long aclid) + { + AclReport aclReport = new AclReport(); + aclReport.setAclId(aclid); + + // In DB + + try + { + List readers = client.getAclReaders(Collections.singletonList(new Acl(0, aclid))); + aclReport.setExistsInDb(readers.size() == 1); + } + catch (IOException | JSONException | AuthenticationException e) + { + aclReport.setExistsInDb(false); + } + + // In Index + return this.infoSrv.checkAclInIndex(aclid, aclReport); + } + + /** + * Every ACL Change Set contains a list of ACLs to be indexed. + * This method gets ACL Change Sets from Alfresco Repository to be indexed. + * + * The indexing is performed in batches of ACL Change Sets and the Tracker Status + * is updated in batched of ACLs. + * + * Tracker Status contains the Commit Time from the latest ACL Change Set indexed, + * so new operations can be retrieved from Repository starting with that time. + */ + protected void trackAclChangeSets() throws AuthenticationException, IOException, JSONException + { + + long startElapsed = System.nanoTime(); + + AclChangeSets aclChangeSets; + BoundedDeque changeSetsFound = new BoundedDeque<>(ACL_CHANGE_SETS_FOUND_QUEUE_SIZE); + long totalAclCount = 0; + int aclCount; + + LOGGER.info("{}-[CORE {}] Tracking ACLs", Thread.currentThread().getId(), coreName); + + do + { + try + { + getWriteLock().acquire(); + + /* + * We acquire the tracker state again here and set it globally. This is because the + * tracker state could have been invalidated due to a rollback by the CommitTracker. + * In this case the state will revert to the last transaction state record in the index. + */ + + this.state = getTrackerState(); + + Long fromCommitTime = getChangeSetFromCommitTime(changeSetsFound, + state.getLastChangeSetCommitTimeOnServer() == 0 ? state.getLastGoodChangeSetCommitTimeInIndex() + : state.getLastChangeSetCommitTimeOnServer()); + aclChangeSets = getSomeAclChangeSets(changeSetsFound, fromCommitTime, timeStep, maxNumberOfAclChangeSets, + state.getTimeToStopIndexing()); + + if (aclChangeSets.getAclChangeSets().size() > 0) + { + LOGGER.info("{}-[CORE {}] Found {} ACL change sets after lastTxCommitTime {}, ACL Change Sets from {} to {}", + Thread.currentThread().getId(), + coreName, + aclChangeSets.getAclChangeSets().size(), + fromCommitTime, + aclChangeSets.getAclChangeSets().get(0), + aclChangeSets.getAclChangeSets().get(aclChangeSets.getAclChangeSets().size() - 1)); + } + else + { + LOGGER.info("{}-[CORE {}] No ACL change set found after lastTxCommitTime {}", + Thread.currentThread().getId(), coreName, fromCommitTime); + } + + + // Make sure we do not go ahead of where we started - we will check the holes here + // correctly next time + if (aclChangeSets.getAclChangeSets() + .stream() + .anyMatch(changeSet -> changeSet.getCommitTimeMs() > state.getTimeToStopIndexing())) + { + break; + } + + final AtomicInteger counter = new AtomicInteger(); + Collection> changeSetBatches = aclChangeSets.getAclChangeSets().stream() + .peek(changeSetsFound::add) + .filter(this::isAclChangeSetAlreadyIndexed) + .collect(Collectors.groupingBy(it -> counter.getAndAdd(1) / changeSetAclsBatchSize)) + .values(); + + + for (List changeSetBatch : changeSetBatches) + { + aclCount = indexBatchOfChangeSets(changeSetBatch); + // Update last committed transactions + setLastChangeSetIdAndCommitTimeInTrackerState(changeSetBatch, state); + indexAclChangeSetAfterWorker(changeSetBatch, state); + + long endElapsed = System.nanoTime(); + trackerStats.addElapsedAclTime(aclCount, endElapsed-startElapsed); + startElapsed = endElapsed; + totalAclCount += aclCount; + } + + } + catch(InterruptedException | ExecutionException e) + { + throw new IOException(e); + } + finally + { + getWriteLock().release(); + } + + } + while ((aclChangeSets.getAclChangeSets().size() > 0)); + + LOGGER.info("{}-[CORE {}] Tracked {} ACLs", Thread.currentThread().getId(), coreName, totalAclCount); + + } + + private boolean isAclChangeSetAlreadyIndexed(AclChangeSet changeSet) + { + + try + { + boolean isInIndex = (changeSet.getCommitTimeMs() <= state.getLastIndexedChangeSetCommitTime() && + infoSrv.aclChangeSetInIndex(changeSet.getId(), true)); + if (LOGGER.isTraceEnabled()) + { + LOGGER.trace("{}-[CORE {}] Skipping change Set Id {} as it was already indexed", + Thread.currentThread().getId(), coreName, changeSet.getId()); + } + return !isInIndex; + } + catch (IOException e) + { + LOGGER.warn( + "{}-[CORE {}] Error catched while checking if ACL Change Set {} was in index", + Thread.currentThread().getId(), coreName, changeSet.getId(), e); + return true; + } + + } + + private void setLastChangeSetIdAndCommitTimeInTrackerState(List aclChangeSets, TrackerState state) + { + + if (!aclChangeSets.isEmpty()) + { + long maxChangeSetCommitTime = + aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getCommitTimeMs)).orElseThrow().getCommitTimeMs(); + state.setLastChangeSetCommitTimeOnServer(maxChangeSetCommitTime); + + long maxChangeSetId = aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getId)).orElseThrow().getId(); + state.setLastChangeSetIdOnServer(maxChangeSetId); + } + + } + + /** + * Index ACL Change Set transaction after ACLs has been indexed by the worker + */ + private void indexAclChangeSetAfterWorker(Collection changeSetsIndexed, TrackerState state) + throws IOException + { + for (AclChangeSet set : changeSetsIndexed) + { + infoSrv.indexAclTransaction(set, true); + // Acl change sets are ordered by commit time and tie-broken by id + if (set.getCommitTimeMs() > state.getLastIndexedChangeSetCommitTime() + || set.getCommitTimeMs() == state.getLastIndexedChangeSetCommitTime() + && set.getId() > state.getLastIndexedChangeSetId()) + { + state.setLastIndexedChangeSetCommitTime(set.getCommitTimeMs()); + state.setLastIndexedChangeSetId(set.getId()); + } + trackerStats.addChangeSetAcls(set.getAclCount()); + } + } + + + /** + * Index ACLs from ACL Change Sets contained in changeSetBatch + * When total ACL indexed count is greater than the specified for a single execution + * (maxAclsPerExecution), no more ACL Change Sets are processed. + * + * @param changeSetBatch List of ACL Change Sets to be indexed + * @return List of ACL Change Set indexed and Count of ACL indexed + */ + private int indexBatchOfChangeSets(List changeSetBatch) + throws AuthenticationException, IOException, JSONException, ExecutionException, InterruptedException { + // Exclude ACL Change Set with no ACLs inside + List nonEmptyChangeSets = changeSetBatch.stream() + .filter(set -> set.getAclCount() > 0) + .collect(Collectors.toList()); + + List acls = client.getAcls(nonEmptyChangeSets, null, Integer.MAX_VALUE); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("{}-[CORE {}] Found {} Acls from Acl Change Sets: {}", Thread.currentThread().getId(), + coreName, acls.size(), nonEmptyChangeSets); + } + + List> aclBatches = Lists.partition(acls, aclBatchSize); + + return forkJoinPool.submit(() -> + aclBatches.parallelStream().map(batch -> { + new AclIndexWorker(batch).run(); + return batch.size(); + }).reduce(0, Integer::sum) + ).get(); + } + + + /** + * ACL Indexer + */ + class AclIndexWorker extends AbstractWorker + { + List acls; + + AclIndexWorker(List acls) + { + this.acls = acls; + } + + @Override + protected void doWork() throws IOException, AuthenticationException, JSONException + { + List filteredAcls = filterAcls(acls); + if(filteredAcls.size() > 0) + { + List readers = client.getAclReaders(filteredAcls); + indexAcl(readers, true); + } + } + + @Override + protected void onFail(Throwable failCausedBy) + { + setRollback(true, failCausedBy); + } + + private List filterAcls(List acls) + { + ArrayList filteredList = new ArrayList<>(acls.size()); + for(Acl acl : acls) + { + if(docRouter.routeAcl(shardCount, shardInstance, acl)) + { + filteredList.add(acl); + } + } + return filteredList; + } + } + + + public void invalidateState() + { + super.invalidateState(); + infoSrv.clearProcessedAclChangeSets(); + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ActivatableTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ActivatableTracker.java index 63e4b8e90..dad53b3fc 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ActivatableTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ActivatableTracker.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 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% diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CascadeTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CascadeTracker.java index e8d78bee2..df0eda7af 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CascadeTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CascadeTracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CommitTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CommitTracker.java index 57b9530e7..b98995ddb 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CommitTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/CommitTracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ComposableDocRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ComposableDocRouter.java index ca7e7bff1..ee57db5a3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ComposableDocRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ComposableDocRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ContentTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ContentTracker.java index a5c870072..d6f2de2df 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ContentTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ContentTracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRangeRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRangeRouter.java index 90f6aa7ed..ecb22d115 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRangeRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRangeRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRouter.java index a1197d41b..120b440bf 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DBIDRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -68,4 +68,4 @@ public class DBIDRouter implements DocRouter String dbid = Long.toString(node.getId()); return (Math.abs(Hash.murmurhash3_x86_32(dbid, 0, dbid.length(), 77)) % shardCount) == shardInstance; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateMonthRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateMonthRouter.java index f88d7b6c7..2219c83d8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateMonthRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateMonthRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateQuarterRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateQuarterRouter.java index a1f9feb0b..6b488d180 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateQuarterRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DateQuarterRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -72,4 +72,4 @@ public class DateQuarterRouter implements DocRouter return ceilGroupInstance % numShards == shardInstance; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouter.java index 074431bd4..257b99da0 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterFactory.java index fef90dff1..f99990b3b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterWithFallback.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterWithFallback.java index a05131a20..691294979 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterWithFallback.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/DocRouterWithFallback.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouter.java index c6c4d192e..4d46d00d1 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouter.java index 8d5f85088..55e95d13d 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -87,4 +87,4 @@ public class ExplicitShardIdWithStaticPropertyRouter extends ComposableDocRouter return explicitShardId.equals(shardInstance); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java index 547667d2f..e65eb7f41 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java index b0c19b9c4..4495305a0 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java @@ -1,580 +1,580 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker; - -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Semaphore; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.M2Namespace; -import org.alfresco.service.cmr.dictionary.ModelDefinition.XMLBindingType; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.client.AlfrescoModel; -import org.alfresco.solr.client.AlfrescoModelDiff; -import org.alfresco.solr.client.SOLRAPIClient; -import org.alfresco.solr.config.ConfigUtil; -import org.apache.solr.core.SolrResourceLoader; -import org.json.JSONException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @startuml - * title ModelTracker - * participant Scheduler - * participant ModelTracker - * participant SOLRAPIClient - * == Initialisation == - * [-> ModelTracker: <> - * activate ModelTracker - * ModelTracker -> ModelTracker : locate create persisted models - * ModelTracker -> ModelTracker : load persisted models - * deactivate ModelTracker - * == Tracking == - * Scheduler -> ModelTracker: Run - * activate ModelTracker - * ModelTracker -> SOLRAPIClient: getModelChanges(Models + CRCs) - * activate SOLRAPIClient - * SOLRAPIClient -> ModelTracker: changedModels - * deactivate SOLRAPIClient - * loop changed models - * ModelTracker -> SOLRAPIClient: getModel - * activate SOLRAPIClient - * SOLRAPIClient -> ModelTracker: Model XML - * deactivate SOLRAPIClient - * end - * ModelTracker -> ModelTracker: update local dictionary - * ModelTracker -> ModelTracker: update cached models - * ModelTracker -> ModelTracker: intialise model dependancies - * ModelTracker -> Scheduler - * deactivate ModelTracker - * @enduml - */ -public class ModelTracker extends AbstractTracker -{ - private static final Logger LOGGER = LoggerFactory.getLogger(ModelTracker.class); - - private final Set indexedStores = new HashSet<>(); - private final Set ignoredStores = new HashSet<>(); - private final Set indexedTenants = new HashSet<>(); - private final Set ignoredTenants = new HashSet<>(); - private final Set indexedDataTypes = new HashSet<>(); - private final Set ignoredDataTypes = new HashSet<>(); - private final Set indexedTypes = new HashSet<>(); - private final Set ignoredTypes = new HashSet<>(); - private final Set indexedAspects = new HashSet<>(); - private final Set ignoredAspects = new HashSet<>(); - private final Set indexedFields = new HashSet<>(); - private final Set ignoredFields = new HashSet<>(); - - private ReentrantReadWriteLock modelLock = new ReentrantReadWriteLock(); - private volatile boolean hasModels = false; - private File alfrescoModelDir; - - // Share run and write locks across all ModelTracker threads - private static Map RUN_LOCK_BY_CORE = new ConcurrentHashMap<>(); - private static Map WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>(); - @Override - public Semaphore getWriteLock() - { - return WRITE_LOCK_BY_CORE.get(coreName); - } - @Override - public Semaphore getRunLock() - { - return RUN_LOCK_BY_CORE.get(coreName); - } - - public ModelTracker(String solrHome, Properties p, SOLRAPIClient client, String coreName, - InformationServer informationServer) - { - super(p, client, coreName, informationServer, Tracker.Type.MODEL); - String normalSolrHome = SolrResourceLoader.normalizeDir(solrHome); - alfrescoModelDir = new File(ConfigUtil.locateProperty("solr.model.dir", normalSolrHome+"alfrescoModels")); - LOGGER.info("Alfresco Model dir {}", alfrescoModelDir); - if (!alfrescoModelDir.exists()) - { - alfrescoModelDir.mkdir(); - } - - loadPersistedModels(); - - RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); - WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); - } - - @Override - public boolean hasMaintenance() - { - return false; - } - - @Override - public void maintenance() - { - // Nothing to be done here - } - - /** - * - */ - private void loadPersistedModels() - { - HashMap modelMap = new HashMap<>(); - if (alfrescoModelDir.exists() && alfrescoModelDir.isDirectory()) - { - // A filter for XML files - FileFilter filter = pathname -> pathname.isFile() && pathname.getName().endsWith(".xml"); - // List XML files - File[] files = alfrescoModelDir.listFiles(filter); - if (files != null) - { - for (File file : files) - { - InputStream modelStream = null; - M2Model model; - try - { - modelStream = new FileInputStream(file); - model = M2Model.createModel(modelStream); - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("File not found: " + file, e); - } - finally - { - if (modelStream != null) - { - try { modelStream.close(); } catch (Exception e) {} - } - } - // Model successfully loaded - for (M2Namespace namespace : model.getNamespaces()) - { - modelMap.put(namespace.getUri(), model); - } - } - } - } - // Load the models ensuring that they are loaded in the correct order - HashSet loadedModels = new HashSet(); - for (M2Model model : modelMap.values()) - { - loadModel(modelMap, loadedModels, model); - } - - if(modelMap.size() > 0) - { - AlfrescoSolrDataModel.getInstance().afterInitModels(); - } - } - - /** - * Default constructor, for testing. - */ - ModelTracker() - { - super(Tracker.Type.MODEL); - } - - @Override - protected void doTrack(String iterationId) throws AuthenticationException, IOException, JSONException - { - // Is the InformationServer ready to update - int registeredSearcherCount = this.infoSrv.getRegisteredSearcherCount(); - if (registeredSearcherCount >= getMaxLiveSearchers()) - { - LOGGER.info(".... skipping tracking registered searcher count = {}", registeredSearcherCount); - return; - } - - checkShutdown(); - trackModels(false); - } - - public void trackModels(boolean onlyFirstTime) throws AuthenticationException, IOException, JSONException - { - boolean requiresWriteLock; - modelLock.readLock().lock(); - try - { - if (hasModels) - { - if (onlyFirstTime) - { - return; - } - else - { - requiresWriteLock = false; - } - } - else - { - requiresWriteLock = true; - } - } - finally - { - modelLock.readLock().unlock(); - } - - if (requiresWriteLock) - { - modelLock.writeLock().lock(); - try - { - if (hasModels) - { - if (onlyFirstTime) { return; } - } - - trackModelsImpl(); - if (onlyFirstTime) - { - infoSrv.initSkippingDescendantDocs(); - } - hasModels = true; - } - finally - { - modelLock.writeLock().unlock(); - } - } - else - { - trackModelsImpl(); - } - } - - public void ensureFirstModelSync() - { - try - { - trackModels(true); - } - catch (Throwable t) - { - LOGGER.error("Model tracking failed for core: {}", coreName, t); - } - - } - - /** - * Tracks models. Reflects changes and updates on disk copy - */ - private void trackModelsImpl() throws AuthenticationException, IOException, JSONException - { - long start = System.nanoTime(); - - List modelDiffs = client.getModelsDiff(coreName, this.infoSrv.getAlfrescoModels()); - Map modelMap = new HashMap<>(); - - for (AlfrescoModelDiff modelDiff : modelDiffs) - { - switch (modelDiff.getType()) - { - case CHANGED: - case NEW: - AlfrescoModel model = client.getModel(coreName, modelDiff.getModelName()); - for (M2Namespace namespace : model.getModel().getNamespaces()) - { - modelMap.put(namespace.getUri(), model.getModel()); - } - break; - case REMOVED: - // We now remove models as we see them - MNT-17627 - // Models have to be deleted from disk before we remove them from memory - // We need to know the prefix for the uri to delete them - break; - } - } - - HashSet loadedModels = new HashSet<>(); - for (M2Model model : modelMap.values()) - { - loadModel(modelMap, loadedModels, model); - } - if (loadedModels.size() > 0) - { - this.infoSrv.afterInitModels(); - } - - for (AlfrescoModelDiff modelDiff : modelDiffs) - { - switch (modelDiff.getType()) - { - case CHANGED: - removeMatchingModels(alfrescoModelDir, modelDiff.getModelName()); - M2Model changedModel = this.infoSrv.getM2Model(modelDiff.getModelName()); - File changedFile = new File(alfrescoModelDir, getModelFileName(changedModel)); - FileOutputStream cos = new FileOutputStream(changedFile); - changedModel.toXML(cos); - cos.flush(); - cos.close(); - break; - case NEW: - M2Model newModel = this.infoSrv.getM2Model(modelDiff.getModelName()); - // add on file - File newFile = new File(alfrescoModelDir, getModelFileName(newModel)); - FileOutputStream nos = new FileOutputStream(newFile); - newModel.toXML(nos); - nos.flush(); - nos.close(); - break; - case REMOVED: - // This will remove the model from the dictionary on completion - try - { - removeMatchingModels(alfrescoModelDir, modelDiff.getModelName()); - } - finally - { - AlfrescoSolrDataModel.getInstance().removeModel(modelDiff.getModelName()); - } - break; - } - } - - long end = System.nanoTime(); - - trackerStats.addModelTime(end - start); - - if (runPostModelLoadInit) - { - for (Object key : props.keySet()) - { - String stringKey = (String) key; - if (stringKey.startsWith("alfresco.index.store")) - { - StoreRef store = new StoreRef(props.getProperty(stringKey)); - indexedStores.add(store); - } - if (stringKey.startsWith("alfresco.ignore.store")) - { - StoreRef store = new StoreRef(props.getProperty(stringKey)); - ignoredStores.add(store); - } - if (stringKey.startsWith("alfresco.index.tenant")) - { - indexedTenants.add(props.getProperty(stringKey)); - } - if (stringKey.startsWith("alfresco.ignore.tenant")) - { - ignoredTenants.add(props.getProperty(stringKey)); - } - if (stringKey.startsWith("alfresco.index.datatype")) - { - QName qname = expandQName(props.getProperty(stringKey)); - indexedDataTypes.add(qname); - } - if (stringKey.startsWith("alfresco.ignore.datatype")) - { - QName qname = expandQName(props.getProperty(stringKey)); - ignoredDataTypes.add(qname); - } - if (stringKey.startsWith("alfresco.index.type")) - { - QName qname = expandQName(props.getProperty(stringKey)); - indexedTypes.add(qname); - } - if (stringKey.startsWith("alfresco.ignore.type")) - { - QName qname = expandQName(props.getProperty(stringKey)); - ignoredTypes.add(qname); - } - if (stringKey.startsWith("alfresco.index.aspect")) - { - QName qname = expandQName(props.getProperty(stringKey)); - indexedAspects.add(qname); - } - if (stringKey.startsWith("alfresco.ignore.aspect")) - { - QName qname = expandQName(props.getProperty(stringKey)); - ignoredAspects.add(qname); - } - if (stringKey.startsWith("alfresco.index.field")) - { - String name = expandName(props.getProperty(stringKey)); - indexedFields.add(name); - } - if (stringKey.startsWith("alfresco.ignore.field")) - { - String name = expandName(props.getProperty(stringKey)); - ignoredFields.add(name); - } - } - runPostModelLoadInit = false; - } - } - - private QName expandQName(String qName) - { - String expandedQName = qName; - if (qName.startsWith("@")) - { - return expandQName(qName.substring(1)); - } - else if (qName.startsWith("{")) - { - expandedQName = expandQNameImpl(qName); - } - else if (qName.contains(":")) - { - expandedQName = expandQNameImpl(qName); - } - - return QName.createQName(expandedQName); - - } - - private String expandQNameImpl(String q) - { - String eq = q; - // Check for any prefixes and expand to the full uri - if (q.charAt(0) != '{') - { - int colonPosition = q.indexOf(':'); - if (colonPosition == -1) - { - // use the default namespace - eq = "{" + this.infoSrv.getNamespaceDAO().getNamespaceURI("") + "}" + q; - } - else - { - // find the prefix - eq = "{" + this.infoSrv.getNamespaceDAO().getNamespaceURI(q.substring(0, colonPosition)) + "}" - + q.substring(colonPosition + 1); - } - } - return eq; - } - - private String expandName(String qName) - { - String expandedQName = qName; - if (qName.startsWith("@")) - { - return expandName(qName.substring(1)); - } - else if (qName.startsWith("{")) - { - expandedQName = expandQNameImpl(qName); - } - else if (qName.contains(":")) - { - expandedQName = expandQNameImpl(qName); - } - - return expandedQName; - } - - private void removeMatchingModels(File alfrescoModelDir, QName modelName) - { - final String prefix = modelName.toPrefixString(this.infoSrv.getNamespaceDAO()).replace(":", ".") + "."; - final String postFix = ".xml"; - - File[] toDelete = alfrescoModelDir.listFiles(pathname -> { - if (pathname.isDirectory()) { return false; } - String name = pathname.getName(); - if (!name.endsWith(postFix)) { return false; } - if (!name.startsWith(prefix)) { return false; } - // check is number between - String checksum = name.substring(prefix.length(), name.length() - postFix.length()); - try - { - Long.parseLong(checksum); - return true; - } - catch (NumberFormatException nfe) - { - return false; - } - }); - - if (toDelete != null) - { - for (File file : toDelete) - { - file.delete(); - } - } - } - - private void loadModel(Map modelMap, HashSet loadedModels, M2Model model) - { - String modelName = model.getName(); - if (!loadedModels.contains(modelName)) - { - for (M2Namespace importNamespace : model.getImports()) - { - M2Model importedModel = modelMap.get(importNamespace.getUri()); - if (importedModel != null) - { - - // Ensure that the imported model is loaded first - loadModel(modelMap, loadedModels, importedModel); - } - } - - if (this.infoSrv.putModel(model)) - { - loadedModels.add(modelName); - } - LOGGER.info("Loading model {}", model.getName()); - } - } - - private String getModelFileName(M2Model model) - { - return model.getName().replace(":", ".") + "." + model.getChecksum(XMLBindingType.DEFAULT) + ".xml"; - } - - public boolean hasModels() - { - return this.hasModels; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.M2Namespace; +import org.alfresco.service.cmr.dictionary.ModelDefinition.XMLBindingType; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.client.AlfrescoModel; +import org.alfresco.solr.client.AlfrescoModelDiff; +import org.alfresco.solr.client.SOLRAPIClient; +import org.alfresco.solr.config.ConfigUtil; +import org.apache.solr.core.SolrResourceLoader; +import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @startuml + * title ModelTracker + * participant Scheduler + * participant ModelTracker + * participant SOLRAPIClient + * == Initialisation == + * [-> ModelTracker: <> + * activate ModelTracker + * ModelTracker -> ModelTracker : locate create persisted models + * ModelTracker -> ModelTracker : load persisted models + * deactivate ModelTracker + * == Tracking == + * Scheduler -> ModelTracker: Run + * activate ModelTracker + * ModelTracker -> SOLRAPIClient: getModelChanges(Models + CRCs) + * activate SOLRAPIClient + * SOLRAPIClient -> ModelTracker: changedModels + * deactivate SOLRAPIClient + * loop changed models + * ModelTracker -> SOLRAPIClient: getModel + * activate SOLRAPIClient + * SOLRAPIClient -> ModelTracker: Model XML + * deactivate SOLRAPIClient + * end + * ModelTracker -> ModelTracker: update local dictionary + * ModelTracker -> ModelTracker: update cached models + * ModelTracker -> ModelTracker: intialise model dependancies + * ModelTracker -> Scheduler + * deactivate ModelTracker + * @enduml + */ +public class ModelTracker extends AbstractTracker +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ModelTracker.class); + + private final Set indexedStores = new HashSet<>(); + private final Set ignoredStores = new HashSet<>(); + private final Set indexedTenants = new HashSet<>(); + private final Set ignoredTenants = new HashSet<>(); + private final Set indexedDataTypes = new HashSet<>(); + private final Set ignoredDataTypes = new HashSet<>(); + private final Set indexedTypes = new HashSet<>(); + private final Set ignoredTypes = new HashSet<>(); + private final Set indexedAspects = new HashSet<>(); + private final Set ignoredAspects = new HashSet<>(); + private final Set indexedFields = new HashSet<>(); + private final Set ignoredFields = new HashSet<>(); + + private ReentrantReadWriteLock modelLock = new ReentrantReadWriteLock(); + private volatile boolean hasModels = false; + private File alfrescoModelDir; + + // Share run and write locks across all ModelTracker threads + private static Map RUN_LOCK_BY_CORE = new ConcurrentHashMap<>(); + private static Map WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>(); + @Override + public Semaphore getWriteLock() + { + return WRITE_LOCK_BY_CORE.get(coreName); + } + @Override + public Semaphore getRunLock() + { + return RUN_LOCK_BY_CORE.get(coreName); + } + + public ModelTracker(String solrHome, Properties p, SOLRAPIClient client, String coreName, + InformationServer informationServer) + { + super(p, client, coreName, informationServer, Tracker.Type.MODEL); + String normalSolrHome = SolrResourceLoader.normalizeDir(solrHome); + alfrescoModelDir = new File(ConfigUtil.locateProperty("solr.model.dir", normalSolrHome+"alfrescoModels")); + LOGGER.info("Alfresco Model dir {}", alfrescoModelDir); + if (!alfrescoModelDir.exists()) + { + alfrescoModelDir.mkdir(); + } + + loadPersistedModels(); + + RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); + WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true)); + } + + @Override + public boolean hasMaintenance() + { + return false; + } + + @Override + public void maintenance() + { + // Nothing to be done here + } + + /** + * + */ + private void loadPersistedModels() + { + HashMap modelMap = new HashMap<>(); + if (alfrescoModelDir.exists() && alfrescoModelDir.isDirectory()) + { + // A filter for XML files + FileFilter filter = pathname -> pathname.isFile() && pathname.getName().endsWith(".xml"); + // List XML files + File[] files = alfrescoModelDir.listFiles(filter); + if (files != null) + { + for (File file : files) + { + InputStream modelStream = null; + M2Model model; + try + { + modelStream = new FileInputStream(file); + model = M2Model.createModel(modelStream); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("File not found: " + file, e); + } + finally + { + if (modelStream != null) + { + try { modelStream.close(); } catch (Exception e) {} + } + } + // Model successfully loaded + for (M2Namespace namespace : model.getNamespaces()) + { + modelMap.put(namespace.getUri(), model); + } + } + } + } + // Load the models ensuring that they are loaded in the correct order + HashSet loadedModels = new HashSet(); + for (M2Model model : modelMap.values()) + { + loadModel(modelMap, loadedModels, model); + } + + if(modelMap.size() > 0) + { + AlfrescoSolrDataModel.getInstance().afterInitModels(); + } + } + + /** + * Default constructor, for testing. + */ + ModelTracker() + { + super(Tracker.Type.MODEL); + } + + @Override + protected void doTrack(String iterationId) throws AuthenticationException, IOException, JSONException + { + // Is the InformationServer ready to update + int registeredSearcherCount = this.infoSrv.getRegisteredSearcherCount(); + if (registeredSearcherCount >= getMaxLiveSearchers()) + { + LOGGER.info(".... skipping tracking registered searcher count = {}", registeredSearcherCount); + return; + } + + checkShutdown(); + trackModels(false); + } + + public void trackModels(boolean onlyFirstTime) throws AuthenticationException, IOException, JSONException + { + boolean requiresWriteLock; + modelLock.readLock().lock(); + try + { + if (hasModels) + { + if (onlyFirstTime) + { + return; + } + else + { + requiresWriteLock = false; + } + } + else + { + requiresWriteLock = true; + } + } + finally + { + modelLock.readLock().unlock(); + } + + if (requiresWriteLock) + { + modelLock.writeLock().lock(); + try + { + if (hasModels) + { + if (onlyFirstTime) { return; } + } + + trackModelsImpl(); + if (onlyFirstTime) + { + infoSrv.initSkippingDescendantDocs(); + } + hasModels = true; + } + finally + { + modelLock.writeLock().unlock(); + } + } + else + { + trackModelsImpl(); + } + } + + public void ensureFirstModelSync() + { + try + { + trackModels(true); + } + catch (Throwable t) + { + LOGGER.error("Model tracking failed for core: {}", coreName, t); + } + + } + + /** + * Tracks models. Reflects changes and updates on disk copy + */ + private void trackModelsImpl() throws AuthenticationException, IOException, JSONException + { + long start = System.nanoTime(); + + List modelDiffs = client.getModelsDiff(coreName, this.infoSrv.getAlfrescoModels()); + Map modelMap = new HashMap<>(); + + for (AlfrescoModelDiff modelDiff : modelDiffs) + { + switch (modelDiff.getType()) + { + case CHANGED: + case NEW: + AlfrescoModel model = client.getModel(coreName, modelDiff.getModelName()); + for (M2Namespace namespace : model.getModel().getNamespaces()) + { + modelMap.put(namespace.getUri(), model.getModel()); + } + break; + case REMOVED: + // We now remove models as we see them - MNT-17627 + // Models have to be deleted from disk before we remove them from memory + // We need to know the prefix for the uri to delete them + break; + } + } + + HashSet loadedModels = new HashSet<>(); + for (M2Model model : modelMap.values()) + { + loadModel(modelMap, loadedModels, model); + } + if (loadedModels.size() > 0) + { + this.infoSrv.afterInitModels(); + } + + for (AlfrescoModelDiff modelDiff : modelDiffs) + { + switch (modelDiff.getType()) + { + case CHANGED: + removeMatchingModels(alfrescoModelDir, modelDiff.getModelName()); + M2Model changedModel = this.infoSrv.getM2Model(modelDiff.getModelName()); + File changedFile = new File(alfrescoModelDir, getModelFileName(changedModel)); + FileOutputStream cos = new FileOutputStream(changedFile); + changedModel.toXML(cos); + cos.flush(); + cos.close(); + break; + case NEW: + M2Model newModel = this.infoSrv.getM2Model(modelDiff.getModelName()); + // add on file + File newFile = new File(alfrescoModelDir, getModelFileName(newModel)); + FileOutputStream nos = new FileOutputStream(newFile); + newModel.toXML(nos); + nos.flush(); + nos.close(); + break; + case REMOVED: + // This will remove the model from the dictionary on completion + try + { + removeMatchingModels(alfrescoModelDir, modelDiff.getModelName()); + } + finally + { + AlfrescoSolrDataModel.getInstance().removeModel(modelDiff.getModelName()); + } + break; + } + } + + long end = System.nanoTime(); + + trackerStats.addModelTime(end - start); + + if (runPostModelLoadInit) + { + for (Object key : props.keySet()) + { + String stringKey = (String) key; + if (stringKey.startsWith("alfresco.index.store")) + { + StoreRef store = new StoreRef(props.getProperty(stringKey)); + indexedStores.add(store); + } + if (stringKey.startsWith("alfresco.ignore.store")) + { + StoreRef store = new StoreRef(props.getProperty(stringKey)); + ignoredStores.add(store); + } + if (stringKey.startsWith("alfresco.index.tenant")) + { + indexedTenants.add(props.getProperty(stringKey)); + } + if (stringKey.startsWith("alfresco.ignore.tenant")) + { + ignoredTenants.add(props.getProperty(stringKey)); + } + if (stringKey.startsWith("alfresco.index.datatype")) + { + QName qname = expandQName(props.getProperty(stringKey)); + indexedDataTypes.add(qname); + } + if (stringKey.startsWith("alfresco.ignore.datatype")) + { + QName qname = expandQName(props.getProperty(stringKey)); + ignoredDataTypes.add(qname); + } + if (stringKey.startsWith("alfresco.index.type")) + { + QName qname = expandQName(props.getProperty(stringKey)); + indexedTypes.add(qname); + } + if (stringKey.startsWith("alfresco.ignore.type")) + { + QName qname = expandQName(props.getProperty(stringKey)); + ignoredTypes.add(qname); + } + if (stringKey.startsWith("alfresco.index.aspect")) + { + QName qname = expandQName(props.getProperty(stringKey)); + indexedAspects.add(qname); + } + if (stringKey.startsWith("alfresco.ignore.aspect")) + { + QName qname = expandQName(props.getProperty(stringKey)); + ignoredAspects.add(qname); + } + if (stringKey.startsWith("alfresco.index.field")) + { + String name = expandName(props.getProperty(stringKey)); + indexedFields.add(name); + } + if (stringKey.startsWith("alfresco.ignore.field")) + { + String name = expandName(props.getProperty(stringKey)); + ignoredFields.add(name); + } + } + runPostModelLoadInit = false; + } + } + + private QName expandQName(String qName) + { + String expandedQName = qName; + if (qName.startsWith("@")) + { + return expandQName(qName.substring(1)); + } + else if (qName.startsWith("{")) + { + expandedQName = expandQNameImpl(qName); + } + else if (qName.contains(":")) + { + expandedQName = expandQNameImpl(qName); + } + + return QName.createQName(expandedQName); + + } + + private String expandQNameImpl(String q) + { + String eq = q; + // Check for any prefixes and expand to the full uri + if (q.charAt(0) != '{') + { + int colonPosition = q.indexOf(':'); + if (colonPosition == -1) + { + // use the default namespace + eq = "{" + this.infoSrv.getNamespaceDAO().getNamespaceURI("") + "}" + q; + } + else + { + // find the prefix + eq = "{" + this.infoSrv.getNamespaceDAO().getNamespaceURI(q.substring(0, colonPosition)) + "}" + + q.substring(colonPosition + 1); + } + } + return eq; + } + + private String expandName(String qName) + { + String expandedQName = qName; + if (qName.startsWith("@")) + { + return expandName(qName.substring(1)); + } + else if (qName.startsWith("{")) + { + expandedQName = expandQNameImpl(qName); + } + else if (qName.contains(":")) + { + expandedQName = expandQNameImpl(qName); + } + + return expandedQName; + } + + private void removeMatchingModels(File alfrescoModelDir, QName modelName) + { + final String prefix = modelName.toPrefixString(this.infoSrv.getNamespaceDAO()).replace(":", ".") + "."; + final String postFix = ".xml"; + + File[] toDelete = alfrescoModelDir.listFiles(pathname -> { + if (pathname.isDirectory()) { return false; } + String name = pathname.getName(); + if (!name.endsWith(postFix)) { return false; } + if (!name.startsWith(prefix)) { return false; } + // check is number between + String checksum = name.substring(prefix.length(), name.length() - postFix.length()); + try + { + Long.parseLong(checksum); + return true; + } + catch (NumberFormatException nfe) + { + return false; + } + }); + + if (toDelete != null) + { + for (File file : toDelete) + { + file.delete(); + } + } + } + + private void loadModel(Map modelMap, HashSet loadedModels, M2Model model) + { + String modelName = model.getName(); + if (!loadedModels.contains(modelName)) + { + for (M2Namespace importNamespace : model.getImports()) + { + M2Model importedModel = modelMap.get(importNamespace.getUri()); + if (importedModel != null) + { + + // Ensure that the imported model is loaded first + loadModel(modelMap, loadedModels, importedModel); + } + } + + if (this.infoSrv.putModel(model)) + { + loadedModels.add(modelName); + } + LOGGER.info("Loading model {}", model.getName()); + } + } + + private String getModelFileName(M2Model model) + { + return model.getName().replace(":", ".") + "." + model.getChecksum(XMLBindingType.DEFAULT) + ".xml"; + } + + public boolean hasModels() + { + return this.hasModels; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/PropertyRouter.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/PropertyRouter.java index 2d453b4a3..412c1c823 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/PropertyRouter.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/PropertyRouter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ShardStatePublisher.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ShardStatePublisher.java index 4196f48ae..8f2a81739 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ShardStatePublisher.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ShardStatePublisher.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/SolrTrackerScheduler.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/SolrTrackerScheduler.java index de75808ad..900519be4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/SolrTrackerScheduler.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/SolrTrackerScheduler.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/Tracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/Tracker.java index b49f04fda..2497a2e33 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/Tracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/Tracker.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerJob.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerJob.java index d323fb6e6..5d822767c 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerJob.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerJob.java @@ -1,74 +1,74 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker; - -import org.quartz.DisallowConcurrentExecution; -import org.quartz.Job; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Generic Solr tracker job, allowing Quartz to initiate an index update from - * a {@link Tracker} regardless of specific implementation. - * - * Concurrent execution is disallowed, as no parallel work can be done when - * indexing contents from the repository. - * - * @author Matt Ward - */ -@DisallowConcurrentExecution -public class TrackerJob implements Job -{ - public static final String JOBDATA_TRACKER_KEY = "TRACKER"; - protected final static Logger log = LoggerFactory.getLogger(TrackerJob.class); - - - /* - * (non-Javadoc) - * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) - */ - @Override - public void execute(JobExecutionContext jec) throws JobExecutionException - { - Tracker tracker = getTracker(jec); - tracker.track(); - } - - /** - * Retrieve the {@link Tracker} from the {@link JobExecutionContext}. - * - * @param jec JobExecutionContext - * @return The tracker - */ - private Tracker getTracker(JobExecutionContext jec) - { - Tracker tracker = (Tracker) jec.getJobDetail().getJobDataMap().get(JOBDATA_TRACKER_KEY); - return tracker; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Generic Solr tracker job, allowing Quartz to initiate an index update from + * a {@link Tracker} regardless of specific implementation. + * + * Concurrent execution is disallowed, as no parallel work can be done when + * indexing contents from the repository. + * + * @author Matt Ward + */ +@DisallowConcurrentExecution +public class TrackerJob implements Job +{ + public static final String JOBDATA_TRACKER_KEY = "TRACKER"; + protected final static Logger log = LoggerFactory.getLogger(TrackerJob.class); + + + /* + * (non-Javadoc) + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + @Override + public void execute(JobExecutionContext jec) throws JobExecutionException + { + Tracker tracker = getTracker(jec); + tracker.track(); + } + + /** + * Retrieve the {@link Tracker} from the {@link JobExecutionContext}. + * + * @param jec JobExecutionContext + * @return The tracker + */ + private Tracker getTracker(JobExecutionContext jec) + { + Tracker tracker = (Tracker) jec.getJobDetail().getJobDataMap().get(JOBDATA_TRACKER_KEY); + return tracker; + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerRegistry.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerRegistry.java index 8c45cd672..83dd155ff 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerRegistry.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/TrackerRegistry.java @@ -1,106 +1,106 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Registry of trackers for an AdminHandler - * @author Ahmed Owian - */ -public class TrackerRegistry -{ - /* - * Keyed on core name and Tracker class. - * This facilitates getting a particular type of Tracker for a given core - */ - private Map, Tracker>> trackers = new ConcurrentHashMap<>(); - - /* - * There is one ModelTracker per Alfresco data schema/repository, and it has no dependency on cores. - */ - private volatile ModelTracker modelTracker; - - public Set getCoreNames() - { - return this.trackers.keySet(); - } - - public Collection getTrackersForCore(String coreName) - { - ConcurrentHashMap, Tracker> coreTrackers = this.trackers.get(coreName); - return ((coreTrackers == null) ? Collections.emptyList() : coreTrackers.values()); - } - - public boolean hasTrackersForCore(String coreName) - { - return this.trackers.containsKey(coreName); - } - - @SuppressWarnings("unchecked") - public T getTrackerForCore(String coreName, Class trackerClass) - { - Map, Tracker> coreTrackers = this.trackers.get(coreName); - return null == coreTrackers ? null : (T) coreTrackers.get(trackerClass); - } - - public synchronized void register(String coreName, Tracker tracker) - { - ConcurrentHashMap, Tracker> coreTrackers = this.trackers.get(coreName); - if (coreTrackers == null) - { - coreTrackers = new ConcurrentHashMap<>(); - this.trackers.put(coreName, coreTrackers); - } - - coreTrackers.put(tracker.getClass(), tracker); - } - - /** - * Removes the trackers for the specified core. - * @param coreName - * @return true if there were trackers registered for the core - */ - public boolean removeTrackersForCore(String coreName) - { - return this.trackers.remove(coreName) != null; - } - - public ModelTracker getModelTracker() - { - return modelTracker; - } - - public void setModelTracker(ModelTracker modelTracker) - { - this.modelTracker = modelTracker; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Registry of trackers for an AdminHandler + * @author Ahmed Owian + */ +public class TrackerRegistry +{ + /* + * Keyed on core name and Tracker class. + * This facilitates getting a particular type of Tracker for a given core + */ + private Map, Tracker>> trackers = new ConcurrentHashMap<>(); + + /* + * There is one ModelTracker per Alfresco data schema/repository, and it has no dependency on cores. + */ + private volatile ModelTracker modelTracker; + + public Set getCoreNames() + { + return this.trackers.keySet(); + } + + public Collection getTrackersForCore(String coreName) + { + ConcurrentHashMap, Tracker> coreTrackers = this.trackers.get(coreName); + return ((coreTrackers == null) ? Collections.emptyList() : coreTrackers.values()); + } + + public boolean hasTrackersForCore(String coreName) + { + return this.trackers.containsKey(coreName); + } + + @SuppressWarnings("unchecked") + public T getTrackerForCore(String coreName, Class trackerClass) + { + Map, Tracker> coreTrackers = this.trackers.get(coreName); + return null == coreTrackers ? null : (T) coreTrackers.get(trackerClass); + } + + public synchronized void register(String coreName, Tracker tracker) + { + ConcurrentHashMap, Tracker> coreTrackers = this.trackers.get(coreName); + if (coreTrackers == null) + { + coreTrackers = new ConcurrentHashMap<>(); + this.trackers.put(coreName, coreTrackers); + } + + coreTrackers.put(tracker.getClass(), tracker); + } + + /** + * Removes the trackers for the specified core. + * @param coreName + * @return true if there were trackers registered for the core + */ + public boolean removeTrackersForCore(String coreName) + { + return this.trackers.remove(coreName) != null; + } + + public ModelTracker getModelTracker() + { + return modelTracker; + } + + public void setModelTracker(ModelTracker modelTracker) + { + this.modelTracker = modelTracker; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactory.java index c740d77c2..fcec22b62 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactory.java @@ -1,179 +1,179 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker.pool; - -import java.util.Properties; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.RejectedExecutionHandler; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.alfresco.util.DynamicallySizedThreadPoolExecutor; -import org.alfresco.util.TraceableThreadFactory; - -/** - * Implementation of the {@link TrackerPoolFactory}. - * - * @author Matt Ward - */ -public class DefaultTrackerPoolFactory implements TrackerPoolFactory -{ - private static final int DEFAULT_CORE_POOL_SIZE = 4; - - private static final int DEFAULT_MAXIMUM_POOL_SIZE = -1; // -1 is a sign that it must match the core pool size - - private static final int DEFAULT_KEEP_ALIVE_TIME = 120; // seconds - - private static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY; - - private static final boolean DEFAULT_THREAD_DAEMON = Boolean.TRUE; - - private static final int DEFAULT_WORK_QUEUE_SIZE = -1; - - private static final RejectedExecutionHandler DEFAULT_REJECTED_EXECUTION_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy(); - - private String poolName = ""; - - private int corePoolSize = DEFAULT_CORE_POOL_SIZE; - - private int maximumPoolSize = DEFAULT_MAXIMUM_POOL_SIZE; - - private int keepAliveTime = DEFAULT_KEEP_ALIVE_TIME; - - private int threadPriority = DEFAULT_THREAD_PRIORITY; - - private boolean threadDaemon = DEFAULT_THREAD_DAEMON; - - private int workQueueSize = DEFAULT_WORK_QUEUE_SIZE; - - private RejectedExecutionHandler rejectedExecutionHandler = DEFAULT_REJECTED_EXECUTION_HANDLER; - - private Integer parseConfig(String trackerConfig, Properties p, Integer ori) - { - if(p.getProperty(trackerConfig) != null && !p.getProperty(trackerConfig).isEmpty()) - { - return Integer.parseInt(p.getProperty(trackerConfig)); - } - return ori; - } - - private Boolean parseConfigBoolean(String trackerConfig, Properties p) - { - if(p.getProperty(trackerConfig) !=null && !p.getProperty(trackerConfig).isEmpty()) - { - return Boolean.parseBoolean(p.getProperty(trackerConfig)); - } - String param = "alfresco" + trackerConfig.substring(trackerConfig.lastIndexOf(".")); - return Boolean.parseBoolean(p.getProperty(param,"true")); - } - - public DefaultTrackerPoolFactory(Properties p, String coreName, String trackerName) - { - corePoolSize = Integer.parseInt(p.getProperty("alfresco.corePoolSize", "4")); - maximumPoolSize = Integer.parseInt(p.getProperty("alfresco.maximumPoolSize", "-1")); - keepAliveTime = Integer.parseInt(p.getProperty("alfresco.keepAliveTime", "120")); - threadPriority = Integer.parseInt(p.getProperty("alfresco.threadPriority", "5")); - threadDaemon = Boolean.parseBoolean(p.getProperty("alfresco.threadDaemon", "true")); - workQueueSize = Integer.parseInt(p.getProperty("alfresco.workQueueSize", "-1")); - if(trackerName != null && !trackerName.isEmpty()) - { - switch (trackerName) - { - case "AclTracker": - corePoolSize = parseConfig("alfresco.acl.tracker.corePoolSize", p, corePoolSize); - maximumPoolSize = parseConfig("alfresco.acl.tracker.maximumPoolSize", p, maximumPoolSize); - keepAliveTime = parseConfig("alfresco.acl.tracker.keepAliveTime", p, keepAliveTime); - threadPriority = parseConfig("alfresco.acl.tracker.threadPriority", p, threadPriority); - threadDaemon = parseConfigBoolean("alfresco.acl.tracker.threadDaemon", p); - workQueueSize = parseConfig("alfresco.acl.tracker.workQueueSize", p, workQueueSize); - break; - case "ContentTracker": - corePoolSize = parseConfig("alfresco.content.tracker.corePoolSize", p, corePoolSize); - maximumPoolSize = parseConfig("alfresco.content.tracker.maximumPoolSize", p, maximumPoolSize); - keepAliveTime = parseConfig("alfresco.content.tracker.keepAliveTime", p, keepAliveTime); - threadPriority = parseConfig("alfresco.content.tracker.threadPriority", p, threadPriority); - threadDaemon = parseConfigBoolean("alfresco.content.tracker.threadDaemon", p); - workQueueSize = parseConfig("alfresco.content.tracker.workQueueSize", p, workQueueSize); - break; - case "MetadataTracker": - corePoolSize = parseConfig("alfresco.metadata.tracker.corePoolSize", p, corePoolSize); - maximumPoolSize = parseConfig("alfresco.metadata.tracker.maximumPoolSize", p, maximumPoolSize); - keepAliveTime = parseConfig("alfresco.metadata.tracker.keepAliveTime", p, keepAliveTime); - threadPriority = parseConfig("alfresco.metadata.tracker.threadPriority", p, threadPriority); - threadDaemon = parseConfigBoolean("alfresco.metadata.tracker.threadDaemon", p); - workQueueSize = parseConfig("alfresco.metadata.tracker.workQueueSize", p, workQueueSize); - break; - default: - break; - } - } - - - poolName = "SolrTrackingPool-" + coreName + "-" + trackerName + "-"; - - // if the maximum pool size has not been set, change it to match the core pool size - if (maximumPoolSize == DEFAULT_MAXIMUM_POOL_SIZE) - { - maximumPoolSize = corePoolSize; - } - } - - @Override - public ThreadPoolExecutor create() - { - // We need a thread factory - TraceableThreadFactory threadFactory = new TraceableThreadFactory(); - threadFactory.setThreadDaemon(threadDaemon); - threadFactory.setThreadPriority(threadPriority); - - if (poolName.length() > 0) - { - threadFactory.setNamePrefix(poolName); - } - - BlockingQueue workQueue; - if (workQueueSize < 0) - { - // We can have an unlimited queue, as we have a sensible thread pool! - workQueue = new LinkedBlockingQueue(); - } - else - { - // Use an array one for consistent performance on a small queue size - workQueue = new ArrayBlockingQueue(workQueueSize); - } - - - ThreadPoolExecutor threadPoolExecutor = new DynamicallySizedThreadPoolExecutor( - corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, - workQueue, threadFactory, rejectedExecutionHandler); - - return threadPoolExecutor; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker.pool; + +import java.util.Properties; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.alfresco.util.DynamicallySizedThreadPoolExecutor; +import org.alfresco.util.TraceableThreadFactory; + +/** + * Implementation of the {@link TrackerPoolFactory}. + * + * @author Matt Ward + */ +public class DefaultTrackerPoolFactory implements TrackerPoolFactory +{ + private static final int DEFAULT_CORE_POOL_SIZE = 4; + + private static final int DEFAULT_MAXIMUM_POOL_SIZE = -1; // -1 is a sign that it must match the core pool size + + private static final int DEFAULT_KEEP_ALIVE_TIME = 120; // seconds + + private static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY; + + private static final boolean DEFAULT_THREAD_DAEMON = Boolean.TRUE; + + private static final int DEFAULT_WORK_QUEUE_SIZE = -1; + + private static final RejectedExecutionHandler DEFAULT_REJECTED_EXECUTION_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy(); + + private String poolName = ""; + + private int corePoolSize = DEFAULT_CORE_POOL_SIZE; + + private int maximumPoolSize = DEFAULT_MAXIMUM_POOL_SIZE; + + private int keepAliveTime = DEFAULT_KEEP_ALIVE_TIME; + + private int threadPriority = DEFAULT_THREAD_PRIORITY; + + private boolean threadDaemon = DEFAULT_THREAD_DAEMON; + + private int workQueueSize = DEFAULT_WORK_QUEUE_SIZE; + + private RejectedExecutionHandler rejectedExecutionHandler = DEFAULT_REJECTED_EXECUTION_HANDLER; + + private Integer parseConfig(String trackerConfig, Properties p, Integer ori) + { + if(p.getProperty(trackerConfig) != null && !p.getProperty(trackerConfig).isEmpty()) + { + return Integer.parseInt(p.getProperty(trackerConfig)); + } + return ori; + } + + private Boolean parseConfigBoolean(String trackerConfig, Properties p) + { + if(p.getProperty(trackerConfig) !=null && !p.getProperty(trackerConfig).isEmpty()) + { + return Boolean.parseBoolean(p.getProperty(trackerConfig)); + } + String param = "alfresco" + trackerConfig.substring(trackerConfig.lastIndexOf(".")); + return Boolean.parseBoolean(p.getProperty(param,"true")); + } + + public DefaultTrackerPoolFactory(Properties p, String coreName, String trackerName) + { + corePoolSize = Integer.parseInt(p.getProperty("alfresco.corePoolSize", "4")); + maximumPoolSize = Integer.parseInt(p.getProperty("alfresco.maximumPoolSize", "-1")); + keepAliveTime = Integer.parseInt(p.getProperty("alfresco.keepAliveTime", "120")); + threadPriority = Integer.parseInt(p.getProperty("alfresco.threadPriority", "5")); + threadDaemon = Boolean.parseBoolean(p.getProperty("alfresco.threadDaemon", "true")); + workQueueSize = Integer.parseInt(p.getProperty("alfresco.workQueueSize", "-1")); + if(trackerName != null && !trackerName.isEmpty()) + { + switch (trackerName) + { + case "AclTracker": + corePoolSize = parseConfig("alfresco.acl.tracker.corePoolSize", p, corePoolSize); + maximumPoolSize = parseConfig("alfresco.acl.tracker.maximumPoolSize", p, maximumPoolSize); + keepAliveTime = parseConfig("alfresco.acl.tracker.keepAliveTime", p, keepAliveTime); + threadPriority = parseConfig("alfresco.acl.tracker.threadPriority", p, threadPriority); + threadDaemon = parseConfigBoolean("alfresco.acl.tracker.threadDaemon", p); + workQueueSize = parseConfig("alfresco.acl.tracker.workQueueSize", p, workQueueSize); + break; + case "ContentTracker": + corePoolSize = parseConfig("alfresco.content.tracker.corePoolSize", p, corePoolSize); + maximumPoolSize = parseConfig("alfresco.content.tracker.maximumPoolSize", p, maximumPoolSize); + keepAliveTime = parseConfig("alfresco.content.tracker.keepAliveTime", p, keepAliveTime); + threadPriority = parseConfig("alfresco.content.tracker.threadPriority", p, threadPriority); + threadDaemon = parseConfigBoolean("alfresco.content.tracker.threadDaemon", p); + workQueueSize = parseConfig("alfresco.content.tracker.workQueueSize", p, workQueueSize); + break; + case "MetadataTracker": + corePoolSize = parseConfig("alfresco.metadata.tracker.corePoolSize", p, corePoolSize); + maximumPoolSize = parseConfig("alfresco.metadata.tracker.maximumPoolSize", p, maximumPoolSize); + keepAliveTime = parseConfig("alfresco.metadata.tracker.keepAliveTime", p, keepAliveTime); + threadPriority = parseConfig("alfresco.metadata.tracker.threadPriority", p, threadPriority); + threadDaemon = parseConfigBoolean("alfresco.metadata.tracker.threadDaemon", p); + workQueueSize = parseConfig("alfresco.metadata.tracker.workQueueSize", p, workQueueSize); + break; + default: + break; + } + } + + + poolName = "SolrTrackingPool-" + coreName + "-" + trackerName + "-"; + + // if the maximum pool size has not been set, change it to match the core pool size + if (maximumPoolSize == DEFAULT_MAXIMUM_POOL_SIZE) + { + maximumPoolSize = corePoolSize; + } + } + + @Override + public ThreadPoolExecutor create() + { + // We need a thread factory + TraceableThreadFactory threadFactory = new TraceableThreadFactory(); + threadFactory.setThreadDaemon(threadDaemon); + threadFactory.setThreadPriority(threadPriority); + + if (poolName.length() > 0) + { + threadFactory.setNamePrefix(poolName); + } + + BlockingQueue workQueue; + if (workQueueSize < 0) + { + // We can have an unlimited queue, as we have a sensible thread pool! + workQueue = new LinkedBlockingQueue(); + } + else + { + // Use an array one for consistent performance on a small queue size + workQueue = new ArrayBlockingQueue(workQueueSize); + } + + + ThreadPoolExecutor threadPoolExecutor = new DynamicallySizedThreadPoolExecutor( + corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, + workQueue, threadFactory, rejectedExecutionHandler); + + return threadPoolExecutor; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/TrackerPoolFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/TrackerPoolFactory.java index fc354c0cd..b4a714442 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/TrackerPoolFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/pool/TrackerPoolFactory.java @@ -1,39 +1,39 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker.pool; - -import java.util.concurrent.ThreadPoolExecutor; - -/** - * Create a thread pool for the {@link org.alfresco.solr.tracker.Tracker}s. - * - * @author Matt Ward - */ -public interface TrackerPoolFactory -{ - ThreadPoolExecutor create(); -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker.pool; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * Create a thread pool for the {@link org.alfresco.solr.tracker.Tracker}s. + * + * @author Matt Ward + */ +public interface TrackerPoolFactory +{ + ThreadPoolExecutor create(); +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformer.java index 9034b984f..5783e6260 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -138,4 +138,4 @@ public class AlfrescoFieldMapperTransformer extends DocTransformer return value; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerFactory.java index 26522265e..40fe2e2d4 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformer.java index 80d9d06e7..2121275af 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformer.java @@ -1,189 +1,189 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.transformer; - -import java.io.IOException; -import java.util.ArrayList; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.SortedDocValues; -import org.apache.lucene.index.SortedNumericDocValues; -import org.apache.lucene.index.SortedSetDocValues; -import org.apache.lucene.util.NumericUtils; -import org.apache.solr.common.SolrDocument; -import org.apache.solr.response.ResultContext; -import org.apache.solr.response.transform.DocTransformer; -import org.apache.solr.schema.SchemaField; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - * - */ -public class DocValueDocTransformer extends DocTransformer -{ - protected final static Logger log = LoggerFactory.getLogger(DocValueDocTransformer.class); - - /* (non-Javadoc) - * @see org.apache.solr.response.transform.DocTransformer#getName() - */ - @Override - public String getName() - { - return "Alfresco doc value document transformer"; - } - - - public void setContext( ResultContext context ) - { - this.context = context; - } - - - /* (non-Javadoc) - * @see org.apache.solr.response.transform.DocTransformer#transform(org.apache.solr.common.SolrDocument, int) - */ - @Override - public void transform(SolrDocument doc, int docid, float score) throws IOException - { - for(String fieldName :context.getSearcher().getFieldNames()) - { - SchemaField schemaField = context.getSearcher().getSchema().getFieldOrNull(fieldName); - if(schemaField != null) - { - if(schemaField.hasDocValues()) - { - SortedDocValues sortedDocValues = context.getSearcher().getSlowAtomicReader().getSortedDocValues(fieldName); - if(sortedDocValues != null) - { - int ordinal = sortedDocValues.getOrd(docid); - if(ordinal > -1) - { - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - doc.addField(alfrescoFieldName, schemaField.getType().toObject(schemaField, sortedDocValues.lookupOrd(ordinal))); - } - } - - SortedSetDocValues sortedSetDocValues = context.getSearcher().getSlowAtomicReader().getSortedSetDocValues(fieldName); - if(sortedSetDocValues != null) - { - ArrayList newValues = new ArrayList(); - sortedSetDocValues.setDocument(docid); - long ordinal; - while ( (ordinal = sortedSetDocValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) - { - newValues.add(schemaField.getType().toObject(schemaField, sortedSetDocValues.lookupOrd(ordinal))); - } - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - doc.addField(alfrescoFieldName, newValues); - } - - - BinaryDocValues binaryDocValues = context.getSearcher().getSlowAtomicReader().getBinaryDocValues(fieldName); - if(binaryDocValues != null) - { - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - doc.addField(alfrescoFieldName, schemaField.getType().toObject(schemaField, binaryDocValues.get(docid))); - } - - if(schemaField.getType().getNumericType() != null) - { - NumericDocValues numericDocValues = context.getSearcher().getSlowAtomicReader().getNumericDocValues(fieldName); - if(numericDocValues != null) - { - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - switch(schemaField.getType().getNumericType()) - { - case DOUBLE: - doc.addField(alfrescoFieldName, Double.longBitsToDouble(numericDocValues.get(docid))); - break; - case FLOAT: - doc.addField(alfrescoFieldName, Float.intBitsToFloat((int) numericDocValues.get(docid))); - break; - case INT: - doc.addField(alfrescoFieldName, (int) numericDocValues.get(docid)); - break; - case LONG: - doc.addField(alfrescoFieldName, numericDocValues.get(docid)); - break; - } - } - - SortedNumericDocValues sortedNumericDocValues = context.getSearcher().getSlowAtomicReader().getSortedNumericDocValues(fieldName); - if(sortedNumericDocValues != null) - { - sortedNumericDocValues.setDocument(docid); - doc.removeFields(fieldName); - String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); - doc.removeFields(alfrescoFieldName); - ArrayList newValues = new ArrayList(sortedNumericDocValues.count()); - if(sortedNumericDocValues.count() > 0) - { - - for(int i = 0; i < sortedNumericDocValues.count(); i++) - { - switch(schemaField.getType().getNumericType()) - { - case DOUBLE: - newValues.add(NumericUtils.sortableLongToDouble(sortedNumericDocValues.valueAt(i))); - break; - case FLOAT: - newValues.add(NumericUtils.sortableIntToFloat((int)sortedNumericDocValues.valueAt(i))); - break; - case INT: - newValues.add((int)sortedNumericDocValues.valueAt(i)); - break; - case LONG: - newValues.add(sortedNumericDocValues.valueAt(i)); - break; - - } - } - } - doc.addField(alfrescoFieldName, newValues); - - } - } - } - } - } - - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.transformer; + +import java.io.IOException; +import java.util.ArrayList; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.index.SortedNumericDocValues; +import org.apache.lucene.index.SortedSetDocValues; +import org.apache.lucene.util.NumericUtils; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.response.ResultContext; +import org.apache.solr.response.transform.DocTransformer; +import org.apache.solr.schema.SchemaField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + * + */ +public class DocValueDocTransformer extends DocTransformer +{ + protected final static Logger log = LoggerFactory.getLogger(DocValueDocTransformer.class); + + /* (non-Javadoc) + * @see org.apache.solr.response.transform.DocTransformer#getName() + */ + @Override + public String getName() + { + return "Alfresco doc value document transformer"; + } + + + public void setContext( ResultContext context ) + { + this.context = context; + } + + + /* (non-Javadoc) + * @see org.apache.solr.response.transform.DocTransformer#transform(org.apache.solr.common.SolrDocument, int) + */ + @Override + public void transform(SolrDocument doc, int docid, float score) throws IOException + { + for(String fieldName :context.getSearcher().getFieldNames()) + { + SchemaField schemaField = context.getSearcher().getSchema().getFieldOrNull(fieldName); + if(schemaField != null) + { + if(schemaField.hasDocValues()) + { + SortedDocValues sortedDocValues = context.getSearcher().getSlowAtomicReader().getSortedDocValues(fieldName); + if(sortedDocValues != null) + { + int ordinal = sortedDocValues.getOrd(docid); + if(ordinal > -1) + { + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + doc.addField(alfrescoFieldName, schemaField.getType().toObject(schemaField, sortedDocValues.lookupOrd(ordinal))); + } + } + + SortedSetDocValues sortedSetDocValues = context.getSearcher().getSlowAtomicReader().getSortedSetDocValues(fieldName); + if(sortedSetDocValues != null) + { + ArrayList newValues = new ArrayList(); + sortedSetDocValues.setDocument(docid); + long ordinal; + while ( (ordinal = sortedSetDocValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) + { + newValues.add(schemaField.getType().toObject(schemaField, sortedSetDocValues.lookupOrd(ordinal))); + } + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + doc.addField(alfrescoFieldName, newValues); + } + + + BinaryDocValues binaryDocValues = context.getSearcher().getSlowAtomicReader().getBinaryDocValues(fieldName); + if(binaryDocValues != null) + { + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + doc.addField(alfrescoFieldName, schemaField.getType().toObject(schemaField, binaryDocValues.get(docid))); + } + + if(schemaField.getType().getNumericType() != null) + { + NumericDocValues numericDocValues = context.getSearcher().getSlowAtomicReader().getNumericDocValues(fieldName); + if(numericDocValues != null) + { + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + switch(schemaField.getType().getNumericType()) + { + case DOUBLE: + doc.addField(alfrescoFieldName, Double.longBitsToDouble(numericDocValues.get(docid))); + break; + case FLOAT: + doc.addField(alfrescoFieldName, Float.intBitsToFloat((int) numericDocValues.get(docid))); + break; + case INT: + doc.addField(alfrescoFieldName, (int) numericDocValues.get(docid)); + break; + case LONG: + doc.addField(alfrescoFieldName, numericDocValues.get(docid)); + break; + } + } + + SortedNumericDocValues sortedNumericDocValues = context.getSearcher().getSlowAtomicReader().getSortedNumericDocValues(fieldName); + if(sortedNumericDocValues != null) + { + sortedNumericDocValues.setDocument(docid); + doc.removeFields(fieldName); + String alfrescoFieldName = AlfrescoSolrDataModel.getInstance().getAlfrescoPropertyFromSchemaField(fieldName); + doc.removeFields(alfrescoFieldName); + ArrayList newValues = new ArrayList(sortedNumericDocValues.count()); + if(sortedNumericDocValues.count() > 0) + { + + for(int i = 0; i < sortedNumericDocValues.count(); i++) + { + switch(schemaField.getType().getNumericType()) + { + case DOUBLE: + newValues.add(NumericUtils.sortableLongToDouble(sortedNumericDocValues.valueAt(i))); + break; + case FLOAT: + newValues.add(NumericUtils.sortableIntToFloat((int)sortedNumericDocValues.valueAt(i))); + break; + case INT: + newValues.add((int)sortedNumericDocValues.valueAt(i)); + break; + case LONG: + newValues.add(sortedNumericDocValues.valueAt(i)); + break; + + } + } + } + doc.addField(alfrescoFieldName, newValues); + + } + } + } + } + } + + } + + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformerFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformerFactory.java index d40ab3741..0829b3457 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformerFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/transformer/DocValueDocTransformerFactory.java @@ -1,50 +1,50 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.transformer; - -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.transform.DocTransformer; -import org.apache.solr.response.transform.TransformerFactory; - -/** - * @author Andy - * - */ -public class DocValueDocTransformerFactory extends TransformerFactory -{ - - /* (non-Javadoc) - * @see org.apache.solr.response.transform.TransformerFactory#create(java.lang.String, org.apache.solr.common.params.SolrParams, org.apache.solr.request.SolrQueryRequest) - */ - @Override - public DocTransformer create(String field, SolrParams params, SolrQueryRequest req) - { - return new DocValueDocTransformer(); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.transformer; + +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.transform.DocTransformer; +import org.apache.solr.response.transform.TransformerFactory; + +/** + * @author Andy + * + */ +public class DocValueDocTransformerFactory extends TransformerFactory +{ + + /* (non-Javadoc) + * @see org.apache.solr.response.transform.TransformerFactory#create(java.lang.String, org.apache.solr.common.params.SolrParams, org.apache.solr.request.SolrQueryRequest) + */ + @Override + public DocTransformer create(String field, SolrParams params, SolrQueryRequest req) + { + return new DocValueDocTransformer(); + } + +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoDistributedUpdateProcessorFactory.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoDistributedUpdateProcessorFactory.java index 8e181ef51..7767df6f3 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoDistributedUpdateProcessorFactory.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoDistributedUpdateProcessorFactory.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 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% diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoExplicitDocumentMerger.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoExplicitDocumentMerger.java index 3a3def45c..bffb57bf6 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoExplicitDocumentMerger.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/update/processor/AlfrescoExplicitDocumentMerger.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 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% diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/AlfrescoFileUtils.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/AlfrescoFileUtils.java index b9e58be58..7f5ba804a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/AlfrescoFileUtils.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/AlfrescoFileUtils.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingConsumer.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingConsumer.java index 4878d0412..da476c76a 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingConsumer.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingConsumer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingFunction.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingFunction.java index 3ff20d11a..a6ca43dde 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingFunction.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/ThrowingFunction.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/Utils.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/Utils.java index ed5f45c71..0e95205b8 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/Utils.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/utils/Utils.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -188,4 +188,4 @@ public abstract class Utils return null; } } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilter.java b/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilter.java index d0774e22d..c31834e58 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilter.java +++ b/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilter.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilterFactory.java b/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilterFactory.java index 8191bc2b7..3ef3b7d18 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilterFactory.java +++ b/search-services/alfresco-search/src/main/java/org/apache/lucene/analysis/minhash/ContextAccumulatingFilterFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/core/CoreDescriptorDecorator.java b/search-services/alfresco-search/src/main/java/org/apache/solr/core/CoreDescriptorDecorator.java index 1fc436606..0e1b5eeee 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/core/CoreDescriptorDecorator.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/core/CoreDescriptorDecorator.java @@ -1,108 +1,108 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.apache.solr.core; - -import java.util.List; -import java.util.Properties; - -import org.alfresco.solr.config.ConfigUtil; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.google.common.collect.ImmutableList; - -/** - * This class was created solely for the purpose of exposing the coreProperties of the CoreDescriptor. - * It is now possible to substitute a sub-set of properties using the rules specified here @see ConfigUtil#locateProperty() - * - * The Substitutable Properties are defined in the substitutableProperties list. - * @author Ahmed Owian - * @author Gethin James - * @author aborroy - */ -public class CoreDescriptorDecorator -{ - private static Log log = LogFactory.getLog(CoreDescriptorDecorator.class); - private final Properties properties = new Properties(); - - private static String SECURE_COMMS_PROPERTY = "alfresco.secureComms"; - - public static final ImmutableList SUBSTITUTABLE_PROPERTIES_SECURE = - ImmutableList.of( - "alfresco.host", - "alfresco.port", - "alfresco.baseUrl", - "alfresco.port.ssl", - "alfresco.secureComms", - "alfresco.encryption.ssl.keystore.passwordFileLocation", - "alfresco.encryption.ssl.truststore.passwordFileLocation", - "alfresco.encryption.ssl.keystore.location", - "alfresco.encryption.ssl.truststore.location", - "alfresco.encryption.ssl.truststore.provider", - "alfresco.encryption.ssl.keystore.type", - "alfresco.encryption.ssl.keystore.provider", - "alfresco.encryption.ssl.truststore.type"); - - public static final ImmutableList SUBSTITUTABLE_PROPERTIES_NONE = - ImmutableList.of( - "alfresco.host", - "alfresco.port", - "alfresco.baseUrl", - "alfresco.secureComms"); - - public CoreDescriptorDecorator(CoreDescriptor descriptor) - { - properties.putAll(descriptor.coreProperties); - - List coreProperties; - String comms = ConfigUtil.locateProperty(SECURE_COMMS_PROPERTY, "none"); - if (comms.equals("https")) - { - coreProperties = SUBSTITUTABLE_PROPERTIES_SECURE; - } - else - { - coreProperties = SUBSTITUTABLE_PROPERTIES_NONE; - } - - try - { - coreProperties.forEach(prop -> - properties.put(prop, ConfigUtil.locateProperty(prop,properties.getProperty(prop))) - ); - } - catch(Exception e) - { - log.warn("Unable to locate alfresco host|port|baseUrl|ssl properties", e); - } - } - - public Properties getProperties() - { - return this.properties; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.apache.solr.core; + +import java.util.List; +import java.util.Properties; + +import org.alfresco.solr.config.ConfigUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.google.common.collect.ImmutableList; + +/** + * This class was created solely for the purpose of exposing the coreProperties of the CoreDescriptor. + * It is now possible to substitute a sub-set of properties using the rules specified here @see ConfigUtil#locateProperty() + * + * The Substitutable Properties are defined in the substitutableProperties list. + * @author Ahmed Owian + * @author Gethin James + * @author aborroy + */ +public class CoreDescriptorDecorator +{ + private static Log log = LogFactory.getLog(CoreDescriptorDecorator.class); + private final Properties properties = new Properties(); + + private static String SECURE_COMMS_PROPERTY = "alfresco.secureComms"; + + public static final ImmutableList SUBSTITUTABLE_PROPERTIES_SECURE = + ImmutableList.of( + "alfresco.host", + "alfresco.port", + "alfresco.baseUrl", + "alfresco.port.ssl", + "alfresco.secureComms", + "alfresco.encryption.ssl.keystore.passwordFileLocation", + "alfresco.encryption.ssl.truststore.passwordFileLocation", + "alfresco.encryption.ssl.keystore.location", + "alfresco.encryption.ssl.truststore.location", + "alfresco.encryption.ssl.truststore.provider", + "alfresco.encryption.ssl.keystore.type", + "alfresco.encryption.ssl.keystore.provider", + "alfresco.encryption.ssl.truststore.type"); + + public static final ImmutableList SUBSTITUTABLE_PROPERTIES_NONE = + ImmutableList.of( + "alfresco.host", + "alfresco.port", + "alfresco.baseUrl", + "alfresco.secureComms"); + + public CoreDescriptorDecorator(CoreDescriptor descriptor) + { + properties.putAll(descriptor.coreProperties); + + List coreProperties; + String comms = ConfigUtil.locateProperty(SECURE_COMMS_PROPERTY, "none"); + if (comms.equals("https")) + { + coreProperties = SUBSTITUTABLE_PROPERTIES_SECURE; + } + else + { + coreProperties = SUBSTITUTABLE_PROPERTIES_NONE; + } + + try + { + coreProperties.forEach(prop -> + properties.put(prop, ConfigUtil.locateProperty(prop,properties.getProperty(prop))) + ); + } + catch(Exception e) + { + log.warn("Unable to locate alfresco host|port|baseUrl|ssl properties", e); + } + } + + public Properties getProperties() + { + return this.properties; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandler.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandler.java index 960375786..7a469b086 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandler.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandler.java @@ -1,51 +1,51 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; - -import org.alfresco.solr.query.AbstractQParser; -import org.apache.http.client.HttpClient; -import org.apache.solr.client.solrj.SolrRequest; -import org.apache.solr.client.solrj.request.QueryRequest; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.util.ContentStreamBase; - -public class AlfrescoHttpShardHandler extends HttpShardHandler { - - public AlfrescoHttpShardHandler(HttpShardHandlerFactory alfrescoHttpShardHandlerFactory, HttpClient httpClient) { - super(alfrescoHttpShardHandlerFactory, httpClient); - } - - /** - * Subclasses could modify the request based on the shard - */ - protected QueryRequest makeQueryRequest(final ShardRequest sreq, ModifiableSolrParams params, String shard) - { - AlfrescoQueryRequest req = new AlfrescoQueryRequest(params, SolrRequest.METHOD.POST); - return req; - } -} - +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; + +import org.alfresco.solr.query.AbstractQParser; +import org.apache.http.client.HttpClient; +import org.apache.solr.client.solrj.SolrRequest; +import org.apache.solr.client.solrj.request.QueryRequest; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.util.ContentStreamBase; + +public class AlfrescoHttpShardHandler extends HttpShardHandler { + + public AlfrescoHttpShardHandler(HttpShardHandlerFactory alfrescoHttpShardHandlerFactory, HttpClient httpClient) { + super(alfrescoHttpShardHandlerFactory, httpClient); + } + + /** + * Subclasses could modify the request based on the shard + */ + protected QueryRequest makeQueryRequest(final ShardRequest sreq, ModifiableSolrParams params, String shard) + { + AlfrescoQueryRequest req = new AlfrescoQueryRequest(params, SolrRequest.METHOD.POST); + return req; + } +} + diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandlerFactory.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandlerFactory.java index d4eab0f06..afae2a709 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandlerFactory.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoHttpShardHandlerFactory.java @@ -1,50 +1,50 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; - - -import org.apache.http.client.HttpClient; - - - -/** - * @author Andy - * - */ - -public class AlfrescoHttpShardHandlerFactory extends HttpShardHandlerFactory { - - - /** - * Get {@link ShardHandler} that uses custom http client. - */ - public ShardHandler getShardHandler(final HttpClient httpClient){ - return new AlfrescoHttpShardHandler(this, httpClient); - } - -} - +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; + + +import org.apache.http.client.HttpClient; + + + +/** + * @author Andy + * + */ + +public class AlfrescoHttpShardHandlerFactory extends HttpShardHandlerFactory { + + + /** + * Get {@link ShardHandler} that uses custom http client. + */ + public ShardHandler getShardHandler(final HttpClient httpClient){ + return new AlfrescoHttpShardHandler(this, httpClient); + } + +} + diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandler.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandler.java index 9c2a0b09f..a42d6cd4f 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandler.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandler.java @@ -1,852 +1,852 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; - -import static org.apache.lucene.index.IndexOptions.DOCS; -import static org.apache.lucene.index.IndexOptions.DOCS_AND_FREQS; -import static org.apache.lucene.index.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS; - -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.util.CharFilterFactory; -import org.apache.lucene.analysis.util.TokenFilterFactory; -import org.apache.lucene.analysis.util.TokenizerFactory; -import org.apache.lucene.document.Document; -import org.apache.lucene.document.Field; -import org.apache.lucene.index.DirectoryReader; -import org.apache.lucene.index.DocValuesType; -import org.apache.lucene.index.FieldInfo; -import org.apache.lucene.index.IndexOptions; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.MultiFields; -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.index.SegmentReader; -import org.apache.lucene.index.Term; -import org.apache.lucene.index.Terms; -import org.apache.lucene.index.TermsEnum; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.similarities.Similarity; -import org.apache.lucene.store.Directory; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.CharsRefBuilder; -import org.apache.lucene.util.PriorityQueue; -import org.apache.solr.analysis.TokenizerChain; -import org.apache.solr.common.SolrException; -import org.apache.solr.common.SolrException.ErrorCode; -import org.apache.solr.common.luke.FieldFlag; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.Base64; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.handler.RequestHandlerBase; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.schema.CopyField; -import org.apache.solr.schema.FieldType; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.update.SolrIndexWriter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class AlfrescoLukeRequestHandler extends RequestHandlerBase { - private static final Logger log = LoggerFactory.getLogger(MethodHandles - .lookup().lookupClass()); - - public static final String NUMTERMS = "numTerms"; - public static final String INCLUDE_INDEX_FIELD_FLAGS = "includeIndexFieldFlags"; - public static final String DOC_ID = "docId"; - public static final String ID = "id"; - public static final int DEFAULT_COUNT = 10; - - static final int HIST_ARRAY_SIZE = 33; - - private static enum ShowStyle { - ALL, DOC, SCHEMA, INDEX; - - public static ShowStyle get(String v) { - if (v == null) - return null; - if ("schema".equalsIgnoreCase(v)) - return SCHEMA; - if ("index".equalsIgnoreCase(v)) - return INDEX; - if ("doc".equalsIgnoreCase(v)) - return DOC; - if ("all".equalsIgnoreCase(v)) - return ALL; - throw new SolrException(ErrorCode.BAD_REQUEST, - "Unknown Show Style: " + v); - } - }; - - @Override - public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) - throws Exception { - IndexSchema schema = req.getSchema(); - SolrIndexSearcher searcher = req.getSearcher(); - DirectoryReader reader = searcher.getIndexReader(); - SolrParams params = req.getParams(); - ShowStyle style = ShowStyle.get(params.get("show")); - - // If no doc is given, show all fields and top terms - - rsp.add("index", getIndexInfo(reader)); - - if (ShowStyle.INDEX == style) { - return; // that's all we need - } - - Integer docId = params.getInt(DOC_ID); - if (docId == null && params.get(ID) != null) { - // Look for something with a given solr ID - SchemaField uniqueKey = schema.getUniqueKeyField(); - String v = uniqueKey.getType().toInternal(params.get(ID)); - Term t = new Term(uniqueKey.getName(), v); - docId = searcher.getFirstMatch(t); - if (docId < 0) { - throw new SolrException(SolrException.ErrorCode.NOT_FOUND, - "Can't find document: " + params.get(ID)); - } - } - - // Read the document from the index - if (docId != null) { - if (style != null && style != ShowStyle.DOC) { - throw new SolrException(ErrorCode.BAD_REQUEST, - "missing doc param for doc style"); - } - Document doc = null; - try { - doc = reader.document(docId); - } catch (Exception ex) { - } - if (doc == null) { - throw new SolrException(SolrException.ErrorCode.NOT_FOUND, - "Can't find document: " + docId); - } - - SimpleOrderedMap info = getDocumentFieldsInfo(doc, docId, - reader, schema); - - SimpleOrderedMap docinfo = new SimpleOrderedMap<>(); - docinfo.add("docId", docId); - docinfo.add("lucene", info); - docinfo.add("solr", doc); - rsp.add("doc", docinfo); - } else if (ShowStyle.SCHEMA == style) { - rsp.add("schema", getSchemaInfo(req.getSchema())); - } else { - rsp.add("fields", getIndexedFieldsInfo(req)); - } - - // Add some generally helpful information - NamedList info = new SimpleOrderedMap<>(); - info.add("key", getFieldFlagsKey()); - info.add( - "NOTE", - "Document Frequency (df) is not updated when a document is marked for deletion. df values include deleted documents."); - rsp.add("info", info); - rsp.setHttpCaching(false); - } - - /** - * @return a string representing a IndexableField's flags. - */ - private static String getFieldFlags(IndexableField f) { - IndexOptions opts = (f == null) ? null : f.fieldType().indexOptions(); - - StringBuilder flags = new StringBuilder(); - - flags.append((f != null && f.fieldType().indexOptions() != IndexOptions.NONE) ? FieldFlag.INDEXED - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().tokenized()) ? FieldFlag.TOKENIZED - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().stored()) ? FieldFlag.STORED - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().docValuesType() != DocValuesType.NONE) ? FieldFlag.DOC_VALUES - .getAbbreviation() : "-"); - flags.append((false) ? FieldFlag.MULTI_VALUED.getAbbreviation() : '-'); // SchemaField - // Specific - flags.append((f != null && f.fieldType().storeTermVectors()) ? FieldFlag.TERM_VECTOR_STORED - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().storeTermVectorOffsets()) ? FieldFlag.TERM_VECTOR_OFFSET - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().storeTermVectorPositions()) ? FieldFlag.TERM_VECTOR_POSITION - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().storeTermVectorPayloads()) ? FieldFlag.TERM_VECTOR_PAYLOADS - .getAbbreviation() : '-'); - flags.append((f != null && f.fieldType().omitNorms()) ? FieldFlag.OMIT_NORMS - .getAbbreviation() : '-'); - - flags.append((f != null && DOCS == opts) ? FieldFlag.OMIT_TF - .getAbbreviation() : '-'); - - flags.append((f != null && DOCS_AND_FREQS == opts) ? FieldFlag.OMIT_POSITIONS - .getAbbreviation() : '-'); - - flags.append((f != null && DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS == opts) ? FieldFlag.STORE_OFFSETS_WITH_POSITIONS - .getAbbreviation() : '-'); - - flags.append((f != null && f.getClass().getSimpleName() - .equals("LazyField")) ? FieldFlag.LAZY.getAbbreviation() : '-'); - flags.append((f != null && f.binaryValue() != null) ? FieldFlag.BINARY - .getAbbreviation() : '-'); - flags.append((false) ? FieldFlag.SORT_MISSING_FIRST.getAbbreviation() - : '-'); // SchemaField Specific - flags.append((false) ? FieldFlag.SORT_MISSING_LAST.getAbbreviation() - : '-'); // SchemaField Specific - return flags.toString(); - } - - /** - * @return a string representing a SchemaField's flags. - */ - private static String getFieldFlags(SchemaField f) { - FieldType t = (f == null) ? null : f.getType(); - - // see: - // http://www.nabble.com/schema-field-properties-tf3437753.html#a9585549 - boolean lazy = false; // "lazy" is purely a property of reading fields - boolean binary = false; // Currently not possible - - StringBuilder flags = new StringBuilder(); - flags.append((f != null && f.indexed()) ? FieldFlag.INDEXED - .getAbbreviation() : '-'); - flags.append((t != null && t.isTokenized()) ? FieldFlag.TOKENIZED - .getAbbreviation() : '-'); - flags.append((f != null && f.stored()) ? FieldFlag.STORED - .getAbbreviation() : '-'); - flags.append((f != null && f.hasDocValues()) ? FieldFlag.DOC_VALUES - .getAbbreviation() : "-"); - flags.append((f != null && f.multiValued()) ? FieldFlag.MULTI_VALUED - .getAbbreviation() : '-'); - flags.append((f != null && f.storeTermVector()) ? FieldFlag.TERM_VECTOR_STORED - .getAbbreviation() : '-'); - flags.append((f != null && f.storeTermOffsets()) ? FieldFlag.TERM_VECTOR_OFFSET - .getAbbreviation() : '-'); - flags.append((f != null && f.storeTermPositions()) ? FieldFlag.TERM_VECTOR_POSITION - .getAbbreviation() : '-'); - flags.append((f != null && f.storeTermPayloads()) ? FieldFlag.TERM_VECTOR_PAYLOADS - .getAbbreviation() : '-'); - flags.append((f != null && f.omitNorms()) ? FieldFlag.OMIT_NORMS - .getAbbreviation() : '-'); - flags.append((f != null && f.omitTermFreqAndPositions()) ? FieldFlag.OMIT_TF - .getAbbreviation() : '-'); - flags.append((f != null && f.omitPositions()) ? FieldFlag.OMIT_POSITIONS - .getAbbreviation() : '-'); - flags.append((f != null && f.storeOffsetsWithPositions()) ? FieldFlag.STORE_OFFSETS_WITH_POSITIONS - .getAbbreviation() : '-'); - flags.append((lazy) ? FieldFlag.LAZY.getAbbreviation() : '-'); - flags.append((binary) ? FieldFlag.BINARY.getAbbreviation() : '-'); - flags.append((f != null && f.sortMissingFirst()) ? FieldFlag.SORT_MISSING_FIRST - .getAbbreviation() : '-'); - flags.append((f != null && f.sortMissingLast()) ? FieldFlag.SORT_MISSING_LAST - .getAbbreviation() : '-'); - return flags.toString(); - } - - /** - * @return a key to what each character means - */ - public static SimpleOrderedMap getFieldFlagsKey() { - SimpleOrderedMap key = new SimpleOrderedMap<>(); - for (FieldFlag f : FieldFlag.values()) { - key.add(String.valueOf(f.getAbbreviation()), f.getDisplay()); - } - return key; - } - - private static SimpleOrderedMap getDocumentFieldsInfo(Document doc, - int docId, IndexReader reader, IndexSchema schema) - throws IOException { - final CharsRefBuilder spare = new CharsRefBuilder(); - SimpleOrderedMap finfo = new SimpleOrderedMap<>(); - for (Object o : doc.getFields()) { - Field field = (Field) o; - SimpleOrderedMap f = new SimpleOrderedMap<>(); - - SchemaField sfield = schema.getFieldOrNull(field.name()); - FieldType ftype = (sfield == null) ? null : sfield.getType(); - - f.add("type", (ftype == null) ? null : ftype.getTypeName()); - f.add("schema", getFieldFlags(sfield)); - f.add("flags", getFieldFlags(field)); - - Term t = new Term(field.name(), - ftype != null ? ftype.storedToIndexed(field) - : field.stringValue()); - - f.add("value", (ftype == null) ? null : ftype.toExternal(field)); - - // TODO: this really should be "stored" - f.add("internal", field.stringValue()); // may be a binary number - - BytesRef bytes = field.binaryValue(); - if (bytes != null) { - f.add("binary", Base64.byteArrayToBase64(bytes.bytes, - bytes.offset, bytes.length)); - } - f.add("boost", field.boost()); - f.add("docFreq", t.text() == null ? 0 : reader.docFreq(t)); // this - // can - // be 0 - // for - // non-indexed - // fields - - // If we have a term vector, return that - if (field.fieldType().storeTermVectors()) { - try { - Terms v = reader.getTermVector(docId, field.name()); - if (v != null) { - SimpleOrderedMap tfv = new SimpleOrderedMap<>(); - final TermsEnum termsEnum = v.iterator(); - BytesRef text; - while ((text = termsEnum.next()) != null) { - final int freq = (int) termsEnum.totalTermFreq(); - spare.copyUTF8Bytes(text); - tfv.add(spare.toString(), freq); - } - f.add("termVector", tfv); - } - } catch (Exception ex) { - log.warn("error writing term vector", ex); - } - } - - finfo.add(field.name(), f); - } - return finfo; - } - - private static SimpleOrderedMap getIndexedFieldsInfo( - SolrQueryRequest req) throws Exception { - - SolrIndexSearcher searcher = req.getSearcher(); - SolrParams params = req.getParams(); - - Set fields = null; - String fl = params.get(CommonParams.FL); - if (fl != null) { - fields = new TreeSet<>(Arrays.asList(fl.split("[,\\s]+"))); - } - - LeafReader reader = searcher.getSlowAtomicReader(); - IndexSchema schema = searcher.getSchema(); - - // Don't be tempted to put this in the loop below, the whole point here - // is to alphabetize the fields! - Set fieldNames = new TreeSet<>(); - for (FieldInfo fieldInfo : reader.getFieldInfos()) { - fieldNames.add(fieldInfo.name); - } - - // Walk the term enum and keep a priority queue for each map in our set - SimpleOrderedMap vInfo = new SimpleOrderedMap<>(); - SimpleOrderedMap aInfo = new SimpleOrderedMap<>(); - - for (String fieldName : fieldNames) { - if (fields != null && !fields.contains(fieldName) - && !fields.contains("*")) { - continue; // we're not interested in this field Still an issue - // here - } - - SimpleOrderedMap fieldMap = new SimpleOrderedMap<>(); - - SchemaField sfield = schema.getFieldOrNull(fieldName); - FieldType ftype = (sfield == null) ? null : sfield.getType(); - - fieldMap.add("type", (ftype == null) ? null : ftype.getTypeName()); - fieldMap.add("schema", getFieldFlags(sfield)); - if (sfield != null && schema.isDynamicField(sfield.getName()) - && schema.getDynamicPattern(sfield.getName()) != null) { - fieldMap.add("dynamicBase", - schema.getDynamicPattern(sfield.getName())); - } - Terms terms = reader.fields().terms(fieldName); - if (terms == null) { // Not indexed, so we need to report what we - // can (it made it through the fl param if - // specified) - vInfo.add(AlfrescoSolrDataModel.getInstance() - .getAlfrescoPropertyFromSchemaField(fieldName), - fieldMap); - aInfo.add(fieldName, fieldMap); - continue; - } - - if (sfield != null && sfield.indexed()) { - if (params.getBool(INCLUDE_INDEX_FIELD_FLAGS, true)) { - Document doc = getFirstLiveDoc(terms, reader); - - if (doc != null) { - // Found a document with this field - try { - IndexableField fld = doc.getField(fieldName); - if (fld != null) { - fieldMap.add("index", getFieldFlags(fld)); - } else { - // it is a non-stored field... - fieldMap.add("index", "(unstored field)"); - } - } catch (Exception ex) { - log.warn("error reading field: " + fieldName); - } - } - } - fieldMap.add("docs", terms.getDocCount()); - - } - if (fields != null - && (fields.contains(fieldName) || fields.contains("*"))) { - getDetailedFieldInfo(req, fieldName, fieldMap); - } - // Add the field - vInfo.add(fieldName, fieldMap); - aInfo.add(AlfrescoSolrDataModel.getInstance() - .getAlfrescoPropertyFromSchemaField(fieldName), fieldMap); - } - - SimpleOrderedMap finfo = new SimpleOrderedMap<>(); - finfo.addAll(vInfo); - // finfo.add("mimetype()", finfo.get("cm:content.mimetype")); - // finfo.add("contentSize()", finfo.get("cm:content.size")); - finfo.addAll(aInfo); - return finfo; - } - - // Just get a document with the term in it, the first one will do! - // Is there a better way to do this? Shouldn't actually be very costly - // to do it this way. - protected static Document getFirstLiveDoc(Terms terms, LeafReader reader) - throws IOException { - TermsEnum termsEnum = terms.iterator(); - if (termsEnum.next() == null) { // Ran off the end of the terms enum without finding any live docs with that field in them. - return null; - } - PostingsEnum postingsEnum = termsEnum.postings(null, PostingsEnum.NONE); - final Bits liveDocs = reader.getLiveDocs(); - if (postingsEnum.nextDoc() == DocIdSetIterator.NO_MORE_DOCS - || (liveDocs != null && liveDocs.get(postingsEnum.docID()))) { - return null; - } - return reader.document(postingsEnum.docID()); - } - - /** - * Return info from the index - */ - private static SimpleOrderedMap getSchemaInfo(IndexSchema schema) { - Map> typeusemap = new TreeMap<>(); - Map fields = new TreeMap<>(); - SchemaField uniqueField = schema.getUniqueKeyField(); - for (SchemaField f : schema.getFields().values()) { - populateFieldInfo(schema, typeusemap, fields, uniqueField, f); - } - - Map dynamicFields = new TreeMap<>(); - for (SchemaField f : schema.getDynamicFieldPrototypes()) { - populateFieldInfo(schema, typeusemap, dynamicFields, uniqueField, f); - } - SimpleOrderedMap types = new SimpleOrderedMap<>(); - Map sortedTypes = new TreeMap<>( - schema.getFieldTypes()); - for (FieldType ft : sortedTypes.values()) { - SimpleOrderedMap field = new SimpleOrderedMap<>(); - field.add("fields", typeusemap.get(ft.getTypeName())); - field.add("tokenized", ft.isTokenized()); - field.add("className", ft.getClass().getName()); - field.add("indexAnalyzer", getAnalyzerInfo(ft.getIndexAnalyzer())); - field.add("queryAnalyzer", getAnalyzerInfo(ft.getQueryAnalyzer())); - field.add("similarity", getSimilarityInfo(ft.getSimilarity())); - types.add(ft.getTypeName(), field); - } - - // Must go through this to maintain binary compatbility. Putting a - // TreeMap into a resp leads to casting errors - SimpleOrderedMap finfo = new SimpleOrderedMap<>(); - - SimpleOrderedMap fieldsSimple = new SimpleOrderedMap<>(); - for (Map.Entry ent : fields.entrySet()) { - fieldsSimple.add(ent.getKey(), ent.getValue()); - } - finfo.add("fields", fieldsSimple); - - SimpleOrderedMap dynamicSimple = new SimpleOrderedMap<>(); - for (Map.Entry ent : dynamicFields.entrySet()) { - dynamicSimple.add(ent.getKey(), ent.getValue()); - } - finfo.add("dynamicFields", dynamicSimple); - - finfo.add("uniqueKeyField", - null == uniqueField ? null : uniqueField.getName()); - finfo.add("defaultSearchField", schema.getDefaultSearchFieldName()); - finfo.add("types", types); - return finfo; - } - - private static SimpleOrderedMap getSimilarityInfo( - Similarity similarity) { - SimpleOrderedMap toReturn = new SimpleOrderedMap<>(); - if (similarity != null) { - toReturn.add("className", similarity.getClass().getName()); - toReturn.add("details", similarity.toString()); - } - return toReturn; - } - - private static SimpleOrderedMap getAnalyzerInfo(Analyzer analyzer) { - SimpleOrderedMap aninfo = new SimpleOrderedMap<>(); - aninfo.add("className", analyzer.getClass().getName()); - if (analyzer instanceof TokenizerChain) { - - TokenizerChain tchain = (TokenizerChain) analyzer; - - CharFilterFactory[] cfiltfacs = tchain.getCharFilterFactories(); - if (0 < cfiltfacs.length) { - SimpleOrderedMap> cfilters = new SimpleOrderedMap<>(); - for (CharFilterFactory cfiltfac : cfiltfacs) { - Map tok = new HashMap<>(); - String className = cfiltfac.getClass().getName(); - tok.put("className", className); - tok.put("args", cfiltfac.getOriginalArgs()); - cfilters.add( - className.substring(className.lastIndexOf('.') + 1), - tok); - } - aninfo.add("charFilters", cfilters); - } - - SimpleOrderedMap tokenizer = new SimpleOrderedMap<>(); - TokenizerFactory tfac = tchain.getTokenizerFactory(); - tokenizer.add("className", tfac.getClass().getName()); - tokenizer.add("args", tfac.getOriginalArgs()); - aninfo.add("tokenizer", tokenizer); - - TokenFilterFactory[] filtfacs = tchain.getTokenFilterFactories(); - if (0 < filtfacs.length) { - SimpleOrderedMap> filters = new SimpleOrderedMap<>(); - for (TokenFilterFactory filtfac : filtfacs) { - Map tok = new HashMap<>(); - String className = filtfac.getClass().getName(); - tok.put("className", className); - tok.put("args", filtfac.getOriginalArgs()); - filters.add( - className.substring(className.lastIndexOf('.') + 1), - tok); - } - aninfo.add("filters", filters); - } - } - return aninfo; - } - - private static void populateFieldInfo(IndexSchema schema, - Map> typeusemap, Map fields, - SchemaField uniqueField, SchemaField f) { - FieldType ft = f.getType(); - SimpleOrderedMap field = new SimpleOrderedMap<>(); - field.add("type", ft.getTypeName()); - field.add("flags", getFieldFlags(f)); - if (f.isRequired()) { - field.add("required", f.isRequired()); - } - if (f.getDefaultValue() != null) { - field.add("default", f.getDefaultValue()); - } - if (f == uniqueField) { - field.add("uniqueKey", true); - } - if (ft.getIndexAnalyzer().getPositionIncrementGap(f.getName()) != 0) { - field.add("positionIncrementGap", ft.getIndexAnalyzer() - .getPositionIncrementGap(f.getName())); - } - field.add("copyDests", - toListOfStringDests(schema.getCopyFieldsList(f.getName()))); - field.add("copySources", schema.getCopySources(f.getName())); - - fields.put(f.getName(), field); - - List v = typeusemap.get(ft.getTypeName()); - if (v == null) { - v = new ArrayList<>(); - } - v.add(f.getName()); - typeusemap.put(ft.getTypeName(), v); - } - - /** - * @deprecated use {@link #getIndexInfo(DirectoryReader)} since you now have - * to explicitly pass the "fl" prameter and this was always - * called with "false" anyway from CoreAdminHandler - */ - public static SimpleOrderedMap getIndexInfo(DirectoryReader reader, - boolean detail) throws IOException { - return getIndexInfo(reader); - } - - // This method just gets the top-most level of information. This was - // conflated with getting detailed info - // for *all* the fields, called from CoreAdminHandler etc. - - public static SimpleOrderedMap getIndexInfo(DirectoryReader reader) - throws IOException { - Directory dir = reader.directory(); - SimpleOrderedMap indexInfo = new SimpleOrderedMap<>(); - - indexInfo.add("numDocs", reader.numDocs()); - indexInfo.add("maxDoc", reader.maxDoc()); - indexInfo.add("deletedDocs", reader.maxDoc() - reader.numDocs()); - indexInfo.add("indexHeapUsageBytes", getIndexHeapUsed(reader)); - - indexInfo.add("version", reader.getVersion()); // TODO? Is this - // different then: - // IndexReader.getCurrentVersion( - // dir )? - indexInfo.add("segmentCount", reader.leaves().size()); - indexInfo.add("current", reader.isCurrent()); - indexInfo.add("hasDeletions", reader.hasDeletions()); - indexInfo.add("directory", dir); - indexInfo.add("userData", reader.getIndexCommit().getUserData()); - String s = reader.getIndexCommit().getUserData() - .get(SolrIndexWriter.COMMIT_TIME_MSEC_KEY); - if (s != null) { - indexInfo.add("lastModified", new Date(Long.parseLong(s))); - } - return indexInfo; - } - - /** Returns the sum of RAM bytes used by each segment */ - private static long getIndexHeapUsed(DirectoryReader reader) { - long indexHeapRamBytesUsed = 0; - for (LeafReaderContext leafReaderContext : reader.leaves()) { - LeafReader leafReader = leafReaderContext.reader(); - if (leafReader instanceof SegmentReader) { - indexHeapRamBytesUsed += ((SegmentReader) leafReader) - .ramBytesUsed(); - } else { - // Not supported for any reader that is not a SegmentReader - return -1; - } - } - return indexHeapRamBytesUsed; - } - - // Get terribly detailed information about a particular field. This is a - // very expensive call, use it with caution - // especially on large indexes! - @SuppressWarnings("unchecked") - private static void getDetailedFieldInfo(SolrQueryRequest req, - String field, SimpleOrderedMap fieldMap) throws IOException { - - SolrParams params = req.getParams(); - final int numTerms = params.getInt(NUMTERMS, DEFAULT_COUNT); - - TopTermQueue tiq = new TopTermQueue(numTerms + 1); // Something to - // collect the top N - // terms in. - - final CharsRefBuilder spare = new CharsRefBuilder(); - - Terms terms = MultiFields.getTerms(req.getSearcher().getIndexReader(), - field); - if (terms == null) { // field does not exist - return; - } - TermsEnum termsEnum = terms.iterator(); - BytesRef text; - int[] buckets = new int[HIST_ARRAY_SIZE]; - while ((text = termsEnum.next()) != null) { - ++tiq.distinctTerms; - int freq = termsEnum.docFreq(); // This calculation seems odd, but - // it gives the same results as it - // used to. - int slot = 32 - Integer.numberOfLeadingZeros(Math.max(0, freq - 1)); - buckets[slot] = buckets[slot] + 1; - if (numTerms > 0 && freq > tiq.minFreq) { - spare.copyUTF8Bytes(text); - String t = spare.toString(); - - tiq.add(new TopTermQueue.TermInfo(new Term(field, t), termsEnum - .docFreq())); - if (tiq.size() > numTerms) { // if tiq full - tiq.pop(); // remove lowest in tiq - tiq.minFreq = tiq.getTopTermInfo().docFreq; - } - } - } - tiq.histogram.add(buckets); - fieldMap.add("distinct", tiq.distinctTerms); - - // Include top terms - fieldMap.add("topTerms", tiq.toNamedList(req.getSearcher().getSchema())); - - // Add a histogram - fieldMap.add("histogram", tiq.histogram.toNamedList()); - } - - private static List toListOfStrings(SchemaField[] raw) { - List result = new ArrayList<>(raw.length); - for (SchemaField f : raw) { - result.add(f.getName()); - } - return result; - } - - private static List toListOfStringDests(List raw) { - List result = new ArrayList<>(raw.size()); - for (CopyField f : raw) { - result.add(f.getDestination().getName()); - } - return result; - } - - // ////////////////////// SolrInfoMBeans methods ////////////////////// - - @Override - public String getDescription() { - return "Lucene Index Browser. Inspired and modeled after Luke: http://www.getopt.org/luke/"; - } - - @Override - public URL[] getDocs() { - try { - return new URL[] { new URL( - "http://wiki.apache.org/solr/LukeRequestHandler") }; - } catch (MalformedURLException ex) { - return null; - } - } - - // ///////////////////////////////////////////////////////////////////////////////////// - - static class TermHistogram { - int _maxBucket = -1; - int _buckets[] = new int[HIST_ARRAY_SIZE]; - - public void add(int[] buckets) { - for (int idx = 0; idx < buckets.length; ++idx) { - if (buckets[idx] != 0) - _maxBucket = idx; - } - for (int idx = 0; idx <= _maxBucket; ++idx) { - _buckets[idx] = buckets[idx]; - } - } - - // TODO? should this be a list or a map? - public NamedList toNamedList() { - NamedList nl = new NamedList<>(); - for (int bucket = 0; bucket <= _maxBucket; bucket++) { - nl.add("" + (1 << bucket), _buckets[bucket]); - } - return nl; - } - } - - /** - * Private internal class that counts up frequent terms - */ - private static class TopTermQueue extends PriorityQueue { - static class TermInfo { - TermInfo(Term t, int df) { - term = t; - docFreq = df; - } - - int docFreq; - Term term; - } - - public int minFreq = 0; - public int distinctTerms = 0; - public TermHistogram histogram; - - TopTermQueue(int size) { - super(size); - histogram = new TermHistogram(); - } - - @Override - protected final boolean lessThan(Object a, Object b) { - TermInfo termInfoA = (TermInfo) a; - TermInfo termInfoB = (TermInfo) b; - return termInfoA.docFreq < termInfoB.docFreq; - } - - /** - * This is a destructive call... the queue is empty at the end - */ - public NamedList toNamedList(IndexSchema schema) { - // reverse the list.. - List aslist = new LinkedList<>(); - while (size() > 0) { - aslist.add(0, (TermInfo) pop()); - } - - NamedList list = new NamedList<>(); - for (TermInfo i : aslist) { - String txt = i.term.text(); - SchemaField ft = schema.getFieldOrNull(i.term.field()); - if (ft != null) { - txt = ft.getType().indexedToReadable(txt); - } - list.add(txt, i.docFreq); - } - return list; - } - - public TermInfo getTopTermInfo() { - return (TermInfo) top(); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; + +import static org.apache.lucene.index.IndexOptions.DOCS; +import static org.apache.lucene.index.IndexOptions.DOCS_AND_FREQS; +import static org.apache.lucene.index.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.util.CharFilterFactory; +import org.apache.lucene.analysis.util.TokenFilterFactory; +import org.apache.lucene.analysis.util.TokenizerFactory; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.DocValuesType; +import org.apache.lucene.index.FieldInfo; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.MultiFields; +import org.apache.lucene.index.PostingsEnum; +import org.apache.lucene.index.SegmentReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.index.Terms; +import org.apache.lucene.index.TermsEnum; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.similarities.Similarity; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.CharsRefBuilder; +import org.apache.lucene.util.PriorityQueue; +import org.apache.solr.analysis.TokenizerChain; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrException.ErrorCode; +import org.apache.solr.common.luke.FieldFlag; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.Base64; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.handler.RequestHandlerBase; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.schema.CopyField; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.update.SolrIndexWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class AlfrescoLukeRequestHandler extends RequestHandlerBase { + private static final Logger log = LoggerFactory.getLogger(MethodHandles + .lookup().lookupClass()); + + public static final String NUMTERMS = "numTerms"; + public static final String INCLUDE_INDEX_FIELD_FLAGS = "includeIndexFieldFlags"; + public static final String DOC_ID = "docId"; + public static final String ID = "id"; + public static final int DEFAULT_COUNT = 10; + + static final int HIST_ARRAY_SIZE = 33; + + private static enum ShowStyle { + ALL, DOC, SCHEMA, INDEX; + + public static ShowStyle get(String v) { + if (v == null) + return null; + if ("schema".equalsIgnoreCase(v)) + return SCHEMA; + if ("index".equalsIgnoreCase(v)) + return INDEX; + if ("doc".equalsIgnoreCase(v)) + return DOC; + if ("all".equalsIgnoreCase(v)) + return ALL; + throw new SolrException(ErrorCode.BAD_REQUEST, + "Unknown Show Style: " + v); + } + }; + + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) + throws Exception { + IndexSchema schema = req.getSchema(); + SolrIndexSearcher searcher = req.getSearcher(); + DirectoryReader reader = searcher.getIndexReader(); + SolrParams params = req.getParams(); + ShowStyle style = ShowStyle.get(params.get("show")); + + // If no doc is given, show all fields and top terms + + rsp.add("index", getIndexInfo(reader)); + + if (ShowStyle.INDEX == style) { + return; // that's all we need + } + + Integer docId = params.getInt(DOC_ID); + if (docId == null && params.get(ID) != null) { + // Look for something with a given solr ID + SchemaField uniqueKey = schema.getUniqueKeyField(); + String v = uniqueKey.getType().toInternal(params.get(ID)); + Term t = new Term(uniqueKey.getName(), v); + docId = searcher.getFirstMatch(t); + if (docId < 0) { + throw new SolrException(SolrException.ErrorCode.NOT_FOUND, + "Can't find document: " + params.get(ID)); + } + } + + // Read the document from the index + if (docId != null) { + if (style != null && style != ShowStyle.DOC) { + throw new SolrException(ErrorCode.BAD_REQUEST, + "missing doc param for doc style"); + } + Document doc = null; + try { + doc = reader.document(docId); + } catch (Exception ex) { + } + if (doc == null) { + throw new SolrException(SolrException.ErrorCode.NOT_FOUND, + "Can't find document: " + docId); + } + + SimpleOrderedMap info = getDocumentFieldsInfo(doc, docId, + reader, schema); + + SimpleOrderedMap docinfo = new SimpleOrderedMap<>(); + docinfo.add("docId", docId); + docinfo.add("lucene", info); + docinfo.add("solr", doc); + rsp.add("doc", docinfo); + } else if (ShowStyle.SCHEMA == style) { + rsp.add("schema", getSchemaInfo(req.getSchema())); + } else { + rsp.add("fields", getIndexedFieldsInfo(req)); + } + + // Add some generally helpful information + NamedList info = new SimpleOrderedMap<>(); + info.add("key", getFieldFlagsKey()); + info.add( + "NOTE", + "Document Frequency (df) is not updated when a document is marked for deletion. df values include deleted documents."); + rsp.add("info", info); + rsp.setHttpCaching(false); + } + + /** + * @return a string representing a IndexableField's flags. + */ + private static String getFieldFlags(IndexableField f) { + IndexOptions opts = (f == null) ? null : f.fieldType().indexOptions(); + + StringBuilder flags = new StringBuilder(); + + flags.append((f != null && f.fieldType().indexOptions() != IndexOptions.NONE) ? FieldFlag.INDEXED + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().tokenized()) ? FieldFlag.TOKENIZED + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().stored()) ? FieldFlag.STORED + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().docValuesType() != DocValuesType.NONE) ? FieldFlag.DOC_VALUES + .getAbbreviation() : "-"); + flags.append((false) ? FieldFlag.MULTI_VALUED.getAbbreviation() : '-'); // SchemaField + // Specific + flags.append((f != null && f.fieldType().storeTermVectors()) ? FieldFlag.TERM_VECTOR_STORED + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().storeTermVectorOffsets()) ? FieldFlag.TERM_VECTOR_OFFSET + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().storeTermVectorPositions()) ? FieldFlag.TERM_VECTOR_POSITION + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().storeTermVectorPayloads()) ? FieldFlag.TERM_VECTOR_PAYLOADS + .getAbbreviation() : '-'); + flags.append((f != null && f.fieldType().omitNorms()) ? FieldFlag.OMIT_NORMS + .getAbbreviation() : '-'); + + flags.append((f != null && DOCS == opts) ? FieldFlag.OMIT_TF + .getAbbreviation() : '-'); + + flags.append((f != null && DOCS_AND_FREQS == opts) ? FieldFlag.OMIT_POSITIONS + .getAbbreviation() : '-'); + + flags.append((f != null && DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS == opts) ? FieldFlag.STORE_OFFSETS_WITH_POSITIONS + .getAbbreviation() : '-'); + + flags.append((f != null && f.getClass().getSimpleName() + .equals("LazyField")) ? FieldFlag.LAZY.getAbbreviation() : '-'); + flags.append((f != null && f.binaryValue() != null) ? FieldFlag.BINARY + .getAbbreviation() : '-'); + flags.append((false) ? FieldFlag.SORT_MISSING_FIRST.getAbbreviation() + : '-'); // SchemaField Specific + flags.append((false) ? FieldFlag.SORT_MISSING_LAST.getAbbreviation() + : '-'); // SchemaField Specific + return flags.toString(); + } + + /** + * @return a string representing a SchemaField's flags. + */ + private static String getFieldFlags(SchemaField f) { + FieldType t = (f == null) ? null : f.getType(); + + // see: + // http://www.nabble.com/schema-field-properties-tf3437753.html#a9585549 + boolean lazy = false; // "lazy" is purely a property of reading fields + boolean binary = false; // Currently not possible + + StringBuilder flags = new StringBuilder(); + flags.append((f != null && f.indexed()) ? FieldFlag.INDEXED + .getAbbreviation() : '-'); + flags.append((t != null && t.isTokenized()) ? FieldFlag.TOKENIZED + .getAbbreviation() : '-'); + flags.append((f != null && f.stored()) ? FieldFlag.STORED + .getAbbreviation() : '-'); + flags.append((f != null && f.hasDocValues()) ? FieldFlag.DOC_VALUES + .getAbbreviation() : "-"); + flags.append((f != null && f.multiValued()) ? FieldFlag.MULTI_VALUED + .getAbbreviation() : '-'); + flags.append((f != null && f.storeTermVector()) ? FieldFlag.TERM_VECTOR_STORED + .getAbbreviation() : '-'); + flags.append((f != null && f.storeTermOffsets()) ? FieldFlag.TERM_VECTOR_OFFSET + .getAbbreviation() : '-'); + flags.append((f != null && f.storeTermPositions()) ? FieldFlag.TERM_VECTOR_POSITION + .getAbbreviation() : '-'); + flags.append((f != null && f.storeTermPayloads()) ? FieldFlag.TERM_VECTOR_PAYLOADS + .getAbbreviation() : '-'); + flags.append((f != null && f.omitNorms()) ? FieldFlag.OMIT_NORMS + .getAbbreviation() : '-'); + flags.append((f != null && f.omitTermFreqAndPositions()) ? FieldFlag.OMIT_TF + .getAbbreviation() : '-'); + flags.append((f != null && f.omitPositions()) ? FieldFlag.OMIT_POSITIONS + .getAbbreviation() : '-'); + flags.append((f != null && f.storeOffsetsWithPositions()) ? FieldFlag.STORE_OFFSETS_WITH_POSITIONS + .getAbbreviation() : '-'); + flags.append((lazy) ? FieldFlag.LAZY.getAbbreviation() : '-'); + flags.append((binary) ? FieldFlag.BINARY.getAbbreviation() : '-'); + flags.append((f != null && f.sortMissingFirst()) ? FieldFlag.SORT_MISSING_FIRST + .getAbbreviation() : '-'); + flags.append((f != null && f.sortMissingLast()) ? FieldFlag.SORT_MISSING_LAST + .getAbbreviation() : '-'); + return flags.toString(); + } + + /** + * @return a key to what each character means + */ + public static SimpleOrderedMap getFieldFlagsKey() { + SimpleOrderedMap key = new SimpleOrderedMap<>(); + for (FieldFlag f : FieldFlag.values()) { + key.add(String.valueOf(f.getAbbreviation()), f.getDisplay()); + } + return key; + } + + private static SimpleOrderedMap getDocumentFieldsInfo(Document doc, + int docId, IndexReader reader, IndexSchema schema) + throws IOException { + final CharsRefBuilder spare = new CharsRefBuilder(); + SimpleOrderedMap finfo = new SimpleOrderedMap<>(); + for (Object o : doc.getFields()) { + Field field = (Field) o; + SimpleOrderedMap f = new SimpleOrderedMap<>(); + + SchemaField sfield = schema.getFieldOrNull(field.name()); + FieldType ftype = (sfield == null) ? null : sfield.getType(); + + f.add("type", (ftype == null) ? null : ftype.getTypeName()); + f.add("schema", getFieldFlags(sfield)); + f.add("flags", getFieldFlags(field)); + + Term t = new Term(field.name(), + ftype != null ? ftype.storedToIndexed(field) + : field.stringValue()); + + f.add("value", (ftype == null) ? null : ftype.toExternal(field)); + + // TODO: this really should be "stored" + f.add("internal", field.stringValue()); // may be a binary number + + BytesRef bytes = field.binaryValue(); + if (bytes != null) { + f.add("binary", Base64.byteArrayToBase64(bytes.bytes, + bytes.offset, bytes.length)); + } + f.add("boost", field.boost()); + f.add("docFreq", t.text() == null ? 0 : reader.docFreq(t)); // this + // can + // be 0 + // for + // non-indexed + // fields + + // If we have a term vector, return that + if (field.fieldType().storeTermVectors()) { + try { + Terms v = reader.getTermVector(docId, field.name()); + if (v != null) { + SimpleOrderedMap tfv = new SimpleOrderedMap<>(); + final TermsEnum termsEnum = v.iterator(); + BytesRef text; + while ((text = termsEnum.next()) != null) { + final int freq = (int) termsEnum.totalTermFreq(); + spare.copyUTF8Bytes(text); + tfv.add(spare.toString(), freq); + } + f.add("termVector", tfv); + } + } catch (Exception ex) { + log.warn("error writing term vector", ex); + } + } + + finfo.add(field.name(), f); + } + return finfo; + } + + private static SimpleOrderedMap getIndexedFieldsInfo( + SolrQueryRequest req) throws Exception { + + SolrIndexSearcher searcher = req.getSearcher(); + SolrParams params = req.getParams(); + + Set fields = null; + String fl = params.get(CommonParams.FL); + if (fl != null) { + fields = new TreeSet<>(Arrays.asList(fl.split("[,\\s]+"))); + } + + LeafReader reader = searcher.getSlowAtomicReader(); + IndexSchema schema = searcher.getSchema(); + + // Don't be tempted to put this in the loop below, the whole point here + // is to alphabetize the fields! + Set fieldNames = new TreeSet<>(); + for (FieldInfo fieldInfo : reader.getFieldInfos()) { + fieldNames.add(fieldInfo.name); + } + + // Walk the term enum and keep a priority queue for each map in our set + SimpleOrderedMap vInfo = new SimpleOrderedMap<>(); + SimpleOrderedMap aInfo = new SimpleOrderedMap<>(); + + for (String fieldName : fieldNames) { + if (fields != null && !fields.contains(fieldName) + && !fields.contains("*")) { + continue; // we're not interested in this field Still an issue + // here + } + + SimpleOrderedMap fieldMap = new SimpleOrderedMap<>(); + + SchemaField sfield = schema.getFieldOrNull(fieldName); + FieldType ftype = (sfield == null) ? null : sfield.getType(); + + fieldMap.add("type", (ftype == null) ? null : ftype.getTypeName()); + fieldMap.add("schema", getFieldFlags(sfield)); + if (sfield != null && schema.isDynamicField(sfield.getName()) + && schema.getDynamicPattern(sfield.getName()) != null) { + fieldMap.add("dynamicBase", + schema.getDynamicPattern(sfield.getName())); + } + Terms terms = reader.fields().terms(fieldName); + if (terms == null) { // Not indexed, so we need to report what we + // can (it made it through the fl param if + // specified) + vInfo.add(AlfrescoSolrDataModel.getInstance() + .getAlfrescoPropertyFromSchemaField(fieldName), + fieldMap); + aInfo.add(fieldName, fieldMap); + continue; + } + + if (sfield != null && sfield.indexed()) { + if (params.getBool(INCLUDE_INDEX_FIELD_FLAGS, true)) { + Document doc = getFirstLiveDoc(terms, reader); + + if (doc != null) { + // Found a document with this field + try { + IndexableField fld = doc.getField(fieldName); + if (fld != null) { + fieldMap.add("index", getFieldFlags(fld)); + } else { + // it is a non-stored field... + fieldMap.add("index", "(unstored field)"); + } + } catch (Exception ex) { + log.warn("error reading field: " + fieldName); + } + } + } + fieldMap.add("docs", terms.getDocCount()); + + } + if (fields != null + && (fields.contains(fieldName) || fields.contains("*"))) { + getDetailedFieldInfo(req, fieldName, fieldMap); + } + // Add the field + vInfo.add(fieldName, fieldMap); + aInfo.add(AlfrescoSolrDataModel.getInstance() + .getAlfrescoPropertyFromSchemaField(fieldName), fieldMap); + } + + SimpleOrderedMap finfo = new SimpleOrderedMap<>(); + finfo.addAll(vInfo); + // finfo.add("mimetype()", finfo.get("cm:content.mimetype")); + // finfo.add("contentSize()", finfo.get("cm:content.size")); + finfo.addAll(aInfo); + return finfo; + } + + // Just get a document with the term in it, the first one will do! + // Is there a better way to do this? Shouldn't actually be very costly + // to do it this way. + protected static Document getFirstLiveDoc(Terms terms, LeafReader reader) + throws IOException { + TermsEnum termsEnum = terms.iterator(); + if (termsEnum.next() == null) { // Ran off the end of the terms enum without finding any live docs with that field in them. + return null; + } + PostingsEnum postingsEnum = termsEnum.postings(null, PostingsEnum.NONE); + final Bits liveDocs = reader.getLiveDocs(); + if (postingsEnum.nextDoc() == DocIdSetIterator.NO_MORE_DOCS + || (liveDocs != null && liveDocs.get(postingsEnum.docID()))) { + return null; + } + return reader.document(postingsEnum.docID()); + } + + /** + * Return info from the index + */ + private static SimpleOrderedMap getSchemaInfo(IndexSchema schema) { + Map> typeusemap = new TreeMap<>(); + Map fields = new TreeMap<>(); + SchemaField uniqueField = schema.getUniqueKeyField(); + for (SchemaField f : schema.getFields().values()) { + populateFieldInfo(schema, typeusemap, fields, uniqueField, f); + } + + Map dynamicFields = new TreeMap<>(); + for (SchemaField f : schema.getDynamicFieldPrototypes()) { + populateFieldInfo(schema, typeusemap, dynamicFields, uniqueField, f); + } + SimpleOrderedMap types = new SimpleOrderedMap<>(); + Map sortedTypes = new TreeMap<>( + schema.getFieldTypes()); + for (FieldType ft : sortedTypes.values()) { + SimpleOrderedMap field = new SimpleOrderedMap<>(); + field.add("fields", typeusemap.get(ft.getTypeName())); + field.add("tokenized", ft.isTokenized()); + field.add("className", ft.getClass().getName()); + field.add("indexAnalyzer", getAnalyzerInfo(ft.getIndexAnalyzer())); + field.add("queryAnalyzer", getAnalyzerInfo(ft.getQueryAnalyzer())); + field.add("similarity", getSimilarityInfo(ft.getSimilarity())); + types.add(ft.getTypeName(), field); + } + + // Must go through this to maintain binary compatbility. Putting a + // TreeMap into a resp leads to casting errors + SimpleOrderedMap finfo = new SimpleOrderedMap<>(); + + SimpleOrderedMap fieldsSimple = new SimpleOrderedMap<>(); + for (Map.Entry ent : fields.entrySet()) { + fieldsSimple.add(ent.getKey(), ent.getValue()); + } + finfo.add("fields", fieldsSimple); + + SimpleOrderedMap dynamicSimple = new SimpleOrderedMap<>(); + for (Map.Entry ent : dynamicFields.entrySet()) { + dynamicSimple.add(ent.getKey(), ent.getValue()); + } + finfo.add("dynamicFields", dynamicSimple); + + finfo.add("uniqueKeyField", + null == uniqueField ? null : uniqueField.getName()); + finfo.add("defaultSearchField", schema.getDefaultSearchFieldName()); + finfo.add("types", types); + return finfo; + } + + private static SimpleOrderedMap getSimilarityInfo( + Similarity similarity) { + SimpleOrderedMap toReturn = new SimpleOrderedMap<>(); + if (similarity != null) { + toReturn.add("className", similarity.getClass().getName()); + toReturn.add("details", similarity.toString()); + } + return toReturn; + } + + private static SimpleOrderedMap getAnalyzerInfo(Analyzer analyzer) { + SimpleOrderedMap aninfo = new SimpleOrderedMap<>(); + aninfo.add("className", analyzer.getClass().getName()); + if (analyzer instanceof TokenizerChain) { + + TokenizerChain tchain = (TokenizerChain) analyzer; + + CharFilterFactory[] cfiltfacs = tchain.getCharFilterFactories(); + if (0 < cfiltfacs.length) { + SimpleOrderedMap> cfilters = new SimpleOrderedMap<>(); + for (CharFilterFactory cfiltfac : cfiltfacs) { + Map tok = new HashMap<>(); + String className = cfiltfac.getClass().getName(); + tok.put("className", className); + tok.put("args", cfiltfac.getOriginalArgs()); + cfilters.add( + className.substring(className.lastIndexOf('.') + 1), + tok); + } + aninfo.add("charFilters", cfilters); + } + + SimpleOrderedMap tokenizer = new SimpleOrderedMap<>(); + TokenizerFactory tfac = tchain.getTokenizerFactory(); + tokenizer.add("className", tfac.getClass().getName()); + tokenizer.add("args", tfac.getOriginalArgs()); + aninfo.add("tokenizer", tokenizer); + + TokenFilterFactory[] filtfacs = tchain.getTokenFilterFactories(); + if (0 < filtfacs.length) { + SimpleOrderedMap> filters = new SimpleOrderedMap<>(); + for (TokenFilterFactory filtfac : filtfacs) { + Map tok = new HashMap<>(); + String className = filtfac.getClass().getName(); + tok.put("className", className); + tok.put("args", filtfac.getOriginalArgs()); + filters.add( + className.substring(className.lastIndexOf('.') + 1), + tok); + } + aninfo.add("filters", filters); + } + } + return aninfo; + } + + private static void populateFieldInfo(IndexSchema schema, + Map> typeusemap, Map fields, + SchemaField uniqueField, SchemaField f) { + FieldType ft = f.getType(); + SimpleOrderedMap field = new SimpleOrderedMap<>(); + field.add("type", ft.getTypeName()); + field.add("flags", getFieldFlags(f)); + if (f.isRequired()) { + field.add("required", f.isRequired()); + } + if (f.getDefaultValue() != null) { + field.add("default", f.getDefaultValue()); + } + if (f == uniqueField) { + field.add("uniqueKey", true); + } + if (ft.getIndexAnalyzer().getPositionIncrementGap(f.getName()) != 0) { + field.add("positionIncrementGap", ft.getIndexAnalyzer() + .getPositionIncrementGap(f.getName())); + } + field.add("copyDests", + toListOfStringDests(schema.getCopyFieldsList(f.getName()))); + field.add("copySources", schema.getCopySources(f.getName())); + + fields.put(f.getName(), field); + + List v = typeusemap.get(ft.getTypeName()); + if (v == null) { + v = new ArrayList<>(); + } + v.add(f.getName()); + typeusemap.put(ft.getTypeName(), v); + } + + /** + * @deprecated use {@link #getIndexInfo(DirectoryReader)} since you now have + * to explicitly pass the "fl" prameter and this was always + * called with "false" anyway from CoreAdminHandler + */ + public static SimpleOrderedMap getIndexInfo(DirectoryReader reader, + boolean detail) throws IOException { + return getIndexInfo(reader); + } + + // This method just gets the top-most level of information. This was + // conflated with getting detailed info + // for *all* the fields, called from CoreAdminHandler etc. + + public static SimpleOrderedMap getIndexInfo(DirectoryReader reader) + throws IOException { + Directory dir = reader.directory(); + SimpleOrderedMap indexInfo = new SimpleOrderedMap<>(); + + indexInfo.add("numDocs", reader.numDocs()); + indexInfo.add("maxDoc", reader.maxDoc()); + indexInfo.add("deletedDocs", reader.maxDoc() - reader.numDocs()); + indexInfo.add("indexHeapUsageBytes", getIndexHeapUsed(reader)); + + indexInfo.add("version", reader.getVersion()); // TODO? Is this + // different then: + // IndexReader.getCurrentVersion( + // dir )? + indexInfo.add("segmentCount", reader.leaves().size()); + indexInfo.add("current", reader.isCurrent()); + indexInfo.add("hasDeletions", reader.hasDeletions()); + indexInfo.add("directory", dir); + indexInfo.add("userData", reader.getIndexCommit().getUserData()); + String s = reader.getIndexCommit().getUserData() + .get(SolrIndexWriter.COMMIT_TIME_MSEC_KEY); + if (s != null) { + indexInfo.add("lastModified", new Date(Long.parseLong(s))); + } + return indexInfo; + } + + /** Returns the sum of RAM bytes used by each segment */ + private static long getIndexHeapUsed(DirectoryReader reader) { + long indexHeapRamBytesUsed = 0; + for (LeafReaderContext leafReaderContext : reader.leaves()) { + LeafReader leafReader = leafReaderContext.reader(); + if (leafReader instanceof SegmentReader) { + indexHeapRamBytesUsed += ((SegmentReader) leafReader) + .ramBytesUsed(); + } else { + // Not supported for any reader that is not a SegmentReader + return -1; + } + } + return indexHeapRamBytesUsed; + } + + // Get terribly detailed information about a particular field. This is a + // very expensive call, use it with caution + // especially on large indexes! + @SuppressWarnings("unchecked") + private static void getDetailedFieldInfo(SolrQueryRequest req, + String field, SimpleOrderedMap fieldMap) throws IOException { + + SolrParams params = req.getParams(); + final int numTerms = params.getInt(NUMTERMS, DEFAULT_COUNT); + + TopTermQueue tiq = new TopTermQueue(numTerms + 1); // Something to + // collect the top N + // terms in. + + final CharsRefBuilder spare = new CharsRefBuilder(); + + Terms terms = MultiFields.getTerms(req.getSearcher().getIndexReader(), + field); + if (terms == null) { // field does not exist + return; + } + TermsEnum termsEnum = terms.iterator(); + BytesRef text; + int[] buckets = new int[HIST_ARRAY_SIZE]; + while ((text = termsEnum.next()) != null) { + ++tiq.distinctTerms; + int freq = termsEnum.docFreq(); // This calculation seems odd, but + // it gives the same results as it + // used to. + int slot = 32 - Integer.numberOfLeadingZeros(Math.max(0, freq - 1)); + buckets[slot] = buckets[slot] + 1; + if (numTerms > 0 && freq > tiq.minFreq) { + spare.copyUTF8Bytes(text); + String t = spare.toString(); + + tiq.add(new TopTermQueue.TermInfo(new Term(field, t), termsEnum + .docFreq())); + if (tiq.size() > numTerms) { // if tiq full + tiq.pop(); // remove lowest in tiq + tiq.minFreq = tiq.getTopTermInfo().docFreq; + } + } + } + tiq.histogram.add(buckets); + fieldMap.add("distinct", tiq.distinctTerms); + + // Include top terms + fieldMap.add("topTerms", tiq.toNamedList(req.getSearcher().getSchema())); + + // Add a histogram + fieldMap.add("histogram", tiq.histogram.toNamedList()); + } + + private static List toListOfStrings(SchemaField[] raw) { + List result = new ArrayList<>(raw.length); + for (SchemaField f : raw) { + result.add(f.getName()); + } + return result; + } + + private static List toListOfStringDests(List raw) { + List result = new ArrayList<>(raw.size()); + for (CopyField f : raw) { + result.add(f.getDestination().getName()); + } + return result; + } + + // ////////////////////// SolrInfoMBeans methods ////////////////////// + + @Override + public String getDescription() { + return "Lucene Index Browser. Inspired and modeled after Luke: http://www.getopt.org/luke/"; + } + + @Override + public URL[] getDocs() { + try { + return new URL[] { new URL( + "http://wiki.apache.org/solr/LukeRequestHandler") }; + } catch (MalformedURLException ex) { + return null; + } + } + + // ///////////////////////////////////////////////////////////////////////////////////// + + static class TermHistogram { + int _maxBucket = -1; + int _buckets[] = new int[HIST_ARRAY_SIZE]; + + public void add(int[] buckets) { + for (int idx = 0; idx < buckets.length; ++idx) { + if (buckets[idx] != 0) + _maxBucket = idx; + } + for (int idx = 0; idx <= _maxBucket; ++idx) { + _buckets[idx] = buckets[idx]; + } + } + + // TODO? should this be a list or a map? + public NamedList toNamedList() { + NamedList nl = new NamedList<>(); + for (int bucket = 0; bucket <= _maxBucket; bucket++) { + nl.add("" + (1 << bucket), _buckets[bucket]); + } + return nl; + } + } + + /** + * Private internal class that counts up frequent terms + */ + private static class TopTermQueue extends PriorityQueue { + static class TermInfo { + TermInfo(Term t, int df) { + term = t; + docFreq = df; + } + + int docFreq; + Term term; + } + + public int minFreq = 0; + public int distinctTerms = 0; + public TermHistogram histogram; + + TopTermQueue(int size) { + super(size); + histogram = new TermHistogram(); + } + + @Override + protected final boolean lessThan(Object a, Object b) { + TermInfo termInfoA = (TermInfo) a; + TermInfo termInfoB = (TermInfo) b; + return termInfoA.docFreq < termInfoB.docFreq; + } + + /** + * This is a destructive call... the queue is empty at the end + */ + public NamedList toNamedList(IndexSchema schema) { + // reverse the list.. + List aslist = new LinkedList<>(); + while (size() > 0) { + aslist.add(0, (TermInfo) pop()); + } + + NamedList list = new NamedList<>(); + for (TermInfo i : aslist) { + String txt = i.term.text(); + SchemaField ft = schema.getFieldOrNull(i.term.field()); + if (ft != null) { + txt = ft.getType().indexedToReadable(txt); + } + list.add(txt, i.docFreq); + } + return list; + } + + public TermInfo getTopTermInfo() { + return (TermInfo) top(); + } + } +} diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoQueryRequest.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoQueryRequest.java index c9f5c6006..7bd150380 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoQueryRequest.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoQueryRequest.java @@ -1,77 +1,77 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; - -import java.util.Collection; -import java.util.Collections; - -import org.apache.solr.client.solrj.request.QueryRequest; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.ContentStream; - -/** - * - * - * @since solr 1.3 - */ -public class AlfrescoQueryRequest extends QueryRequest -{ - private ContentStream contentStream = null; - - public AlfrescoQueryRequest() - { - super(); - } - - public AlfrescoQueryRequest( SolrParams q ) - { - super(q); - } - - public AlfrescoQueryRequest( SolrParams q, METHOD method ) - { - super(q, method); - } - - - //--------------------------------------------------------------------------------- - //--------------------------------------------------------------------------------- - - @Override - public Collection getContentStreams() { - return contentStream == null ? null : Collections.singletonList(contentStream); - } - - /** - * @param contentStream the contentStream to set - */ -public void setContentStream(ContentStream contentStream) -{ - this.contentStream = contentStream; -} -} - +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; + +import java.util.Collection; +import java.util.Collections; + +import org.apache.solr.client.solrj.request.QueryRequest; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.ContentStream; + +/** + * + * + * @since solr 1.3 + */ +public class AlfrescoQueryRequest extends QueryRequest +{ + private ContentStream contentStream = null; + + public AlfrescoQueryRequest() + { + super(); + } + + public AlfrescoQueryRequest( SolrParams q ) + { + super(q); + } + + public AlfrescoQueryRequest( SolrParams q, METHOD method ) + { + super(q, method); + } + + + //--------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------- + + @Override + public Collection getContentStreams() { + return contentStream == null ? null : Collections.singletonList(contentStream); + } + + /** + * @param contentStream the contentStream to set + */ +public void setContentStream(ContentStream contentStream) +{ + this.contentStream = contentStream; +} +} + diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSearchHandler.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSearchHandler.java index 6ed3d58d8..62f0341f7 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSearchHandler.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSearchHandler.java @@ -1,715 +1,715 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; - -import static java.util.Optional.of; -import static java.util.Optional.ofNullable; -import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.FACET; -import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.FTS; -import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.ID; -import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.SORT; -import static org.apache.solr.common.params.CommonParams.PATH; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.io.Reader; -import java.io.StringWriter; -import java.lang.invoke.MethodHandles; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.query.AbstractQParser; -import org.apache.cxf.transport.http.auth.HttpAuthHeader; -import org.apache.lucene.document.Document; -import org.apache.lucene.index.ExitableDirectoryReader; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.util.Version; -import org.apache.solr.client.solrj.SolrServerException; -import org.apache.solr.common.SolrDocument; -import org.apache.solr.common.SolrDocumentList; -import org.apache.solr.common.SolrException; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.ContentStream; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.core.CloseHook; -import org.apache.solr.core.PluginInfo; -import org.apache.solr.core.SolrCore; -import org.apache.solr.handler.RequestHandlerBase; -import org.apache.solr.handler.component.FacetComponent.FacetContext; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.BasicResultContext; -import org.apache.solr.response.ResultContext; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocList; -import org.apache.solr.search.SolrQueryTimeoutImpl; -import org.apache.solr.search.SolrReturnFields; -import org.apache.solr.search.facet.FacetModule; -import org.apache.solr.util.RTimerTree; -import org.apache.solr.util.SolrPluginUtils; -import org.apache.solr.util.plugin.PluginInfoInitialized; -import org.apache.solr.util.plugin.SolrCoreAware; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.noggit.ObjectBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Andy - */ -public class AlfrescoSearchHandler extends RequestHandlerBase implements - SolrCoreAware, PluginInfoInitialized { - - static final String INIT_COMPONENTS = "components"; - static final String INIT_FIRST_COMPONENTS = "first-components"; - static final String INIT_LAST_COMPONENTS = "last-components"; - - private static final Logger log = LoggerFactory.getLogger(MethodHandles - .lookup().lookupClass()); - - protected volatile List components; - private ShardHandlerFactory shardHandlerFactory; - private PluginInfo shfInfo; - private SolrCore core; - - protected List getDefaultComponents() { - ArrayList names = new ArrayList<>(8); - names.add(QueryComponent.COMPONENT_NAME); - names.add(FacetComponent.COMPONENT_NAME); - names.add(FacetModule.COMPONENT_NAME); - names.add(MoreLikeThisComponent.COMPONENT_NAME); - names.add(HighlightComponent.COMPONENT_NAME); - names.add(StatsComponent.COMPONENT_NAME); - names.add(DebugComponent.COMPONENT_NAME); - names.add(ExpandComponent.COMPONENT_NAME); - return names; - } - - @Override - public void init(PluginInfo info) { - init(info.initArgs); - for (PluginInfo child : info.children) { - if ("shardHandlerFactory".equals(child.type)) { - this.shfInfo = child; - break; - } - } - } - - /** - * Initialize the components based on name. Note, if using - * INIT_FIRST_COMPONENTS or INIT_LAST_COMPONENTS, - * then the {@link DebugComponent} will always occur last. If this is not - * desired, then one must explicitly declare all components using the - * INIT_COMPONENTS syntax. - */ - @Override - @SuppressWarnings("unchecked") - public void inform(SolrCore core) { - this.core = core; - Set missing = new HashSet<>(); - List c = (List) initArgs.get(INIT_COMPONENTS); - missing.addAll(core.getSearchComponents().checkContains(c)); - List first = (List) initArgs.get(INIT_FIRST_COMPONENTS); - missing.addAll(core.getSearchComponents().checkContains(first)); - List last = (List) initArgs.get(INIT_LAST_COMPONENTS); - missing.addAll(core.getSearchComponents().checkContains(last)); - if (!missing.isEmpty()) - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, - "Missing SearchComponents named : " + missing); - if (c != null && (first != null || last != null)) - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, - "First/Last components only valid if you do not declare 'components'"); - - if (shfInfo == null) { - shardHandlerFactory = core.getCoreContainer().getShardHandlerFactory(); - } else { - shardHandlerFactory = core.createInitInstance(shfInfo, - ShardHandlerFactory.class, null, null); - core.addCloseHook(new CloseHook() { - @Override - public void preClose(SolrCore core) { - shardHandlerFactory.close(); - } - - @Override - public void postClose(SolrCore core) { - } - }); - } - - } - - private void initComponents() { - Object declaredComponents = initArgs.get(INIT_COMPONENTS); - List first = (List) initArgs.get(INIT_FIRST_COMPONENTS); - List last = (List) initArgs.get(INIT_LAST_COMPONENTS); - - List list = null; - boolean makeDebugLast = true; - if (declaredComponents == null) { - // Use the default component list - list = getDefaultComponents(); - - if (first != null) { - List clist = first; - clist.addAll(list); - list = clist; - } - - if (last != null) { - list.addAll(last); - } - } else { - list = (List) declaredComponents; - if (first != null || last != null) { - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, - "First/Last components only valid if you do not declare 'components'"); - } - makeDebugLast = false; - } - - // Build the component list - List components = new ArrayList<>(list.size()); - DebugComponent dbgCmp = null; - for (String c : list) { - SearchComponent comp = core.getSearchComponent(c); - if (comp instanceof DebugComponent && makeDebugLast == true) { - dbgCmp = (DebugComponent) comp; - } else { - components.add(comp); - log.debug("Adding component:" + comp); - } - } - if (makeDebugLast == true && dbgCmp != null) { - components.add(dbgCmp); - log.debug("Adding debug component:" + dbgCmp); - } - this.components = components; - } - - public List getComponents() { - List result = components; // volatile read - if (result == null) { - synchronized (this) { - if (components == null) { - initComponents(); - } - result = components; - } - } - return result; - } - - private void readJsonIntoContent(SolrQueryRequest req) throws IOException { - SolrParams solrParams = req.getParams(); - String jsonFacet = solrParams.get("json.facet"); - if(jsonFacet != null) { - Object o = ObjectBuilder.fromJSON(jsonFacet); - Map json = new HashMap(); - json.put("facet", o); - req.setJSON(json); - } - Iterable streams = req.getContentStreams(); - - JSONObject json = (JSONObject) req.getContext().get(AbstractQParser.ALFRESCO_JSON); - - if (json == null) { - if (streams != null) { - try { - Reader reader = null; - for (ContentStream stream : streams) { - reader = new BufferedReader(new InputStreamReader( - stream.getStream(), "UTF-8")); - } - - // TODO - replace with streaming-based solution e.g. - // SimpleJSON ContentHandler - if (reader != null) { - json = new JSONObject(new JSONTokener(reader)); - req.getContext().put(AbstractQParser.ALFRESCO_JSON, - json); - } - } catch (JSONException e) { - // This is expected when there is no json element to the - // request - } catch (IOException e) { - throw new AlfrescoRuntimeException( - "IO Error parsing query parameters", e); - } - } else if(req.getParams().get(AbstractQParser.ALFRESCO_JSON) != null) { - //json is in the params. - req.getContext().put(AbstractQParser.ALFRESCO_JSON, new JSONObject(req.getParams().get(AbstractQParser.ALFRESCO_JSON))); - } - } - } - - private ShardHandler getAndPrepShardHandler(SolrQueryRequest req, - ResponseBuilder rb) { - ShardHandler shardHandler = null; - - rb.isDistrib = req.getParams().getBool( - "distrib", - req.getCore().getCoreContainer().isZooKeeperAware()); - if (!rb.isDistrib) { - // for back compat, a shards param with URLs like - // localhost:8983/solr will mean that this - // search is distributed. - final String shards = req.getParams().get(ShardParams.SHARDS); - rb.isDistrib = ((shards != null) && (shards.indexOf('/') > 0)); - } - - if (rb.isDistrib) { - shardHandler = shardHandlerFactory.getShardHandler(); - shardHandler.prepDistributed(rb); - if (!rb.isDistrib) { - shardHandler = null; // request is not distributed after all and - // so the shard handler is not needed - } - } - - return shardHandler; - } - - - - - @Override - public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) - throws Exception { - readJsonIntoContent(req); - - - List components = getComponents(); - ResponseBuilder rb = new ResponseBuilder(req, rsp, components); - if (rb.requestInfo != null) { - rb.requestInfo.setResponseBuilder(rb); - } - - boolean dbg = req.getParams().getBool(CommonParams.DEBUG_QUERY, false); - rb.setDebug(dbg); - if (dbg == false) {// if it's true, we are doing everything anyway. - SolrPluginUtils.getDebugInterests( - req.getParams().getParams(CommonParams.DEBUG), rb); - } - - final RTimerTree timer = rb.isDebug() ? req.getRequestTimer() : null; - - final ShardHandler shardHandler1 = getAndPrepShardHandler(req, rb); // creates - // a - // ShardHandler - // object - // only - // if - // it's - // needed - - if (timer == null) { - // non-debugging prepare phase - - for (SearchComponent c : components) { - c.prepare(rb); - } - } else { - // debugging prepare phase - RTimerTree subt = timer.sub("prepare"); - for (SearchComponent c : components) { - rb.setTimer(subt.sub(c.getName())); - c.prepare(rb); - rb.getTimer().stop(); - } - subt.stop(); - } - - if (!rb.isDistrib) { - // a normal non-distributed request - - long timeAllowed = req.getParams().getLong( - CommonParams.TIME_ALLOWED, -1L); - if (timeAllowed > 0L) { - SolrQueryTimeoutImpl.set(timeAllowed); - } - try { - // The semantics of debugging vs not debugging are different - // enough that - // it makes sense to have two control loops - if (!rb.isDebug()) { - // Process - for (SearchComponent c : components) { - c.process(rb); - } - } else { - // Process - RTimerTree subt = timer.sub("process"); - for (SearchComponent c : components) { - rb.setTimer(subt.sub(c.getName())); - c.process(rb); - rb.getTimer().stop(); - } - subt.stop(); - - // add the timing info - if (rb.isDebugTimings()) { - rb.addDebugInfo("timing", timer.asNamedList()); - } - } - } catch (ExitableDirectoryReader.ExitingReaderException ex) { - log.warn("Query: " + req.getParamString() + "; " - + ex.getMessage()); - SolrDocumentList r = (SolrDocumentList) rb.rsp.getValues().get( - "response"); - if (r == null) - r = new SolrDocumentList(); - r.setNumFound(0); - rb.rsp.add("response", r); - if (rb.isDebug()) { - NamedList debug = new NamedList(); - debug.add("explain", new NamedList()); - rb.rsp.add("debug", debug); - } - rb.rsp.getResponseHeader().add("partialResults", Boolean.TRUE); - } finally { - SolrQueryTimeoutImpl.reset(); - } - - if (req.getParams().getBool("alfresco.getSolrDocumentList", false)) { - NamedList values = rsp.getValues(); - ResultContext response = (ResultContext) values.get("response"); - SolrDocumentList newResponse = new SolrDocumentList(); - DocList docs = response.getDocList(); - for (DocIterator it = docs.iterator(); it.hasNext(); /**/) { - newResponse.add(toSolrDocument( - req.getSearcher().doc(it.nextDoc()), - req.getSchema())); - } - values.add("responseSolrDocumentList", newResponse); - } - } else { - // a distributed request - - if (rb.outgoing == null) { - rb.outgoing = new LinkedList<>(); - } - rb.finished = new ArrayList<>(); - - int nextStage = 0; - do { - rb.stage = nextStage; - nextStage = ResponseBuilder.STAGE_DONE; - - // call all components - for (SearchComponent c : components) { - // the next stage is the minimum of what all components - // report - nextStage = Math.min(nextStage, c.distributedProcess(rb)); - } - - // check the outgoing queue and send requests - while (rb.outgoing.size() > 0) { - - // submit all current request tasks at once - while (rb.outgoing.size() > 0) { - ShardRequest sreq = rb.outgoing.remove(0); - sreq.actualShards = sreq.shards; - if (sreq.actualShards == ShardRequest.ALL_SHARDS) { - sreq.actualShards = rb.shards; - } - sreq.responses = new ArrayList<>( - sreq.actualShards.length); // presume we'll get - // a response from - // each shard we - // send to - - // TODO: map from shard to address[] - for (String shard : sreq.actualShards) { - ModifiableSolrParams params = new ModifiableSolrParams( - sreq.params); - params.remove(ShardParams.SHARDS); // not a - // top-level - // request - params.set(CommonParams.DISTRIB, "false"); // not a - // top-level - // request - params.remove("indent"); - params.remove(CommonParams.HEADER_ECHO_PARAMS); - params.set(ShardParams.IS_SHARD, true); // a sub - // (shard) - // request - params.set(ShardParams.SHARDS_PURPOSE, sreq.purpose); - params.set(ShardParams.SHARD_URL, shard); // so the - // shard - // knows - // what - // was - // asked - if (req.getContext().get(AbstractQParser.ALFRESCO_JSON) != null) { - //This will add the Alfresco JSON as a parameter, overwriting the parameter if it already exists. - params.set(AbstractQParser.ALFRESCO_JSON, - req.getContext().get(AbstractQParser.ALFRESCO_JSON).toString()); - } - if (rb.requestInfo != null) { - // we could try and detect when this is needed, - // but it could be tricky - params.set("NOW", Long.toString(rb.requestInfo - .getNOW().getTime())); - } - String shardQt = params.get(ShardParams.SHARDS_QT); - if (shardQt != null) { - params.set(CommonParams.QT, shardQt); - } else { - // for distributed queries that don't include - // shards.qt, use the original path - // as the default but operators need to update - // their luceneMatchVersion to enable - // this behavior since it did not work this way - // prior to 5.1 - if (req.getCore().getSolrConfig().luceneMatchVersion - .onOrAfter(Version.LATEST)) { - String reqPath = (String) req.getContext() - .get(PATH); - if (!"/select".equals(reqPath)) { - params.set(CommonParams.QT, reqPath); - } // else if path is /select, then the qt - // gets passed thru if set - } else { - // this is the pre-5.1 behavior, which - // translates to sending the shard request - // to /select - params.remove(CommonParams.QT); - } - } - shardHandler1.submit(sreq, shard, params); - } - } - - // now wait for replies, but if anyone puts more requests on - // the outgoing queue, send them out immediately (by exiting - // this loop) - boolean tolerant = rb.req.getParams().getBool( - ShardParams.SHARDS_TOLERANT, false); - while (rb.outgoing.size() == 0) { - ShardResponse srsp = tolerant ? shardHandler1 - .takeCompletedIncludingErrors() : shardHandler1 - .takeCompletedOrError(); - if (srsp == null) - break; // no more requests to wait for - - // Was there an exception? - if (srsp.getException() != null) { - // If things are not tolerant, abort everything and - // rethrow - if (!tolerant) { - shardHandler1.cancelAll(); - if (srsp.getException() instanceof SolrException) { - throw (SolrException) srsp.getException(); - } else { - throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, - srsp.getException()); - } - } else { - if (rsp.getResponseHeader().get( - "partialResults") == null) { - rsp.getResponseHeader().add( - "partialResults", Boolean.TRUE); - } - } - } - - rb.finished.add(srsp.getShardRequest()); - - // let the components see the responses to the request - for (SearchComponent c : components) { - c.handleResponses(rb, srsp.getShardRequest()); - } - } - } - - for (SearchComponent c : components) { - c.finishStage(rb); - } - - // we are done when the next stage is MAX_VALUE - } while (nextStage != Integer.MAX_VALUE); - } - - // SOLR-5550: still provide shards.info if requested even for a short - // circuited distrib request - if (!rb.isDistrib - && req.getParams().getBool(ShardParams.SHARDS_INFO, false) - && rb.shortCircuitedURL != null) { - NamedList shardInfo = new SimpleOrderedMap(); - SimpleOrderedMap nl = new SimpleOrderedMap(); - if (rsp.getException() != null) { - Throwable cause = rsp.getException(); - if (cause instanceof SolrServerException) { - cause = ((SolrServerException) cause).getRootCause(); - } else { - if (cause.getCause() != null) { - cause = cause.getCause(); - } - } - nl.add("error", cause.toString()); - StringWriter trace = new StringWriter(); - cause.printStackTrace(new PrintWriter(trace)); - nl.add("trace", trace.toString()); - } else { - nl.add("numFound", rb.getResults().docList.matches()); - nl.add("maxScore", rb.getResults().docList.maxScore()); - } - nl.add("shardAddress", rb.shortCircuitedURL); - nl.add("time", req.getRequestTimer().getTime()); // elapsed time of - // this request - // so far - - int pos = rb.shortCircuitedURL.indexOf("://"); - String shardInfoName = pos != -1 ? rb.shortCircuitedURL - .substring(pos + 3) : rb.shortCircuitedURL; - shardInfo.add(shardInfoName, nl); - rsp.getValues().add(ShardParams.SHARDS_INFO, shardInfo); - } - - removeFacetQueriesWithCountZero(rsp); - - } - - /** - * Facet SOLR Queries are omitting facet results with count equals to 0 as general rule. - * SOLR Queries executed on a Shard environment, don't include these results but the same - * query executed on a single server environment are adding these elements to the response. - * This method removes every facet query having count equals to 0 to provide the same - * behaviour in both cases. - * - * A query like the following, will return only Facet Queries having elements: - * "facetQueries" : [ - * { "query" : "content.size:[0 TO 102400]", "label" : "small"}, - * { "query" : "content.size:[102400 TO 1048576]", "label" : "medium"}, - * { "query" : "content.size:[1048576 TO 16777216]", "label" : "large"} - * ] - * - * For instance, if there are only results with "small" key, the result will be: - * "facetQueries": [ - * { - * "label": "small", - * "filterQuery": "content.size:[0 TO 102400]", - * "count": 5 - * } - * ] - * - */ - public static final String FACET_COUNTS_KEY = "facet_counts"; - public static final String FACET_CONTEXT_KEY = "_facet.context"; - - @SuppressWarnings("unchecked") - public static void removeFacetQueriesWithCountZero(SolrQueryResponse rsp) - { - NamedList facetCounts = (NamedList) rsp.getValues().get(FACET_COUNTS_KEY); - if (facetCounts != null) - { - NamedList facetQueries = (NamedList) facetCounts.get(FacetComponent.FACET_QUERY_KEY); - if (facetQueries != null) - { - List keyCountsToRemove = new ArrayList<>(); - - facetQueries.forEach(facetQuery -> { - if ((Integer) facetQuery.getValue() == 0) - { - keyCountsToRemove.add(facetQuery.getKey()); - } - }); - - if (!keyCountsToRemove.isEmpty()) - { - keyCountsToRemove.forEach(key -> facetQueries.remove(key)); - - ((NamedList) rsp.getValues().get(FACET_COUNTS_KEY)).remove(FacetComponent.FACET_QUERY_KEY); - ((NamedList) rsp.getValues().get(FACET_COUNTS_KEY)).add(FacetComponent.FACET_QUERY_KEY, - facetQueries); - - BasicResultContext result = (BasicResultContext) rsp.getResponse(); - FacetContext facetContext = (FacetContext) result.getRequest().getContext() - .get(FACET_CONTEXT_KEY); - facetContext.getAllQueryFacets() - .removeIf(queryFacet -> keyCountsToRemove.contains(queryFacet.getKey())); - result.getRequest().getContext().put(FACET_CONTEXT_KEY, facetContext); - - log.debug("In SOLR query '" + result.getRequest() + "', Facet Queries results having labels " - + keyCountsToRemove + " have been removed from results"); - } - } - } - } - - // ////////////////////// SolrInfoMBeans methods ////////////////////// - - @Override - public String getDescription() { - StringBuilder sb = new StringBuilder(); - sb.append("Search using components: "); - if (components != null) { - for (SearchComponent c : components) { - sb.append(c.getName()); - sb.append(","); - } - } - return sb.toString(); - } - - public final SolrDocument toSolrDocument(Document doc, IndexSchema schema) { - SolrDocument out = new SolrDocument(); - for (IndexableField f : doc) { - // Make sure multivalued fields are represented as lists - Object existing = out.get(f.name()); - if (existing == null) { - SchemaField sf = schema.getFieldOrNull(f.name()); - if (sf != null && sf.multiValued()) { - List vals = new ArrayList<>(); - vals.add(f); - out.setField(f.name(), vals); - } else { - out.setField(f.name(), f); - } - } else { - out.addField(f.name(), f); - } - } - return out; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; + +import static java.util.Optional.of; +import static java.util.Optional.ofNullable; +import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.FACET; +import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.FTS; +import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.ID; +import static org.alfresco.solr.AlfrescoSolrDataModel.FieldUse.SORT; +import static org.apache.solr.common.params.CommonParams.PATH; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringWriter; +import java.lang.invoke.MethodHandles; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.query.AbstractQParser; +import org.apache.cxf.transport.http.auth.HttpAuthHeader; +import org.apache.lucene.document.Document; +import org.apache.lucene.index.ExitableDirectoryReader; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.util.Version; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.ContentStream; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.CloseHook; +import org.apache.solr.core.PluginInfo; +import org.apache.solr.core.SolrCore; +import org.apache.solr.handler.RequestHandlerBase; +import org.apache.solr.handler.component.FacetComponent.FacetContext; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.apache.solr.response.ResultContext; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrQueryTimeoutImpl; +import org.apache.solr.search.SolrReturnFields; +import org.apache.solr.search.facet.FacetModule; +import org.apache.solr.util.RTimerTree; +import org.apache.solr.util.SolrPluginUtils; +import org.apache.solr.util.plugin.PluginInfoInitialized; +import org.apache.solr.util.plugin.SolrCoreAware; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.noggit.ObjectBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Andy + */ +public class AlfrescoSearchHandler extends RequestHandlerBase implements + SolrCoreAware, PluginInfoInitialized { + + static final String INIT_COMPONENTS = "components"; + static final String INIT_FIRST_COMPONENTS = "first-components"; + static final String INIT_LAST_COMPONENTS = "last-components"; + + private static final Logger log = LoggerFactory.getLogger(MethodHandles + .lookup().lookupClass()); + + protected volatile List components; + private ShardHandlerFactory shardHandlerFactory; + private PluginInfo shfInfo; + private SolrCore core; + + protected List getDefaultComponents() { + ArrayList names = new ArrayList<>(8); + names.add(QueryComponent.COMPONENT_NAME); + names.add(FacetComponent.COMPONENT_NAME); + names.add(FacetModule.COMPONENT_NAME); + names.add(MoreLikeThisComponent.COMPONENT_NAME); + names.add(HighlightComponent.COMPONENT_NAME); + names.add(StatsComponent.COMPONENT_NAME); + names.add(DebugComponent.COMPONENT_NAME); + names.add(ExpandComponent.COMPONENT_NAME); + return names; + } + + @Override + public void init(PluginInfo info) { + init(info.initArgs); + for (PluginInfo child : info.children) { + if ("shardHandlerFactory".equals(child.type)) { + this.shfInfo = child; + break; + } + } + } + + /** + * Initialize the components based on name. Note, if using + * INIT_FIRST_COMPONENTS or INIT_LAST_COMPONENTS, + * then the {@link DebugComponent} will always occur last. If this is not + * desired, then one must explicitly declare all components using the + * INIT_COMPONENTS syntax. + */ + @Override + @SuppressWarnings("unchecked") + public void inform(SolrCore core) { + this.core = core; + Set missing = new HashSet<>(); + List c = (List) initArgs.get(INIT_COMPONENTS); + missing.addAll(core.getSearchComponents().checkContains(c)); + List first = (List) initArgs.get(INIT_FIRST_COMPONENTS); + missing.addAll(core.getSearchComponents().checkContains(first)); + List last = (List) initArgs.get(INIT_LAST_COMPONENTS); + missing.addAll(core.getSearchComponents().checkContains(last)); + if (!missing.isEmpty()) + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, + "Missing SearchComponents named : " + missing); + if (c != null && (first != null || last != null)) + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, + "First/Last components only valid if you do not declare 'components'"); + + if (shfInfo == null) { + shardHandlerFactory = core.getCoreContainer().getShardHandlerFactory(); + } else { + shardHandlerFactory = core.createInitInstance(shfInfo, + ShardHandlerFactory.class, null, null); + core.addCloseHook(new CloseHook() { + @Override + public void preClose(SolrCore core) { + shardHandlerFactory.close(); + } + + @Override + public void postClose(SolrCore core) { + } + }); + } + + } + + private void initComponents() { + Object declaredComponents = initArgs.get(INIT_COMPONENTS); + List first = (List) initArgs.get(INIT_FIRST_COMPONENTS); + List last = (List) initArgs.get(INIT_LAST_COMPONENTS); + + List list = null; + boolean makeDebugLast = true; + if (declaredComponents == null) { + // Use the default component list + list = getDefaultComponents(); + + if (first != null) { + List clist = first; + clist.addAll(list); + list = clist; + } + + if (last != null) { + list.addAll(last); + } + } else { + list = (List) declaredComponents; + if (first != null || last != null) { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, + "First/Last components only valid if you do not declare 'components'"); + } + makeDebugLast = false; + } + + // Build the component list + List components = new ArrayList<>(list.size()); + DebugComponent dbgCmp = null; + for (String c : list) { + SearchComponent comp = core.getSearchComponent(c); + if (comp instanceof DebugComponent && makeDebugLast == true) { + dbgCmp = (DebugComponent) comp; + } else { + components.add(comp); + log.debug("Adding component:" + comp); + } + } + if (makeDebugLast == true && dbgCmp != null) { + components.add(dbgCmp); + log.debug("Adding debug component:" + dbgCmp); + } + this.components = components; + } + + public List getComponents() { + List result = components; // volatile read + if (result == null) { + synchronized (this) { + if (components == null) { + initComponents(); + } + result = components; + } + } + return result; + } + + private void readJsonIntoContent(SolrQueryRequest req) throws IOException { + SolrParams solrParams = req.getParams(); + String jsonFacet = solrParams.get("json.facet"); + if(jsonFacet != null) { + Object o = ObjectBuilder.fromJSON(jsonFacet); + Map json = new HashMap(); + json.put("facet", o); + req.setJSON(json); + } + Iterable streams = req.getContentStreams(); + + JSONObject json = (JSONObject) req.getContext().get(AbstractQParser.ALFRESCO_JSON); + + if (json == null) { + if (streams != null) { + try { + Reader reader = null; + for (ContentStream stream : streams) { + reader = new BufferedReader(new InputStreamReader( + stream.getStream(), "UTF-8")); + } + + // TODO - replace with streaming-based solution e.g. + // SimpleJSON ContentHandler + if (reader != null) { + json = new JSONObject(new JSONTokener(reader)); + req.getContext().put(AbstractQParser.ALFRESCO_JSON, + json); + } + } catch (JSONException e) { + // This is expected when there is no json element to the + // request + } catch (IOException e) { + throw new AlfrescoRuntimeException( + "IO Error parsing query parameters", e); + } + } else if(req.getParams().get(AbstractQParser.ALFRESCO_JSON) != null) { + //json is in the params. + req.getContext().put(AbstractQParser.ALFRESCO_JSON, new JSONObject(req.getParams().get(AbstractQParser.ALFRESCO_JSON))); + } + } + } + + private ShardHandler getAndPrepShardHandler(SolrQueryRequest req, + ResponseBuilder rb) { + ShardHandler shardHandler = null; + + rb.isDistrib = req.getParams().getBool( + "distrib", + req.getCore().getCoreContainer().isZooKeeperAware()); + if (!rb.isDistrib) { + // for back compat, a shards param with URLs like + // localhost:8983/solr will mean that this + // search is distributed. + final String shards = req.getParams().get(ShardParams.SHARDS); + rb.isDistrib = ((shards != null) && (shards.indexOf('/') > 0)); + } + + if (rb.isDistrib) { + shardHandler = shardHandlerFactory.getShardHandler(); + shardHandler.prepDistributed(rb); + if (!rb.isDistrib) { + shardHandler = null; // request is not distributed after all and + // so the shard handler is not needed + } + } + + return shardHandler; + } + + + + + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) + throws Exception { + readJsonIntoContent(req); + + + List components = getComponents(); + ResponseBuilder rb = new ResponseBuilder(req, rsp, components); + if (rb.requestInfo != null) { + rb.requestInfo.setResponseBuilder(rb); + } + + boolean dbg = req.getParams().getBool(CommonParams.DEBUG_QUERY, false); + rb.setDebug(dbg); + if (dbg == false) {// if it's true, we are doing everything anyway. + SolrPluginUtils.getDebugInterests( + req.getParams().getParams(CommonParams.DEBUG), rb); + } + + final RTimerTree timer = rb.isDebug() ? req.getRequestTimer() : null; + + final ShardHandler shardHandler1 = getAndPrepShardHandler(req, rb); // creates + // a + // ShardHandler + // object + // only + // if + // it's + // needed + + if (timer == null) { + // non-debugging prepare phase + + for (SearchComponent c : components) { + c.prepare(rb); + } + } else { + // debugging prepare phase + RTimerTree subt = timer.sub("prepare"); + for (SearchComponent c : components) { + rb.setTimer(subt.sub(c.getName())); + c.prepare(rb); + rb.getTimer().stop(); + } + subt.stop(); + } + + if (!rb.isDistrib) { + // a normal non-distributed request + + long timeAllowed = req.getParams().getLong( + CommonParams.TIME_ALLOWED, -1L); + if (timeAllowed > 0L) { + SolrQueryTimeoutImpl.set(timeAllowed); + } + try { + // The semantics of debugging vs not debugging are different + // enough that + // it makes sense to have two control loops + if (!rb.isDebug()) { + // Process + for (SearchComponent c : components) { + c.process(rb); + } + } else { + // Process + RTimerTree subt = timer.sub("process"); + for (SearchComponent c : components) { + rb.setTimer(subt.sub(c.getName())); + c.process(rb); + rb.getTimer().stop(); + } + subt.stop(); + + // add the timing info + if (rb.isDebugTimings()) { + rb.addDebugInfo("timing", timer.asNamedList()); + } + } + } catch (ExitableDirectoryReader.ExitingReaderException ex) { + log.warn("Query: " + req.getParamString() + "; " + + ex.getMessage()); + SolrDocumentList r = (SolrDocumentList) rb.rsp.getValues().get( + "response"); + if (r == null) + r = new SolrDocumentList(); + r.setNumFound(0); + rb.rsp.add("response", r); + if (rb.isDebug()) { + NamedList debug = new NamedList(); + debug.add("explain", new NamedList()); + rb.rsp.add("debug", debug); + } + rb.rsp.getResponseHeader().add("partialResults", Boolean.TRUE); + } finally { + SolrQueryTimeoutImpl.reset(); + } + + if (req.getParams().getBool("alfresco.getSolrDocumentList", false)) { + NamedList values = rsp.getValues(); + ResultContext response = (ResultContext) values.get("response"); + SolrDocumentList newResponse = new SolrDocumentList(); + DocList docs = response.getDocList(); + for (DocIterator it = docs.iterator(); it.hasNext(); /**/) { + newResponse.add(toSolrDocument( + req.getSearcher().doc(it.nextDoc()), + req.getSchema())); + } + values.add("responseSolrDocumentList", newResponse); + } + } else { + // a distributed request + + if (rb.outgoing == null) { + rb.outgoing = new LinkedList<>(); + } + rb.finished = new ArrayList<>(); + + int nextStage = 0; + do { + rb.stage = nextStage; + nextStage = ResponseBuilder.STAGE_DONE; + + // call all components + for (SearchComponent c : components) { + // the next stage is the minimum of what all components + // report + nextStage = Math.min(nextStage, c.distributedProcess(rb)); + } + + // check the outgoing queue and send requests + while (rb.outgoing.size() > 0) { + + // submit all current request tasks at once + while (rb.outgoing.size() > 0) { + ShardRequest sreq = rb.outgoing.remove(0); + sreq.actualShards = sreq.shards; + if (sreq.actualShards == ShardRequest.ALL_SHARDS) { + sreq.actualShards = rb.shards; + } + sreq.responses = new ArrayList<>( + sreq.actualShards.length); // presume we'll get + // a response from + // each shard we + // send to + + // TODO: map from shard to address[] + for (String shard : sreq.actualShards) { + ModifiableSolrParams params = new ModifiableSolrParams( + sreq.params); + params.remove(ShardParams.SHARDS); // not a + // top-level + // request + params.set(CommonParams.DISTRIB, "false"); // not a + // top-level + // request + params.remove("indent"); + params.remove(CommonParams.HEADER_ECHO_PARAMS); + params.set(ShardParams.IS_SHARD, true); // a sub + // (shard) + // request + params.set(ShardParams.SHARDS_PURPOSE, sreq.purpose); + params.set(ShardParams.SHARD_URL, shard); // so the + // shard + // knows + // what + // was + // asked + if (req.getContext().get(AbstractQParser.ALFRESCO_JSON) != null) { + //This will add the Alfresco JSON as a parameter, overwriting the parameter if it already exists. + params.set(AbstractQParser.ALFRESCO_JSON, + req.getContext().get(AbstractQParser.ALFRESCO_JSON).toString()); + } + if (rb.requestInfo != null) { + // we could try and detect when this is needed, + // but it could be tricky + params.set("NOW", Long.toString(rb.requestInfo + .getNOW().getTime())); + } + String shardQt = params.get(ShardParams.SHARDS_QT); + if (shardQt != null) { + params.set(CommonParams.QT, shardQt); + } else { + // for distributed queries that don't include + // shards.qt, use the original path + // as the default but operators need to update + // their luceneMatchVersion to enable + // this behavior since it did not work this way + // prior to 5.1 + if (req.getCore().getSolrConfig().luceneMatchVersion + .onOrAfter(Version.LATEST)) { + String reqPath = (String) req.getContext() + .get(PATH); + if (!"/select".equals(reqPath)) { + params.set(CommonParams.QT, reqPath); + } // else if path is /select, then the qt + // gets passed thru if set + } else { + // this is the pre-5.1 behavior, which + // translates to sending the shard request + // to /select + params.remove(CommonParams.QT); + } + } + shardHandler1.submit(sreq, shard, params); + } + } + + // now wait for replies, but if anyone puts more requests on + // the outgoing queue, send them out immediately (by exiting + // this loop) + boolean tolerant = rb.req.getParams().getBool( + ShardParams.SHARDS_TOLERANT, false); + while (rb.outgoing.size() == 0) { + ShardResponse srsp = tolerant ? shardHandler1 + .takeCompletedIncludingErrors() : shardHandler1 + .takeCompletedOrError(); + if (srsp == null) + break; // no more requests to wait for + + // Was there an exception? + if (srsp.getException() != null) { + // If things are not tolerant, abort everything and + // rethrow + if (!tolerant) { + shardHandler1.cancelAll(); + if (srsp.getException() instanceof SolrException) { + throw (SolrException) srsp.getException(); + } else { + throw new SolrException( + SolrException.ErrorCode.SERVER_ERROR, + srsp.getException()); + } + } else { + if (rsp.getResponseHeader().get( + "partialResults") == null) { + rsp.getResponseHeader().add( + "partialResults", Boolean.TRUE); + } + } + } + + rb.finished.add(srsp.getShardRequest()); + + // let the components see the responses to the request + for (SearchComponent c : components) { + c.handleResponses(rb, srsp.getShardRequest()); + } + } + } + + for (SearchComponent c : components) { + c.finishStage(rb); + } + + // we are done when the next stage is MAX_VALUE + } while (nextStage != Integer.MAX_VALUE); + } + + // SOLR-5550: still provide shards.info if requested even for a short + // circuited distrib request + if (!rb.isDistrib + && req.getParams().getBool(ShardParams.SHARDS_INFO, false) + && rb.shortCircuitedURL != null) { + NamedList shardInfo = new SimpleOrderedMap(); + SimpleOrderedMap nl = new SimpleOrderedMap(); + if (rsp.getException() != null) { + Throwable cause = rsp.getException(); + if (cause instanceof SolrServerException) { + cause = ((SolrServerException) cause).getRootCause(); + } else { + if (cause.getCause() != null) { + cause = cause.getCause(); + } + } + nl.add("error", cause.toString()); + StringWriter trace = new StringWriter(); + cause.printStackTrace(new PrintWriter(trace)); + nl.add("trace", trace.toString()); + } else { + nl.add("numFound", rb.getResults().docList.matches()); + nl.add("maxScore", rb.getResults().docList.maxScore()); + } + nl.add("shardAddress", rb.shortCircuitedURL); + nl.add("time", req.getRequestTimer().getTime()); // elapsed time of + // this request + // so far + + int pos = rb.shortCircuitedURL.indexOf("://"); + String shardInfoName = pos != -1 ? rb.shortCircuitedURL + .substring(pos + 3) : rb.shortCircuitedURL; + shardInfo.add(shardInfoName, nl); + rsp.getValues().add(ShardParams.SHARDS_INFO, shardInfo); + } + + removeFacetQueriesWithCountZero(rsp); + + } + + /** + * Facet SOLR Queries are omitting facet results with count equals to 0 as general rule. + * SOLR Queries executed on a Shard environment, don't include these results but the same + * query executed on a single server environment are adding these elements to the response. + * This method removes every facet query having count equals to 0 to provide the same + * behaviour in both cases. + * + * A query like the following, will return only Facet Queries having elements: + * "facetQueries" : [ + * { "query" : "content.size:[0 TO 102400]", "label" : "small"}, + * { "query" : "content.size:[102400 TO 1048576]", "label" : "medium"}, + * { "query" : "content.size:[1048576 TO 16777216]", "label" : "large"} + * ] + * + * For instance, if there are only results with "small" key, the result will be: + * "facetQueries": [ + * { + * "label": "small", + * "filterQuery": "content.size:[0 TO 102400]", + * "count": 5 + * } + * ] + * + */ + public static final String FACET_COUNTS_KEY = "facet_counts"; + public static final String FACET_CONTEXT_KEY = "_facet.context"; + + @SuppressWarnings("unchecked") + public static void removeFacetQueriesWithCountZero(SolrQueryResponse rsp) + { + NamedList facetCounts = (NamedList) rsp.getValues().get(FACET_COUNTS_KEY); + if (facetCounts != null) + { + NamedList facetQueries = (NamedList) facetCounts.get(FacetComponent.FACET_QUERY_KEY); + if (facetQueries != null) + { + List keyCountsToRemove = new ArrayList<>(); + + facetQueries.forEach(facetQuery -> { + if ((Integer) facetQuery.getValue() == 0) + { + keyCountsToRemove.add(facetQuery.getKey()); + } + }); + + if (!keyCountsToRemove.isEmpty()) + { + keyCountsToRemove.forEach(key -> facetQueries.remove(key)); + + ((NamedList) rsp.getValues().get(FACET_COUNTS_KEY)).remove(FacetComponent.FACET_QUERY_KEY); + ((NamedList) rsp.getValues().get(FACET_COUNTS_KEY)).add(FacetComponent.FACET_QUERY_KEY, + facetQueries); + + BasicResultContext result = (BasicResultContext) rsp.getResponse(); + FacetContext facetContext = (FacetContext) result.getRequest().getContext() + .get(FACET_CONTEXT_KEY); + facetContext.getAllQueryFacets() + .removeIf(queryFacet -> keyCountsToRemove.contains(queryFacet.getKey())); + result.getRequest().getContext().put(FACET_CONTEXT_KEY, facetContext); + + log.debug("In SOLR query '" + result.getRequest() + "', Facet Queries results having labels " + + keyCountsToRemove + " have been removed from results"); + } + } + } + } + + // ////////////////////// SolrInfoMBeans methods ////////////////////// + + @Override + public String getDescription() { + StringBuilder sb = new StringBuilder(); + sb.append("Search using components: "); + if (components != null) { + for (SearchComponent c : components) { + sb.append(c.getName()); + sb.append(","); + } + } + return sb.toString(); + } + + public final SolrDocument toSolrDocument(Document doc, IndexSchema schema) { + SolrDocument out = new SolrDocument(); + for (IndexableField f : doc) { + // Make sure multivalued fields are represented as lists + Object existing = out.get(f.name()); + if (existing == null) { + SchemaField sf = schema.getFieldOrNull(f.name()); + if (sf != null && sf.multiValued()) { + List vals = new ArrayList<>(); + vals.add(f); + out.setField(f.name(), vals); + } else { + out.setField(f.name(), f); + } + } else { + out.addField(f.name(), f); + } + } + return out; + } +} diff --git a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSolrHighlighter.java b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSolrHighlighter.java index 7ab0ea24f..360de7842 100644 --- a/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSolrHighlighter.java +++ b/search-services/alfresco-search/src/main/java/org/apache/solr/handler/component/AlfrescoSolrHighlighter.java @@ -1,462 +1,462 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.apache.solr.handler.component; - -import static java.lang.String.join; -import static java.util.Arrays.stream; -import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; - -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.document.Document; -import org.apache.lucene.index.Terms; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.highlight.Highlighter; -import org.apache.lucene.search.highlight.QueryScorer; -import org.apache.lucene.search.highlight.WeightedSpanTerm; -import org.apache.lucene.search.highlight.WeightedSpanTermExtractor; -import org.apache.solr.common.params.HighlightParams; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.core.SolrCore; -import org.apache.solr.highlight.DefaultSolrHighlighter; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.search.DocList; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.util.plugin.PluginInfoInitialized; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.StreamSupport; - -/** - * The Alfresco customisation of the original (or somewhere called default) Solr highlighter. - * In this latest revision, the core Highlighting logic has been delegated to the Solr highlighter. - * The main difference introduced in this customisation is related with the fields mappings (i.e. mappings between - * the Alfresco and Solr fields names). - * - * The purpose of the delegation to the built-in Solr logic is to allow (later) an easier refactoring/removal. - * Specifically, when the mapping logic will be extracted in an external (calling) layer this component will be removed - * in favour of the built-in Solr (Default) Highlighter. - * - * The {@link #doHighlighting(DocList, Query, SolrQueryRequest, String[])} core method, which has been overriden, - * provides the required mapping between the requested highlighting fields in the hl.fl parameters (which follow the - * Alfresco semantic) and the Solr fields we have in the schema. - * - * At the end of the highlighting process the same mappings are used for doing the reverse process (replace the Solr - * fields with the original requested fields). - * - * Additionally, since the Solr ID has no meaning outside Solr, the Highlighter adds the DBID to each highlighting - * snippets: - * - *
- *  <lst name="_DEFAULT_!8000016f66a1a298!8000016f66a1a29e">
- *      <str name="DBID">1577974866590</str>
- *      <arr name="name">
- *          <str>some very <em>long</em> name</str>
- *      </arr>
- *      <arr name="title">
- *          <str>This the <em>long</em> french version of of the</str>
- *          <str>This the <em>long</em> english version of the</str>
- *      </arr>
- *  </lst>
- * 
- * - * @see SEARCH-2033 - */ -public class AlfrescoSolrHighlighter extends DefaultSolrHighlighter implements PluginInfoInitialized -{ - private static final Logger LOGGER = LoggerFactory.getLogger(AlfrescoSolrHighlighter.class); - - private static class DocumentIdentifiers - { - final String solrId; - final String dbid; - - private DocumentIdentifiers(String solrId, String dbid) - { - this.solrId = solrId; - this.dbid = dbid; - } - - String dbid() - { - return dbid; - } - - @Override - public boolean equals(Object obj) - { - return obj instanceof DocumentIdentifiers - && ((DocumentIdentifiers)obj).solrId.equals(solrId); - } - - @Override - public int hashCode() - { - return Objects.hash(solrId); - } - } - - private final Predicate> notNullAndNotEmpty = response -> response != null && response.size() > 0; - - public AlfrescoSolrHighlighter(SolrCore core) - { - super(core); - } - - @Override - protected Highlighter getHighlighter(Query query, String requestFieldname, SolrQueryRequest request) - { - String schemaFieldName = - AlfrescoSolrDataModel.getInstance() - .mapProperty(requestFieldname, FieldUse.HIGHLIGHT, request); - - Highlighter highlighter = - new Highlighter( - getFormatter(requestFieldname, request.getParams()), - getEncoder(requestFieldname, request.getParams()), - getQueryScorer(query,schemaFieldName, request)); - - highlighter.setTextFragmenter(getFragmenter(requestFieldname, request.getParams())); - return highlighter; - } - - /** - * We need to override this method because here Solr manages the {@link HighlightParams#FIELD_MATCH} parameter. - * In the last iteration, we improved the Solr schema by using one single stored field (used in this context for the - * highlighting) which is copied across multiple search fields (e.g. text@s__t,text@s_lt,mltext@m__lt). - * - * The input requestFieldName parameter is the stored field mentioned above; in case {@link HighlightParams#FIELD_MATCH} - * is set to true only query terms aligning with the field being highlighted will in turn be highlighted. - * - * The reverse field mapping you'll find in this method is because the stored field is not indexed, and therefore - * there won't be any "alignment" as described above, never. - * - * So starting from the stored field we need to query {@link AlfrescoSolrDataModel} for retrieving the corresponding - * cross-locale field (text|mltext@m|s__t@) and inject it into the {@link QueryScorer} that is in charge to score only - * those terms that contributed in generating the 'hit' on the document. - * - * TODO: this is one thing that can be improved together with the multilanguage improvement/implementation tasks - * - * @see SEARCH-1665 - * @see SEARCH-2056 - */ - @Override - protected QueryScorer getSpanQueryScorer(Query query, String requestFieldname, TokenStream tokenStream, SolrQueryRequest request) - { - String localFieldName = requestFieldname.substring(requestFieldname.lastIndexOf("}") + 1); - String schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty(localFieldName, FieldUse.FTS, request); - if (!schemaFieldName.contains("_t@{")) - { - schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty(localFieldName, FieldUse.FTS, request, 1); - } - - - // The query scorer purpose is to give a score to the text fragments by the number of unique query terms found. - // - // Fields that use the Alfresco MLAnalyser have a locale marker at the beginning of each token. For example, - // the text "My name is Gazza" becomes (locale = en): - // - // {en}my {en}name {en}is {en}gazza - // - // while this is not an issue for searching, those tokens never find a match during the highlighting because - // my != {en}my - // - // The purpose of this custom query scorer is to replace the collected weighted terms with their corresponding - // un-localised version. - QueryScorer scorer = new QueryScorer(query,request.getParams().getFieldBool(requestFieldname, HighlightParams.FIELD_MATCH, false) ? schemaFieldName : null) - { - @Override - protected WeightedSpanTermExtractor newTermExtractor(String defaultField) - { - return new WeightedSpanTermExtractor(defaultField) - { - @Override - protected void extractWeightedTerms(Map terms, Query query, float boost) throws IOException - { - super.extractWeightedTerms(terms, query, boost); - List termsWithoutLocale = - terms.values() - .stream() - .peek(term -> term.setTerm(withoutLocalePrefixMarker(term.getTerm()))) - .collect(toList()); - - terms.clear(); - termsWithoutLocale.forEach(term -> terms.put(term.getTerm(), term)); - } - }; - } - - /** - * Removes the locale marker from the given text. - * - * @param text the input text. - * @return the text without the beginning locale marker, or the same text is the marker cannot be found. - */ - private String withoutLocalePrefixMarker(String text) - { - if (text == null) return null; - - int startIndexOfMarker = text.indexOf("{"); - if (startIndexOfMarker == 0) - { - int endIndexOfMarker = text.indexOf("}"); - if (endIndexOfMarker != -1 && text.length() > (endIndexOfMarker + 1)) - { - return text.substring(endIndexOfMarker + 1); - } - } - - return text; - } - }; - scorer.setExpandMultiTermQuery(request.getParams().getBool(HighlightParams.HIGHLIGHT_MULTI_TERM, true)); - - boolean defaultPayloads = true;// overwritten below - try - { - // It'd be nice to know if payloads are on the tokenStream but the - // presence of the attribute isn't a good - // indicator. - final Terms terms = request.getSearcher().getSlowAtomicReader().fields().terms(schemaFieldName); - if (terms != null) - { - defaultPayloads = terms.hasPayloads(); - } - } - catch (IOException e) - { - LOGGER.error("Couldn't check for existence of payloads", e); - } - scorer.setUsePayloads(request.getParams().getFieldBool(requestFieldname, HighlightParams.PAYLOADS, defaultPayloads)); - return scorer; - } - - @SuppressWarnings("unchecked") - @Override - public NamedList doHighlighting(DocList docs, Query query, SolrQueryRequest request, String[] defaultFields) throws IOException - { - final String idFieldName = request.getSchema().getUniqueKeyField().getName(); - final Set idFields = Set.of(idFieldName, "DBID"); - final SolrParams originalRequestParameters = request.getParams(); - - // raw fields in the hl.fl parameter (e.g. hl.fl=content, name, title) - List highlightFields = stream(super.getHighlightFields(query, request, defaultFields)).collect(toList()); - - /* - For each field name collected in the previous step, we need to query the AlfrescoSolrDataModel in order to - retrieve the corresponding field that will be used for the current highlighting request. - - e.g. - { - name => text@s_stored_lt@{http://www.alfresco.org/model/content/1.0}name, - title => mltext@m__stored_lt@{http://www.alfresco.org/model/content/1.0}title, - content => content@s_stored_lt@{http://www.alfresco.org/model/content/1.0}content - } - - Since at the end we need to restore (in the response) the original request(ed) fields names (e.g. content, name) used on client side - we need to maintain a map which associates each schema field (e.g. text@s_stored_lt@{http://www.alfresco.org/model/content/1.0}name) - with the corresponding request(ed) field (e.g. name). - */ - Map mappings = withDebug(createInitialFieldMappings(request, highlightFields)); - - // The identifiers map collects two documents identifiers for each document (Solr "id" and "DBID"). - // Keys of the identifiers map are Solr "id", while values are simple value objects encapsulating all those two identifiers (for a specific document). - Iterable iterable = docs::iterator; - Map identifiers = - StreamSupport.stream(iterable.spliterator(), false) - .map(docid -> identifiersEntry(request.getSearcher(), docid, idFields, idFieldName)) - .filter(Objects::nonNull) - .collect(toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); - - // First round: call the Solr highlighting procedure using the current fields mappings. - request.setParams(rewrite(originalRequestParameters, mappings, join(",", mappings.keySet()))); - NamedList highlightingResponse = super.doHighlighting(docs, query, request, defaultFields); - - // Final step: under each document section, highlight snippets are associated with Solr field names, - // so we need to replace them with fields actually requested - // In addition, beside the snippets we want to have the document DBID as well. - NamedList response = new SimpleOrderedMap<>(); - highlightingResponse.forEach( entry -> { - String id = entry.getKey(); - NamedList documentHighlighting = (NamedList) entry.getValue(); - NamedList renamedDocumentHighlighting = new SimpleOrderedMap<>(); - if (notNullAndNotEmpty.test(documentHighlighting)) - { - ofNullable(identifiers.get(id)) - .map(DocumentIdentifiers::dbid) - .ifPresent(dbid -> renamedDocumentHighlighting.add("DBID", dbid)); - } - - documentHighlighting.forEach(fieldEntry -> { - detectAndRemoveLocalePrefix(fieldEntry); - - String solrFieldName = fieldEntry.getKey(); - String requestFieldName = mappings.get(solrFieldName); - renamedDocumentHighlighting.add(requestFieldName, fieldEntry.getValue()); - }); - - response.add(id, renamedDocumentHighlighting); - }); - - return response; - } - - /** - * Remember the stored field, used for highlighting, contains a locale marker prefix which has to be removed - * before returning back the response. - * - * @param highlightFieldEntry the response highlight entry for a specific field. - */ - private void detectAndRemoveLocalePrefix(Map.Entry highlightFieldEntry) - { - if (highlightFieldEntry.getValue() instanceof String[]) - { - String [] snippets = (String[])highlightFieldEntry.getValue(); - if (snippets.length > 0) - { - snippets[0] = snippets[0].charAt(3) == '\u0000' ? snippets[0].substring(4) : snippets[0].substring(5); - } - } - } - - private AbstractMap.SimpleEntry identifiersEntry(SolrIndexSearcher searcher, int docid, Set idFields, String idFieldName) - { - try - { - Document doc = searcher.doc(docid, idFields); - String solrId = doc.get(idFieldName); - return new AbstractMap.SimpleEntry<>(solrId, new DocumentIdentifiers(solrId, doc.get("DBID"))); - } - catch (Exception exception) - { - return null; - } - } - - private void rewriteLocalFieldParameters(ModifiableSolrParams newParams, SolrParams previousParams, String fieldName, String schemaFieldName) - { - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SIMPLE_PRE, fieldName, schemaFieldName); - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SIMPLE_POST, fieldName, schemaFieldName); - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.FRAGSIZE, fieldName, schemaFieldName); - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.MERGE_CONTIGUOUS_FRAGMENTS, fieldName, schemaFieldName); - rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SNIPPETS, fieldName, schemaFieldName); - } - - private void rewriteHighlightFieldOptions(ModifiableSolrParams fixed, SolrParams params, String paramName, String oldField, String newField) - { - for(Iterator it = params.getParameterNamesIterator(); it.hasNext();) - { - String name = it.next(); - if(name.startsWith("f.")) - { - if(name.endsWith("." + paramName)) - { - - String source = name.substring(2, name.length() - paramName.length() - 1); - if(source.equals(oldField)) - { - fixed.set("f." + newField + "." + paramName, params.getParams(name)); - } - else - { - fixed.set(name, params.getParams(name)); - } - } - else - { - fixed.set(name, params.getParams(name)); - } - } - } - } - - private SolrParams rewrite(SolrParams params, Map mappings, String fields) - { - ModifiableSolrParams rewrittenParams = new ModifiableSolrParams(params).set(HighlightParams.FIELDS, fields); - mappings.forEach((key, value) -> rewriteLocalFieldParameters(rewrittenParams, params, value, key)); - return rewrittenParams; - } - - /** - * Debugs the content of the given mappings. - * - * @param mappings the fields mapping. - * @return the same input mappings instance. - */ - private Map withDebug(Map mappings) - { - if (LOGGER.isDebugEnabled()) - { - mappings.forEach( (solrField, requestField) -> LOGGER.debug("Request field {} has been mapped to {}", requestField, solrField)); - } - return mappings; - } - - /** - * Starting from the input requested highlight fields (i.e. fields listed in {@link HighlightParams#FIELDS} parameter) - * we create a map which associates each member with the corresponding field in the Solr schema. - * For example: - * - *
-	 * 	name 	=>	text@s___t@{http://www.alfresco.org/model/content/1.0}name,
-	 * 	title	=>	mltext@m___t@{http://www.alfresco.org/model/content/1.0}title,
-	 * 	content	=>	content@s___t@{http://www.alfresco.org/model/content/1.0}content
-	 * 
- * - * IMPORTANT: although returned as {@link Map} interface, the returned data structure IS MUTABLE. This is needed - * because during the highlighting workflow we need to change its content by adding fields. - * - * @param request the current incoming client request. - * @param requestedHighlightFields a list of raw fields listed in {@link HighlightParams#FIELDS} parameter - * @return a map associating request(ed) fields with the corresponding schema fields. - */ - private Map createInitialFieldMappings(SolrQueryRequest request, List requestedHighlightFields) - { - return requestedHighlightFields.stream() - .map(requestFieldName -> - new AbstractMap.SimpleEntry<>( - AlfrescoSolrDataModel.getInstance().mapProperty(requestFieldName, FieldUse.HIGHLIGHT, request), - requestFieldName)) - .collect(toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, (prev, next) -> next, HashMap::new)); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.apache.solr.handler.component; + +import static java.lang.String.join; +import static java.util.Arrays.stream; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; + +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.AlfrescoSolrDataModel.FieldUse; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.document.Document; +import org.apache.lucene.index.Terms; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.highlight.Highlighter; +import org.apache.lucene.search.highlight.QueryScorer; +import org.apache.lucene.search.highlight.WeightedSpanTerm; +import org.apache.lucene.search.highlight.WeightedSpanTermExtractor; +import org.apache.solr.common.params.HighlightParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.SolrCore; +import org.apache.solr.highlight.DefaultSolrHighlighter; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.util.plugin.PluginInfoInitialized; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.StreamSupport; + +/** + * The Alfresco customisation of the original (or somewhere called default) Solr highlighter. + * In this latest revision, the core Highlighting logic has been delegated to the Solr highlighter. + * The main difference introduced in this customisation is related with the fields mappings (i.e. mappings between + * the Alfresco and Solr fields names). + * + * The purpose of the delegation to the built-in Solr logic is to allow (later) an easier refactoring/removal. + * Specifically, when the mapping logic will be extracted in an external (calling) layer this component will be removed + * in favour of the built-in Solr (Default) Highlighter. + * + * The {@link #doHighlighting(DocList, Query, SolrQueryRequest, String[])} core method, which has been overriden, + * provides the required mapping between the requested highlighting fields in the hl.fl parameters (which follow the + * Alfresco semantic) and the Solr fields we have in the schema. + * + * At the end of the highlighting process the same mappings are used for doing the reverse process (replace the Solr + * fields with the original requested fields). + * + * Additionally, since the Solr ID has no meaning outside Solr, the Highlighter adds the DBID to each highlighting + * snippets: + * + *
+ *  <lst name="_DEFAULT_!8000016f66a1a298!8000016f66a1a29e">
+ *      <str name="DBID">1577974866590</str>
+ *      <arr name="name">
+ *          <str>some very <em>long</em> name</str>
+ *      </arr>
+ *      <arr name="title">
+ *          <str>This the <em>long</em> french version of of the</str>
+ *          <str>This the <em>long</em> english version of the</str>
+ *      </arr>
+ *  </lst>
+ * 
+ * + * @see SEARCH-2033 + */ +public class AlfrescoSolrHighlighter extends DefaultSolrHighlighter implements PluginInfoInitialized +{ + private static final Logger LOGGER = LoggerFactory.getLogger(AlfrescoSolrHighlighter.class); + + private static class DocumentIdentifiers + { + final String solrId; + final String dbid; + + private DocumentIdentifiers(String solrId, String dbid) + { + this.solrId = solrId; + this.dbid = dbid; + } + + String dbid() + { + return dbid; + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof DocumentIdentifiers + && ((DocumentIdentifiers)obj).solrId.equals(solrId); + } + + @Override + public int hashCode() + { + return Objects.hash(solrId); + } + } + + private final Predicate> notNullAndNotEmpty = response -> response != null && response.size() > 0; + + public AlfrescoSolrHighlighter(SolrCore core) + { + super(core); + } + + @Override + protected Highlighter getHighlighter(Query query, String requestFieldname, SolrQueryRequest request) + { + String schemaFieldName = + AlfrescoSolrDataModel.getInstance() + .mapProperty(requestFieldname, FieldUse.HIGHLIGHT, request); + + Highlighter highlighter = + new Highlighter( + getFormatter(requestFieldname, request.getParams()), + getEncoder(requestFieldname, request.getParams()), + getQueryScorer(query,schemaFieldName, request)); + + highlighter.setTextFragmenter(getFragmenter(requestFieldname, request.getParams())); + return highlighter; + } + + /** + * We need to override this method because here Solr manages the {@link HighlightParams#FIELD_MATCH} parameter. + * In the last iteration, we improved the Solr schema by using one single stored field (used in this context for the + * highlighting) which is copied across multiple search fields (e.g. text@s__t,text@s_lt,mltext@m__lt). + * + * The input requestFieldName parameter is the stored field mentioned above; in case {@link HighlightParams#FIELD_MATCH} + * is set to true only query terms aligning with the field being highlighted will in turn be highlighted. + * + * The reverse field mapping you'll find in this method is because the stored field is not indexed, and therefore + * there won't be any "alignment" as described above, never. + * + * So starting from the stored field we need to query {@link AlfrescoSolrDataModel} for retrieving the corresponding + * cross-locale field (text|mltext@m|s__t@) and inject it into the {@link QueryScorer} that is in charge to score only + * those terms that contributed in generating the 'hit' on the document. + * + * TODO: this is one thing that can be improved together with the multilanguage improvement/implementation tasks + * + * @see SEARCH-1665 + * @see SEARCH-2056 + */ + @Override + protected QueryScorer getSpanQueryScorer(Query query, String requestFieldname, TokenStream tokenStream, SolrQueryRequest request) + { + String localFieldName = requestFieldname.substring(requestFieldname.lastIndexOf("}") + 1); + String schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty(localFieldName, FieldUse.FTS, request); + if (!schemaFieldName.contains("_t@{")) + { + schemaFieldName = AlfrescoSolrDataModel.getInstance().mapProperty(localFieldName, FieldUse.FTS, request, 1); + } + + + // The query scorer purpose is to give a score to the text fragments by the number of unique query terms found. + // + // Fields that use the Alfresco MLAnalyser have a locale marker at the beginning of each token. For example, + // the text "My name is Gazza" becomes (locale = en): + // + // {en}my {en}name {en}is {en}gazza + // + // while this is not an issue for searching, those tokens never find a match during the highlighting because + // my != {en}my + // + // The purpose of this custom query scorer is to replace the collected weighted terms with their corresponding + // un-localised version. + QueryScorer scorer = new QueryScorer(query,request.getParams().getFieldBool(requestFieldname, HighlightParams.FIELD_MATCH, false) ? schemaFieldName : null) + { + @Override + protected WeightedSpanTermExtractor newTermExtractor(String defaultField) + { + return new WeightedSpanTermExtractor(defaultField) + { + @Override + protected void extractWeightedTerms(Map terms, Query query, float boost) throws IOException + { + super.extractWeightedTerms(terms, query, boost); + List termsWithoutLocale = + terms.values() + .stream() + .peek(term -> term.setTerm(withoutLocalePrefixMarker(term.getTerm()))) + .collect(toList()); + + terms.clear(); + termsWithoutLocale.forEach(term -> terms.put(term.getTerm(), term)); + } + }; + } + + /** + * Removes the locale marker from the given text. + * + * @param text the input text. + * @return the text without the beginning locale marker, or the same text is the marker cannot be found. + */ + private String withoutLocalePrefixMarker(String text) + { + if (text == null) return null; + + int startIndexOfMarker = text.indexOf("{"); + if (startIndexOfMarker == 0) + { + int endIndexOfMarker = text.indexOf("}"); + if (endIndexOfMarker != -1 && text.length() > (endIndexOfMarker + 1)) + { + return text.substring(endIndexOfMarker + 1); + } + } + + return text; + } + }; + scorer.setExpandMultiTermQuery(request.getParams().getBool(HighlightParams.HIGHLIGHT_MULTI_TERM, true)); + + boolean defaultPayloads = true;// overwritten below + try + { + // It'd be nice to know if payloads are on the tokenStream but the + // presence of the attribute isn't a good + // indicator. + final Terms terms = request.getSearcher().getSlowAtomicReader().fields().terms(schemaFieldName); + if (terms != null) + { + defaultPayloads = terms.hasPayloads(); + } + } + catch (IOException e) + { + LOGGER.error("Couldn't check for existence of payloads", e); + } + scorer.setUsePayloads(request.getParams().getFieldBool(requestFieldname, HighlightParams.PAYLOADS, defaultPayloads)); + return scorer; + } + + @SuppressWarnings("unchecked") + @Override + public NamedList doHighlighting(DocList docs, Query query, SolrQueryRequest request, String[] defaultFields) throws IOException + { + final String idFieldName = request.getSchema().getUniqueKeyField().getName(); + final Set idFields = Set.of(idFieldName, "DBID"); + final SolrParams originalRequestParameters = request.getParams(); + + // raw fields in the hl.fl parameter (e.g. hl.fl=content, name, title) + List highlightFields = stream(super.getHighlightFields(query, request, defaultFields)).collect(toList()); + + /* + For each field name collected in the previous step, we need to query the AlfrescoSolrDataModel in order to + retrieve the corresponding field that will be used for the current highlighting request. + + e.g. + { + name => text@s_stored_lt@{http://www.alfresco.org/model/content/1.0}name, + title => mltext@m__stored_lt@{http://www.alfresco.org/model/content/1.0}title, + content => content@s_stored_lt@{http://www.alfresco.org/model/content/1.0}content + } + + Since at the end we need to restore (in the response) the original request(ed) fields names (e.g. content, name) used on client side + we need to maintain a map which associates each schema field (e.g. text@s_stored_lt@{http://www.alfresco.org/model/content/1.0}name) + with the corresponding request(ed) field (e.g. name). + */ + Map mappings = withDebug(createInitialFieldMappings(request, highlightFields)); + + // The identifiers map collects two documents identifiers for each document (Solr "id" and "DBID"). + // Keys of the identifiers map are Solr "id", while values are simple value objects encapsulating all those two identifiers (for a specific document). + Iterable iterable = docs::iterator; + Map identifiers = + StreamSupport.stream(iterable.spliterator(), false) + .map(docid -> identifiersEntry(request.getSearcher(), docid, idFields, idFieldName)) + .filter(Objects::nonNull) + .collect(toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); + + // First round: call the Solr highlighting procedure using the current fields mappings. + request.setParams(rewrite(originalRequestParameters, mappings, join(",", mappings.keySet()))); + NamedList highlightingResponse = super.doHighlighting(docs, query, request, defaultFields); + + // Final step: under each document section, highlight snippets are associated with Solr field names, + // so we need to replace them with fields actually requested + // In addition, beside the snippets we want to have the document DBID as well. + NamedList response = new SimpleOrderedMap<>(); + highlightingResponse.forEach( entry -> { + String id = entry.getKey(); + NamedList documentHighlighting = (NamedList) entry.getValue(); + NamedList renamedDocumentHighlighting = new SimpleOrderedMap<>(); + if (notNullAndNotEmpty.test(documentHighlighting)) + { + ofNullable(identifiers.get(id)) + .map(DocumentIdentifiers::dbid) + .ifPresent(dbid -> renamedDocumentHighlighting.add("DBID", dbid)); + } + + documentHighlighting.forEach(fieldEntry -> { + detectAndRemoveLocalePrefix(fieldEntry); + + String solrFieldName = fieldEntry.getKey(); + String requestFieldName = mappings.get(solrFieldName); + renamedDocumentHighlighting.add(requestFieldName, fieldEntry.getValue()); + }); + + response.add(id, renamedDocumentHighlighting); + }); + + return response; + } + + /** + * Remember the stored field, used for highlighting, contains a locale marker prefix which has to be removed + * before returning back the response. + * + * @param highlightFieldEntry the response highlight entry for a specific field. + */ + private void detectAndRemoveLocalePrefix(Map.Entry highlightFieldEntry) + { + if (highlightFieldEntry.getValue() instanceof String[]) + { + String [] snippets = (String[])highlightFieldEntry.getValue(); + if (snippets.length > 0) + { + snippets[0] = snippets[0].charAt(3) == '\u0000' ? snippets[0].substring(4) : snippets[0].substring(5); + } + } + } + + private AbstractMap.SimpleEntry identifiersEntry(SolrIndexSearcher searcher, int docid, Set idFields, String idFieldName) + { + try + { + Document doc = searcher.doc(docid, idFields); + String solrId = doc.get(idFieldName); + return new AbstractMap.SimpleEntry<>(solrId, new DocumentIdentifiers(solrId, doc.get("DBID"))); + } + catch (Exception exception) + { + return null; + } + } + + private void rewriteLocalFieldParameters(ModifiableSolrParams newParams, SolrParams previousParams, String fieldName, String schemaFieldName) + { + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SIMPLE_PRE, fieldName, schemaFieldName); + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SIMPLE_POST, fieldName, schemaFieldName); + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.FRAGSIZE, fieldName, schemaFieldName); + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.MERGE_CONTIGUOUS_FRAGMENTS, fieldName, schemaFieldName); + rewriteHighlightFieldOptions(newParams, previousParams, HighlightParams.SNIPPETS, fieldName, schemaFieldName); + } + + private void rewriteHighlightFieldOptions(ModifiableSolrParams fixed, SolrParams params, String paramName, String oldField, String newField) + { + for(Iterator it = params.getParameterNamesIterator(); it.hasNext();) + { + String name = it.next(); + if(name.startsWith("f.")) + { + if(name.endsWith("." + paramName)) + { + + String source = name.substring(2, name.length() - paramName.length() - 1); + if(source.equals(oldField)) + { + fixed.set("f." + newField + "." + paramName, params.getParams(name)); + } + else + { + fixed.set(name, params.getParams(name)); + } + } + else + { + fixed.set(name, params.getParams(name)); + } + } + } + } + + private SolrParams rewrite(SolrParams params, Map mappings, String fields) + { + ModifiableSolrParams rewrittenParams = new ModifiableSolrParams(params).set(HighlightParams.FIELDS, fields); + mappings.forEach((key, value) -> rewriteLocalFieldParameters(rewrittenParams, params, value, key)); + return rewrittenParams; + } + + /** + * Debugs the content of the given mappings. + * + * @param mappings the fields mapping. + * @return the same input mappings instance. + */ + private Map withDebug(Map mappings) + { + if (LOGGER.isDebugEnabled()) + { + mappings.forEach( (solrField, requestField) -> LOGGER.debug("Request field {} has been mapped to {}", requestField, solrField)); + } + return mappings; + } + + /** + * Starting from the input requested highlight fields (i.e. fields listed in {@link HighlightParams#FIELDS} parameter) + * we create a map which associates each member with the corresponding field in the Solr schema. + * For example: + * + *
+	 * 	name 	=>	text@s___t@{http://www.alfresco.org/model/content/1.0}name,
+	 * 	title	=>	mltext@m___t@{http://www.alfresco.org/model/content/1.0}title,
+	 * 	content	=>	content@s___t@{http://www.alfresco.org/model/content/1.0}content
+	 * 
+ * + * IMPORTANT: although returned as {@link Map} interface, the returned data structure IS MUTABLE. This is needed + * because during the highlighting workflow we need to change its content by adding fields. + * + * @param request the current incoming client request. + * @param requestedHighlightFields a list of raw fields listed in {@link HighlightParams#FIELDS} parameter + * @return a map associating request(ed) fields with the corresponding schema fields. + */ + private Map createInitialFieldMappings(SolrQueryRequest request, List requestedHighlightFields) + { + return requestedHighlightFields.stream() + .map(requestFieldName -> + new AbstractMap.SimpleEntry<>( + AlfrescoSolrDataModel.getInstance().mapProperty(requestFieldName, FieldUse.HIGHLIGHT, request), + requestFieldName)) + .collect(toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, (prev, next) -> next, HashMap::new)); + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayserTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayserTest.java index 3401a07c6..e38000599 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayserTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/MLAnalayserTest.java @@ -1,182 +1,182 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import org.alfresco.repo.search.MLAnalysisMode; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.namespace.QName; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; - -/** - * Retrofitted tests for the {@link MLAnalayser} class. These - * should not be considered comprehensive, but are here to aid - * refactoring during move to SOLR4. - * - * @author Matt Ward - */ -@RunWith(MockitoJUnitRunner.class) -public class MLAnalayserTest -{ - private static final String PROPERTY_NAME = "@{http://www.alfresco.org/model/content/1.0}propertyName"; - private MLAnalayser analyser; - private @Mock DictionaryService dictionaryService; - private MLAnalysisMode mlAnalaysisMode = MLAnalysisMode.EXACT_COUNRTY; - - - @Before - public void setUp() throws Exception - { - analyser = new MLAnalayser(mlAnalaysisMode); - PropertyDefinition propDef = Mockito.mock(PropertyDefinition.class); - when(dictionaryService.getProperty(any(QName.class))).thenReturn(propDef); - } - - @Ignore - @Test - public void testTokenStreamForLanguageAndCountry() throws IOException - { - final String inputStr = "\u0000fr_FR\u0000Ceci n'est pas Française"; - final Reader reader = new StringReader(inputStr); - - List expectedTokens = new ArrayList<>(); - expectedTokens.add("{fr_FR}ceci"); - expectedTokens.add("{fr_FR}n'est"); - expectedTokens.add("{fr_FR}pas"); - expectedTokens.add("{fr_FR}francaise"); // normalised 'c'. - - TokenStream ts = analyser.tokenStream(PROPERTY_NAME, reader); - verifyTokenStream(ts, expectedTokens); - } - - @Ignore - @Test - public void testTokenStreamForLanguage() throws IOException - { - final String inputStr = "\u0000fr\u0000Ceci n'est pas Française"; - final Reader reader = new StringReader(inputStr); - - List expectedTokens = new ArrayList<>(); - expectedTokens.add("{fr}ceci"); - expectedTokens.add("{fr_CH}ceci"); - expectedTokens.add("{fr_LU}ceci"); - expectedTokens.add("{fr_FR}ceci"); - expectedTokens.add("{fr_BE}ceci"); - expectedTokens.add("{fr_CA}ceci"); - - expectedTokens.add("{fr}n'est"); - expectedTokens.add("{fr_CH}n'est"); - expectedTokens.add("{fr_LU}n'est"); - expectedTokens.add("{fr_FR}n'est"); - expectedTokens.add("{fr_BE}n'est"); - expectedTokens.add("{fr_CA}n'est"); - - expectedTokens.add("{fr}pas"); - expectedTokens.add("{fr_CH}pas"); - expectedTokens.add("{fr_LU}pas"); - expectedTokens.add("{fr_FR}pas"); - expectedTokens.add("{fr_BE}pas"); - expectedTokens.add("{fr_CA}pas"); - - expectedTokens.add("{fr}francaise"); - expectedTokens.add("{fr_CH}francaise"); - expectedTokens.add("{fr_LU}francaise"); - expectedTokens.add("{fr_FR}francaise"); - expectedTokens.add("{fr_BE}francaise"); - expectedTokens.add("{fr_CA}francaise"); - - TokenStream ts = analyser.tokenStream(PROPERTY_NAME, reader); - verifyTokenStream(ts, expectedTokens); - } - - /** - * Check that the TokenStream yields the exact tokens specified. - * Note that order is not checked, since the map of locales will not provide a - * predictable ordering when enumerated. - * - * The expected list of tokens may contain the same token more than once and - * the number of instances will have to match the number found in the stream. - * - * @param ts TokenStream to inspect. - * @param expectedTokens List of tokens in the order expected from the stream. - * @throws IOException - */ - private void verifyTokenStream(TokenStream ts, List expectedTokens) throws IOException - { - final int expectedCount = expectedTokens.size(); - int count = 0; - - CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); - - try - { - ts.reset(); - while (ts.incrementToken()) - { - count++; - System.out.println("Token: " + termAtt.toString()); - if (expectedTokens.contains(termAtt.toString())) - { - // remove an instance of the term text so that it is not matched again - expectedTokens.remove(termAtt.toString()); - } - else - { - fail("Unexpected token: " + termAtt.toString()); - } - } - ts.end(); - } - finally - { - ts.close(); - } - - assertEquals("Incorrect number of tokens generated.", expectedCount, count); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.alfresco.repo.search.MLAnalysisMode; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.QName; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * Retrofitted tests for the {@link MLAnalayser} class. These + * should not be considered comprehensive, but are here to aid + * refactoring during move to SOLR4. + * + * @author Matt Ward + */ +@RunWith(MockitoJUnitRunner.class) +public class MLAnalayserTest +{ + private static final String PROPERTY_NAME = "@{http://www.alfresco.org/model/content/1.0}propertyName"; + private MLAnalayser analyser; + private @Mock DictionaryService dictionaryService; + private MLAnalysisMode mlAnalaysisMode = MLAnalysisMode.EXACT_COUNRTY; + + + @Before + public void setUp() throws Exception + { + analyser = new MLAnalayser(mlAnalaysisMode); + PropertyDefinition propDef = Mockito.mock(PropertyDefinition.class); + when(dictionaryService.getProperty(any(QName.class))).thenReturn(propDef); + } + + @Ignore + @Test + public void testTokenStreamForLanguageAndCountry() throws IOException + { + final String inputStr = "\u0000fr_FR\u0000Ceci n'est pas Française"; + final Reader reader = new StringReader(inputStr); + + List expectedTokens = new ArrayList<>(); + expectedTokens.add("{fr_FR}ceci"); + expectedTokens.add("{fr_FR}n'est"); + expectedTokens.add("{fr_FR}pas"); + expectedTokens.add("{fr_FR}francaise"); // normalised 'c'. + + TokenStream ts = analyser.tokenStream(PROPERTY_NAME, reader); + verifyTokenStream(ts, expectedTokens); + } + + @Ignore + @Test + public void testTokenStreamForLanguage() throws IOException + { + final String inputStr = "\u0000fr\u0000Ceci n'est pas Française"; + final Reader reader = new StringReader(inputStr); + + List expectedTokens = new ArrayList<>(); + expectedTokens.add("{fr}ceci"); + expectedTokens.add("{fr_CH}ceci"); + expectedTokens.add("{fr_LU}ceci"); + expectedTokens.add("{fr_FR}ceci"); + expectedTokens.add("{fr_BE}ceci"); + expectedTokens.add("{fr_CA}ceci"); + + expectedTokens.add("{fr}n'est"); + expectedTokens.add("{fr_CH}n'est"); + expectedTokens.add("{fr_LU}n'est"); + expectedTokens.add("{fr_FR}n'est"); + expectedTokens.add("{fr_BE}n'est"); + expectedTokens.add("{fr_CA}n'est"); + + expectedTokens.add("{fr}pas"); + expectedTokens.add("{fr_CH}pas"); + expectedTokens.add("{fr_LU}pas"); + expectedTokens.add("{fr_FR}pas"); + expectedTokens.add("{fr_BE}pas"); + expectedTokens.add("{fr_CA}pas"); + + expectedTokens.add("{fr}francaise"); + expectedTokens.add("{fr_CH}francaise"); + expectedTokens.add("{fr_LU}francaise"); + expectedTokens.add("{fr_FR}francaise"); + expectedTokens.add("{fr_BE}francaise"); + expectedTokens.add("{fr_CA}francaise"); + + TokenStream ts = analyser.tokenStream(PROPERTY_NAME, reader); + verifyTokenStream(ts, expectedTokens); + } + + /** + * Check that the TokenStream yields the exact tokens specified. + * Note that order is not checked, since the map of locales will not provide a + * predictable ordering when enumerated. + * + * The expected list of tokens may contain the same token more than once and + * the number of instances will have to match the number found in the stream. + * + * @param ts TokenStream to inspect. + * @param expectedTokens List of tokens in the order expected from the stream. + * @throws IOException + */ + private void verifyTokenStream(TokenStream ts, List expectedTokens) throws IOException + { + final int expectedCount = expectedTokens.size(); + int count = 0; + + CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); + + try + { + ts.reset(); + while (ts.incrementToken()) + { + count++; + System.out.println("Token: " + termAtt.toString()); + if (expectedTokens.contains(termAtt.toString())) + { + // remove an instance of the term text so that it is not matched again + expectedTokens.remove(termAtt.toString()); + } + else + { + fail("Unexpected token: " + termAtt.toString()); + } + } + ts.end(); + } + finally + { + ts.close(); + } + + assertEquals("Incorrect number of tokens generated.", expectedCount, count); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilterTest.java index 9c7350aab..a9d1aeaa2 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/repo/search/impl/lucene/analysis/PathTokenFilterTest.java @@ -1,220 +1,220 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.impl.lucene.analysis; - -import java.io.IOException; -import java.io.StringReader; - -import junit.framework.TestCase; - -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TypeAttribute; - -public class PathTokenFilterTest extends TestCase -{ - - public PathTokenFilterTest() - { - super(); - } - - public PathTokenFilterTest(String arg0) - { - super(arg0); - } - - - public void testFullPath() throws IOException - { - tokenise("{uri1}one", new String[]{"uri1", "one"}); - tokenise("/{uri1}one", new String[]{"uri1", "one"}); - tokenise("{uri1}one/{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("/{uri1}one/{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("{uri1}one/{uri2}two/{uri3}three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - tokenise("/{uri1}one/{uri2}two/{uri3}three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - try - { - tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - } - catch(IllegalStateException ise) - { - - } - - } - - - public void testPrefixPath() throws IOException - { - tokenise("uri1:one", new String[]{"uri1", "one"}); - tokenise("/uri1:one", new String[]{"uri1", "one"}); - tokenise("uri1:one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("/uri1:one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("uri1:one/uri2:two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - tokenise("/uri1:one/uri2:two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - try - { - tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - } - catch(IllegalStateException ise) - { - - } - - } - - - public void testMixedPath() throws IOException - { - - tokenise("{uri1}one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("/{uri1}one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); - tokenise("uri1:one/{uri2}two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - tokenise("/uri1:one/{uri2}two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); - try - { - tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); - } - catch(IllegalStateException ise) - { - - } - - } - - public void testTokenizerReuse() throws IOException - { - // We should be able to use the same Tokenizer twice. - final String path = "uri1:one"; - StringReader reader = new StringReader(path); - PathTokenFilter ts = new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, - PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, - PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); - ts.setReader(reader); - - OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class); - - // First use - tokenise(ts, new String[]{"uri1", "one"}); - assertEquals(path.length(), offsetAtt.startOffset()); - assertEquals(path.length(), offsetAtt.endOffset()); - - // Second use - final String path2 = "/{uri1}one/uri2:two/"; - StringReader reader2 = new StringReader(path2); - ts.setReader(reader2); - tokenise(ts, new String[]{"uri1", "one", "uri2", "two"}); - assertEquals(path2.length(), offsetAtt.startOffset()); - assertEquals(path2.length(), offsetAtt.endOffset()); - } - - - public void testAttributesAfterStreamEnd() throws IOException - { - final String path = "uri1:one"; - StringReader reader = new StringReader(path); - PathTokenFilter ts = new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, - PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, - PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); - ts.setReader(reader); - - CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); - TypeAttribute typeAtt = ts.addAttribute(TypeAttribute.class); - OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class); - PositionIncrementAttribute posIncAtt = ts.addAttribute(PositionIncrementAttribute.class); - - // PathTokenFilter.end() will be called after all tokens consumed. - tokenise(ts, new String[]{"uri1", "one"}); - - // Check attributes cleaned up - assertEquals("", termAtt.toString()); - assertEquals("word", typeAtt.type()); // the default - assertEquals(0, posIncAtt.getPositionIncrement()); - // Final offset... - assertEquals(path.length(), offsetAtt.startOffset()); - assertEquals(path.length(), offsetAtt.endOffset()); - } - - private void tokenise(String path, String[] tokens) throws IOException - { - StringReader reader = new StringReader(path); - PathTokenFilter ts = new PathTokenFilter( PathTokenFilter.PATH_SEPARATOR, - PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, - PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); - ts.setReader(reader); - - tokenise(ts, tokens); - } - - private void tokenise(TokenStream ts, String[] tokens) throws IOException - { - int i = 0; - - CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); - TypeAttribute typeAtt = ts.addAttribute(TypeAttribute.class); - - try - { - ts.reset(); - while (ts.incrementToken()) - { - System.out.println("token: " + ts.reflectAsString(true)); - - String termText = termAtt.toString(); - - if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAMESPACE)) - { - assert(i % 2 == 0); - assertEquals(termText, tokens[i++]); - } - else if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX)) - { - assert(i % 2 == 0); - assertEquals(termText, tokens[i++]); - } - else if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAME)) - { - assert(i % 2 == 1); - assertEquals(termText, tokens[i++]); - } - } - ts.end(); - } - finally - { - ts.close(); - } - - if(i != tokens.length) - { - fail("Invalid number of tokens, found "+i+" and expected "+tokens.length); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.impl.lucene.analysis; + +import java.io.IOException; +import java.io.StringReader; + +import junit.framework.TestCase; + +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.analysis.tokenattributes.TypeAttribute; + +public class PathTokenFilterTest extends TestCase +{ + + public PathTokenFilterTest() + { + super(); + } + + public PathTokenFilterTest(String arg0) + { + super(arg0); + } + + + public void testFullPath() throws IOException + { + tokenise("{uri1}one", new String[]{"uri1", "one"}); + tokenise("/{uri1}one", new String[]{"uri1", "one"}); + tokenise("{uri1}one/{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("/{uri1}one/{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("{uri1}one/{uri2}two/{uri3}three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + tokenise("/{uri1}one/{uri2}two/{uri3}three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + try + { + tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + } + catch(IllegalStateException ise) + { + + } + + } + + + public void testPrefixPath() throws IOException + { + tokenise("uri1:one", new String[]{"uri1", "one"}); + tokenise("/uri1:one", new String[]{"uri1", "one"}); + tokenise("uri1:one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("/uri1:one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("uri1:one/uri2:two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + tokenise("/uri1:one/uri2:two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + try + { + tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + } + catch(IllegalStateException ise) + { + + } + + } + + + public void testMixedPath() throws IOException + { + + tokenise("{uri1}one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("/{uri1}one/uri2:two/", new String[]{"uri1", "one", "uri2", "two"}); + tokenise("uri1:one/{uri2}two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + tokenise("/uri1:one/{uri2}two/uri3:three", new String[]{"uri1", "one", "uri2", "two", "uri3", "three"}); + try + { + tokenise("{uri1}one;{uri2}two/", new String[]{"uri1", "one", "uri2", "two"}); + } + catch(IllegalStateException ise) + { + + } + + } + + public void testTokenizerReuse() throws IOException + { + // We should be able to use the same Tokenizer twice. + final String path = "uri1:one"; + StringReader reader = new StringReader(path); + PathTokenFilter ts = new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, + PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, + PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); + ts.setReader(reader); + + OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class); + + // First use + tokenise(ts, new String[]{"uri1", "one"}); + assertEquals(path.length(), offsetAtt.startOffset()); + assertEquals(path.length(), offsetAtt.endOffset()); + + // Second use + final String path2 = "/{uri1}one/uri2:two/"; + StringReader reader2 = new StringReader(path2); + ts.setReader(reader2); + tokenise(ts, new String[]{"uri1", "one", "uri2", "two"}); + assertEquals(path2.length(), offsetAtt.startOffset()); + assertEquals(path2.length(), offsetAtt.endOffset()); + } + + + public void testAttributesAfterStreamEnd() throws IOException + { + final String path = "uri1:one"; + StringReader reader = new StringReader(path); + PathTokenFilter ts = new PathTokenFilter(PathTokenFilter.PATH_SEPARATOR, + PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, + PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); + ts.setReader(reader); + + CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); + TypeAttribute typeAtt = ts.addAttribute(TypeAttribute.class); + OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class); + PositionIncrementAttribute posIncAtt = ts.addAttribute(PositionIncrementAttribute.class); + + // PathTokenFilter.end() will be called after all tokens consumed. + tokenise(ts, new String[]{"uri1", "one"}); + + // Check attributes cleaned up + assertEquals("", termAtt.toString()); + assertEquals("word", typeAtt.type()); // the default + assertEquals(0, posIncAtt.getPositionIncrement()); + // Final offset... + assertEquals(path.length(), offsetAtt.startOffset()); + assertEquals(path.length(), offsetAtt.endOffset()); + } + + private void tokenise(String path, String[] tokens) throws IOException + { + StringReader reader = new StringReader(path); + PathTokenFilter ts = new PathTokenFilter( PathTokenFilter.PATH_SEPARATOR, + PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT, + PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true); + ts.setReader(reader); + + tokenise(ts, tokens); + } + + private void tokenise(TokenStream ts, String[] tokens) throws IOException + { + int i = 0; + + CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class); + TypeAttribute typeAtt = ts.addAttribute(TypeAttribute.class); + + try + { + ts.reset(); + while (ts.incrementToken()) + { + System.out.println("token: " + ts.reflectAsString(true)); + + String termText = termAtt.toString(); + + if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAMESPACE)) + { + assert(i % 2 == 0); + assertEquals(termText, tokens[i++]); + } + else if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAMESPACE_PREFIX)) + { + assert(i % 2 == 0); + assertEquals(termText, tokens[i++]); + } + else if(typeAtt.type().equals(PathTokenFilter.TOKEN_TYPE_PATH_ELEMENT_NAME)) + { + assert(i % 2 == 1); + assertEquals(termText, tokens[i++]); + } + } + ts.end(); + } + finally + { + ts.close(); + } + + if(i != tokens.length) + { + fail("Invalid number of tokens, found "+i+" and expected "+tokens.length); + } + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoDistributedIT.java index c5d83b196..552e2416a 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -899,4 +899,4 @@ public abstract class AbstractAlfrescoDistributedIT extends SolrITInitializer // Nothing to be done here } } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoSolrIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoSolrIT.java index 51e1587a4..d73d2638b 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoSolrIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AbstractAlfrescoSolrIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerDistributedIT.java index 98819e172..d157e14b8 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -92,4 +92,4 @@ public class AdminHandlerDistributedIT extends AbstractAlfrescoDistributedIT props = testingCore.getCoreDescriptor().getSubstitutableProperties(); assertEquals(ACL_ID.toString(), props.get("shard.method")); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerIT.java index b86619db5..56887c032 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AdminHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldTypeTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldTypeTest.java index 1d9a92883..cd8470d82 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldTypeTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableMLTextFieldTypeTest.java @@ -1,208 +1,208 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import java.text.Collator; -import java.util.Locale; - -import org.alfresco.solr.AlfrescoCollatableMLTextFieldType.MLTextSortFieldComparator; -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -/** Unit tests for {@link AlfrescoCollatableMLTextFieldType}. */ -public class AlfrescoCollatableMLTextFieldTypeTest -{ - private static final int NUM_HITS = 3; - private static final String FIELD = "field"; - private static final Locale LOCALE = Locale.getDefault(); - /** A document id. */ - private static final int DOC = 0; - /** A value for the current bottom document. */ - private static final String BOTTOM_STRING = "Bottom"; - - @InjectMocks - MLTextSortFieldComparator textSortFieldComparator = new MLTextSortFieldComparator(NUM_HITS, FIELD, LOCALE); - @Mock - BinaryDocValues mockDocTerms; - @Mock - Bits mockDocsWithField; - @Mock - Collator mockCollator; - - @Before - public void setUp() - { - initMocks(this); - reset(mockDocTerms, mockDocsWithField); - textSortFieldComparator.bottom = BOTTOM_STRING; - } - - /** Check that a zero length term is sorted before a populated field. */ - @Test - public void testCompareBottom_termLengthZeroAndDocDoesntHaveField() - { - // Set up the document to have an empty term. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - int result = textSortFieldComparator.compareBottom(DOC); - - assertEquals("Expected value for doc to be null, and so it to be sorted before BOTTOM_TERM", 1, result); - } - - /** - * Check the behaviour of compareBottom when docsWithField is null (this happens when all documents contain the - * field). - */ - @Test - public void testCompareBottom_nullDocsWithField() - { - // Set docsWithField to null to simulate all documents containing the field. - Bits oldValue = textSortFieldComparator.docsWithField; - textSortFieldComparator.docsWithField = null; - - // Set up the document to have an empty term. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - // Expect the EMPTY_TERM to be compared - verify(mockCollator).compare(BOTTOM_STRING, ""); - - // Reset docsWithField with the mock after the test. - textSortFieldComparator.docsWithField = oldValue; - } - - /** Check that if the doc has a value then it is compared with the existing value. */ - @Test - public void testCompareBottom_populatedTerm() - { - // Set up the document to have "Some value" for the field. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("Some value")); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - verify(mockCollator).compare(BOTTOM_STRING, "Some value"); - } - - /** Check the behaviour if the multilanguage term is encoded. */ - @Test - public void testCompareBottom_encodedTerm_localeFound() - { - // Create an encoded multilanguage string with Russian, US English and Thai with Thai digits. - String mlText = "\u0000ru\u0000First\u0000Ignored" + - "\u0000en_US\u0000Second\u0000IgnoredToo" + - "\u0000th_TH_TH\u0000Third\u0000AlsoIgnored"; - // Set up the document to have an encoded value for the field. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef(mlText)); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Check that the Russian text can be extracted. - textSortFieldComparator.collatorLocale = Locale.forLanguageTag("ru"); - textSortFieldComparator.compareBottom(DOC); - verify(mockCollator).compare(BOTTOM_STRING, "First"); - - // Check that the English text can be extracted. - textSortFieldComparator.collatorLocale = Locale.forLanguageTag("en"); - textSortFieldComparator.compareBottom(DOC); - verify(mockCollator).compare(BOTTOM_STRING, "Second"); - - // Check that the Thai text can be extracted. - textSortFieldComparator.collatorLocale = Locale.forLanguageTag("th"); - textSortFieldComparator.compareBottom(DOC); - verify(mockCollator).compare(BOTTOM_STRING, "Third"); - - // Reset the locale for other tests. - textSortFieldComparator.collatorLocale = LOCALE; - } - - /** Check the behaviour if the term has a locale but no text. */ - @Test - public void testCompareBottom_badlyEncodedTerm() - { - // Set the value to have a locale but no text. - String mlText = "\u0000ru"; - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef(mlText)); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - // Check that an empty string is assumed. - verify(mockCollator).compare(BOTTOM_STRING, ""); - } - - @Test - public void testCompareValues_nullLessThanString() - { - int result = textSortFieldComparator.compareValues(null, "NotNull"); - assertEquals("Expected null to be 'less' than string.", -1, result); - } - - @Test - public void testCompareValues_stringGreaterThanNull() - { - int result = textSortFieldComparator.compareValues("NotNull", null); - assertEquals("Expected string to be 'greater' than null.", 1, result); - } - - @Test - public void testCompareValues_nullEqualToNull() - { - int result = textSortFieldComparator.compareValues(null, null); - assertEquals("Expected two null values to be equal.", 0, result); - } - - /** Check that when two non-null strings are compared then the underlying collator is used to get the result. */ - @Test - public void testCompareValues_twoStringsCompared() - { - // An arbitrary value to be returned by the collator. - int comparisonResult = 10; - when(mockCollator.compare("NotNull1", "NotNull2")).thenReturn(comparisonResult); - - // Call the method under test. - int result = textSortFieldComparator.compareValues("NotNull1", "NotNull2"); - - verify(mockCollator).compare("NotNull1", "NotNull2"); - assertEquals("Expected result to be obtained from collator.", comparisonResult, result); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.text.Collator; +import java.util.Locale; + +import org.alfresco.solr.AlfrescoCollatableMLTextFieldType.MLTextSortFieldComparator; +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** Unit tests for {@link AlfrescoCollatableMLTextFieldType}. */ +public class AlfrescoCollatableMLTextFieldTypeTest +{ + private static final int NUM_HITS = 3; + private static final String FIELD = "field"; + private static final Locale LOCALE = Locale.getDefault(); + /** A document id. */ + private static final int DOC = 0; + /** A value for the current bottom document. */ + private static final String BOTTOM_STRING = "Bottom"; + + @InjectMocks + MLTextSortFieldComparator textSortFieldComparator = new MLTextSortFieldComparator(NUM_HITS, FIELD, LOCALE); + @Mock + BinaryDocValues mockDocTerms; + @Mock + Bits mockDocsWithField; + @Mock + Collator mockCollator; + + @Before + public void setUp() + { + initMocks(this); + reset(mockDocTerms, mockDocsWithField); + textSortFieldComparator.bottom = BOTTOM_STRING; + } + + /** Check that a zero length term is sorted before a populated field. */ + @Test + public void testCompareBottom_termLengthZeroAndDocDoesntHaveField() + { + // Set up the document to have an empty term. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + int result = textSortFieldComparator.compareBottom(DOC); + + assertEquals("Expected value for doc to be null, and so it to be sorted before BOTTOM_TERM", 1, result); + } + + /** + * Check the behaviour of compareBottom when docsWithField is null (this happens when all documents contain the + * field). + */ + @Test + public void testCompareBottom_nullDocsWithField() + { + // Set docsWithField to null to simulate all documents containing the field. + Bits oldValue = textSortFieldComparator.docsWithField; + textSortFieldComparator.docsWithField = null; + + // Set up the document to have an empty term. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + // Expect the EMPTY_TERM to be compared + verify(mockCollator).compare(BOTTOM_STRING, ""); + + // Reset docsWithField with the mock after the test. + textSortFieldComparator.docsWithField = oldValue; + } + + /** Check that if the doc has a value then it is compared with the existing value. */ + @Test + public void testCompareBottom_populatedTerm() + { + // Set up the document to have "Some value" for the field. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("Some value")); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + verify(mockCollator).compare(BOTTOM_STRING, "Some value"); + } + + /** Check the behaviour if the multilanguage term is encoded. */ + @Test + public void testCompareBottom_encodedTerm_localeFound() + { + // Create an encoded multilanguage string with Russian, US English and Thai with Thai digits. + String mlText = "\u0000ru\u0000First\u0000Ignored" + + "\u0000en_US\u0000Second\u0000IgnoredToo" + + "\u0000th_TH_TH\u0000Third\u0000AlsoIgnored"; + // Set up the document to have an encoded value for the field. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef(mlText)); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Check that the Russian text can be extracted. + textSortFieldComparator.collatorLocale = Locale.forLanguageTag("ru"); + textSortFieldComparator.compareBottom(DOC); + verify(mockCollator).compare(BOTTOM_STRING, "First"); + + // Check that the English text can be extracted. + textSortFieldComparator.collatorLocale = Locale.forLanguageTag("en"); + textSortFieldComparator.compareBottom(DOC); + verify(mockCollator).compare(BOTTOM_STRING, "Second"); + + // Check that the Thai text can be extracted. + textSortFieldComparator.collatorLocale = Locale.forLanguageTag("th"); + textSortFieldComparator.compareBottom(DOC); + verify(mockCollator).compare(BOTTOM_STRING, "Third"); + + // Reset the locale for other tests. + textSortFieldComparator.collatorLocale = LOCALE; + } + + /** Check the behaviour if the term has a locale but no text. */ + @Test + public void testCompareBottom_badlyEncodedTerm() + { + // Set the value to have a locale but no text. + String mlText = "\u0000ru"; + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef(mlText)); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + // Check that an empty string is assumed. + verify(mockCollator).compare(BOTTOM_STRING, ""); + } + + @Test + public void testCompareValues_nullLessThanString() + { + int result = textSortFieldComparator.compareValues(null, "NotNull"); + assertEquals("Expected null to be 'less' than string.", -1, result); + } + + @Test + public void testCompareValues_stringGreaterThanNull() + { + int result = textSortFieldComparator.compareValues("NotNull", null); + assertEquals("Expected string to be 'greater' than null.", 1, result); + } + + @Test + public void testCompareValues_nullEqualToNull() + { + int result = textSortFieldComparator.compareValues(null, null); + assertEquals("Expected two null values to be equal.", 0, result); + } + + /** Check that when two non-null strings are compared then the underlying collator is used to get the result. */ + @Test + public void testCompareValues_twoStringsCompared() + { + // An arbitrary value to be returned by the collator. + int comparisonResult = 10; + when(mockCollator.compare("NotNull1", "NotNull2")).thenReturn(comparisonResult); + + // Call the method under test. + int result = textSortFieldComparator.compareValues("NotNull1", "NotNull2"); + + verify(mockCollator).compare("NotNull1", "NotNull2"); + assertEquals("Expected result to be obtained from collator.", comparisonResult, result); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableTextFieldTypeTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableTextFieldTypeTest.java index 5093889e7..d3424bb5a 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableTextFieldTypeTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCollatableTextFieldTypeTest.java @@ -1,176 +1,176 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import java.text.Collator; -import java.util.Locale; - -import org.alfresco.solr.AlfrescoCollatableTextFieldType.TextSortFieldComparator; -import org.apache.lucene.index.BinaryDocValues; -import org.apache.lucene.util.Bits; -import org.apache.lucene.util.BytesRef; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -/** Unit tests for {@link AlfrescoCollatableTextFieldType}. */ -public class AlfrescoCollatableTextFieldTypeTest -{ - private static final int NUM_HITS = 3; - private static final String FIELD = "field"; - private static final Locale LOCALE = Locale.getDefault(); - /** A document id. */ - private static final int DOC = 0; - /** A value for the current bottom document. */ - private static final String BOTTOM_STRING = "Bottom"; - - @InjectMocks - TextSortFieldComparator textSortFieldComparator = new TextSortFieldComparator(NUM_HITS, FIELD, LOCALE); - @Mock - BinaryDocValues mockDocTerms; - @Mock - Bits mockDocsWithField; - @Mock - Collator mockCollator; - - @Before - public void setUp() - { - initMocks(this); - reset(mockDocTerms, mockDocsWithField); - textSortFieldComparator.bottom = BOTTOM_STRING; - } - - /** Check that a zero length term is sorted before a populated field. */ - @Test - public void testCompareBottom_termLengthZeroAndDocDoesntHaveField() - { - // Set up the document to have an empty term. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - int result = textSortFieldComparator.compareBottom(DOC); - - assertEquals("Expected value for doc to be null, and so it to be sorted before BOTTOM_TERM", 1, result); - } - - /** - * Check the behaviour of compareBottom when docsWithField is null (this happens when all documents contain the - * field). - */ - @Test - public void testCompareBottom_nullDocsWithField() - { - // Set docsWithField to null to simulate all documents containing the field. - Bits oldValue = textSortFieldComparator.docsWithField; - textSortFieldComparator.docsWithField = null; - - // Set up the document to have an empty term. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - // Expect the EMPTY_TERM to be compared - verify(mockCollator).compare(BOTTOM_STRING, ""); - - // Reset docsWithField with the mock after the test. - textSortFieldComparator.docsWithField = oldValue; - } - - /** Check that if the doc has a value then it is compared with the existing value. */ - @Test - public void testCompareBottom_populatedTerm() - { - // Set up the document to have "Some value" for the field. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("Some value")); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - verify(mockCollator).compare(BOTTOM_STRING, "Some value"); - } - - /** Check the behaviour if the term is encoded. */ - @Test - public void testCompareBottom_encodedTerm() - { - // Set up the document to have an encoded value for the field. - when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("\u0000Ignored\u0000Value")); - when(mockDocsWithField.get(DOC)).thenReturn(false); - - // Call the method under test. - textSortFieldComparator.compareBottom(DOC); - - verify(mockCollator).compare(BOTTOM_STRING, "Value"); - } - - @Test - public void testCompareValues_nullLessThanString() - { - int result = textSortFieldComparator.compareValues(null, "NotNull"); - assertEquals("Expected null to be 'less' than string.", -1, result); - } - - @Test - public void testCompareValues_stringGreaterThanNull() - { - int result = textSortFieldComparator.compareValues("NotNull", null); - assertEquals("Expected string to be 'greater' than null.", 1, result); - } - - @Test - public void testCompareValues_nullEqualToNull() - { - int result = textSortFieldComparator.compareValues(null, null); - assertEquals("Expected two null values to be equal.", 0, result); - } - - /** Check that when two non-null strings are compared then the underlying collator is used to get the result. */ - @Test - public void testCompareValues_twoStringsCompared() - { - // An arbitrary value to be returned by the collator. - int comparisonResult = 10; - when(mockCollator.compare("NotNull1", "NotNull2")).thenReturn(comparisonResult); - - // Call the method under test. - int result = textSortFieldComparator.compareValues("NotNull1", "NotNull2"); - - verify(mockCollator).compare("NotNull1", "NotNull2"); - assertEquals("Expected result to be obtained from collator.", comparisonResult, result); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.text.Collator; +import java.util.Locale; + +import org.alfresco.solr.AlfrescoCollatableTextFieldType.TextSortFieldComparator; +import org.apache.lucene.index.BinaryDocValues; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** Unit tests for {@link AlfrescoCollatableTextFieldType}. */ +public class AlfrescoCollatableTextFieldTypeTest +{ + private static final int NUM_HITS = 3; + private static final String FIELD = "field"; + private static final Locale LOCALE = Locale.getDefault(); + /** A document id. */ + private static final int DOC = 0; + /** A value for the current bottom document. */ + private static final String BOTTOM_STRING = "Bottom"; + + @InjectMocks + TextSortFieldComparator textSortFieldComparator = new TextSortFieldComparator(NUM_HITS, FIELD, LOCALE); + @Mock + BinaryDocValues mockDocTerms; + @Mock + Bits mockDocsWithField; + @Mock + Collator mockCollator; + + @Before + public void setUp() + { + initMocks(this); + reset(mockDocTerms, mockDocsWithField); + textSortFieldComparator.bottom = BOTTOM_STRING; + } + + /** Check that a zero length term is sorted before a populated field. */ + @Test + public void testCompareBottom_termLengthZeroAndDocDoesntHaveField() + { + // Set up the document to have an empty term. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + int result = textSortFieldComparator.compareBottom(DOC); + + assertEquals("Expected value for doc to be null, and so it to be sorted before BOTTOM_TERM", 1, result); + } + + /** + * Check the behaviour of compareBottom when docsWithField is null (this happens when all documents contain the + * field). + */ + @Test + public void testCompareBottom_nullDocsWithField() + { + // Set docsWithField to null to simulate all documents containing the field. + Bits oldValue = textSortFieldComparator.docsWithField; + textSortFieldComparator.docsWithField = null; + + // Set up the document to have an empty term. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef()); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + // Expect the EMPTY_TERM to be compared + verify(mockCollator).compare(BOTTOM_STRING, ""); + + // Reset docsWithField with the mock after the test. + textSortFieldComparator.docsWithField = oldValue; + } + + /** Check that if the doc has a value then it is compared with the existing value. */ + @Test + public void testCompareBottom_populatedTerm() + { + // Set up the document to have "Some value" for the field. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("Some value")); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + verify(mockCollator).compare(BOTTOM_STRING, "Some value"); + } + + /** Check the behaviour if the term is encoded. */ + @Test + public void testCompareBottom_encodedTerm() + { + // Set up the document to have an encoded value for the field. + when(mockDocTerms.get(DOC)).thenReturn(new BytesRef("\u0000Ignored\u0000Value")); + when(mockDocsWithField.get(DOC)).thenReturn(false); + + // Call the method under test. + textSortFieldComparator.compareBottom(DOC); + + verify(mockCollator).compare(BOTTOM_STRING, "Value"); + } + + @Test + public void testCompareValues_nullLessThanString() + { + int result = textSortFieldComparator.compareValues(null, "NotNull"); + assertEquals("Expected null to be 'less' than string.", -1, result); + } + + @Test + public void testCompareValues_stringGreaterThanNull() + { + int result = textSortFieldComparator.compareValues("NotNull", null); + assertEquals("Expected string to be 'greater' than null.", 1, result); + } + + @Test + public void testCompareValues_nullEqualToNull() + { + int result = textSortFieldComparator.compareValues(null, null); + assertEquals("Expected two null values to be equal.", 0, result); + } + + /** Check that when two non-null strings are compared then the underlying collator is used to get the result. */ + @Test + public void testCompareValues_twoStringsCompared() + { + // An arbitrary value to be returned by the collator. + int comparisonResult = 10; + when(mockCollator.compare("NotNull1", "NotNull2")).thenReturn(comparisonResult); + + // Call the method under test. + int result = textSortFieldComparator.compareValues("NotNull1", "NotNull2"); + + verify(mockCollator).compare("NotNull1", "NotNull2"); + assertEquals("Expected result to be obtained from collator.", comparisonResult, result); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java index c9018c8db..d380eeeb2 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -426,4 +426,4 @@ public class AlfrescoCoreAdminHandlerIT verify(spy).newCore(eq(ALFRESCO_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ALFRESCO_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); verify(spy).newCore(eq(VERSION_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(VERSION_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerTest.java index 6982c4db9..4eb97a2e5 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -1158,4 +1158,4 @@ public class AlfrescoCoreAdminHandlerTest NamedList actionResponse = admin.actionFIX(params); assertEquals(parameterValue, actionResponse.get(parameterName)); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoIndexingStatePersistenceAcrossReloadsIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoIndexingStatePersistenceAcrossReloadsIT.java index e3e98f412..6a7f8c9b6 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoIndexingStatePersistenceAcrossReloadsIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoIndexingStatePersistenceAcrossReloadsIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -177,4 +177,4 @@ public class AlfrescoIndexingStatePersistenceAcrossReloadsIT extends AbstractAlf { return admin.getScheduler().getJobsCount(); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoJsonQueryRequest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoJsonQueryRequest.java index fe79c42aa..9dea4b66b 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoJsonQueryRequest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoJsonQueryRequest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrConstants.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrConstants.java index 9ccc2d3b4..fc745aebe 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrConstants.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrConstants.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -78,4 +78,4 @@ public interface AlfrescoSolrConstants QName MULTIPLE_DATE = QName.createQName(CMIS_TEST_NAMESPACE, "multipleDate"); QName SINGLE_DATETIME = QName.createQName(CMIS_TEST_NAMESPACE, "singleDatetime"); QName MULTIPLE_DATETIME = QName.createQName(CMIS_TEST_NAMESPACE, "multipleDatetime"); -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrReloadIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrReloadIT.java index 241516e3b..0b13baafa 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrReloadIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrReloadIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -153,4 +153,4 @@ public class AlfrescoSolrReloadIT extends AbstractAlfrescoSolrIT { return admin.getScheduler().getJobsCount(); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java index 02b4f7bac..30eb6e3a6 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -968,4 +968,4 @@ public class AlfrescoSolrUtils .map(AlfrescoCoreAdminHandler.class::cast) .orElseThrow(() -> new IllegalStateException("Cannot retrieve the Core Admin Handler on this test core.")); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoTrackerRegistrationIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoTrackerRegistrationIT.java index 9232488f4..ef84250d3 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoTrackerRegistrationIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoTrackerRegistrationIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CMISDataCreatorTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CMISDataCreatorTest.java index 74bf1eac7..77278b0d3 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CMISDataCreatorTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CMISDataCreatorTest.java @@ -1,450 +1,450 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import java.io.ByteArrayInputStream; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import junit.framework.TestCase; - -import org.alfresco.util.GUID; -import org.alfresco.util.ISO8601DateFormat; -import org.apache.chemistry.opencmis.client.api.CmisObject; -import org.apache.chemistry.opencmis.client.api.Document; -import org.apache.chemistry.opencmis.client.api.Folder; -import org.apache.chemistry.opencmis.client.api.ItemIterable; -import org.apache.chemistry.opencmis.client.api.ObjectId; -import org.apache.chemistry.opencmis.client.api.QueryResult; -import org.apache.chemistry.opencmis.client.api.Repository; -import org.apache.chemistry.opencmis.client.api.Session; -import org.apache.chemistry.opencmis.client.api.SessionFactory; -import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl; -import org.apache.chemistry.opencmis.commons.BasicPermissions; -import org.apache.chemistry.opencmis.commons.PropertyIds; -import org.apache.chemistry.opencmis.commons.SessionParameter; -import org.apache.chemistry.opencmis.commons.data.Ace; -import org.apache.chemistry.opencmis.commons.enums.AclPropagation; -import org.apache.chemistry.opencmis.commons.enums.BindingType; -import org.apache.chemistry.opencmis.commons.enums.VersioningState; -import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlEntryImpl; -import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl; -import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; - -/** - * @author Andy - * - */ -public class CMISDataCreatorTest extends TestCase -{ - private static Session getSession(String user, String pwd) - { - String url = "http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/atom"; - - SessionFactory factory = SessionFactoryImpl.newInstance(); - Map parameter = new HashMap(); - parameter.put(SessionParameter.USER, user); - parameter.put(SessionParameter.PASSWORD, pwd); - parameter.put(SessionParameter.ATOMPUB_URL, url); - parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value()); - - List repositories = factory.getRepositories(parameter); - Session session = repositories.get(0).createSession(); - - return session; - } - - private String getRootFolderName() - { - return "CMIS-Data-Creator-Test-"+System.currentTimeMillis(); - } - - private String getUniqueName() - { - return "CMIS-Data-Creator-Test-"+GUID.generate(); - } - - public void testCreate() - { - Session session = getSession("admin", "admin"); - - String folderName = getRootFolderName(); - Folder root = session.getRootFolder(); - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderName); - - // create the folder - Folder newFolder = root.createFolder(properties); - - for(int i = 0; i < 50; i++) - { - AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl("user"+i); - List permissions = new ArrayList(1); - permissions.add(BasicPermissions.READ); - List addAces = new ArrayList(1); - addAces.add(new AccessControlEntryImpl(principal, permissions)); - newFolder.addAcl(addAces, AclPropagation.PROPAGATE); - - Map updateProperties = new HashMap(); - updateProperties.put("cm:title", "Update title "+i); - newFolder.updateProperties(properties); - - if(i % 10 == 0) - { - System.out.println("@ "+i); - } - } - ItemIterable result = session.query("select * from cmis:folder", false); - assertTrue(result.getTotalNumItems() > 0); - - result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - assertTrue(result.getTotalNumItems() > 0); - - } - - public void testCreateLots() throws Exception - { - Session session = getSession("admin", "admin"); - - Folder root = session.getRootFolder(); - String folderNameBase = getRootFolderName(); - - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderNameBase); - - Folder base = root.createFolder(properties); - for(int i = 0; i < 10; i++) - { - AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl(""+i+i+i); - List permissions = new ArrayList(1); - permissions.add(BasicPermissions.ALL); - List addAces = new ArrayList(1); - addAces.add(new AccessControlEntryImpl(principal, permissions)); - base.addAcl(addAces, AclPropagation.PROPAGATE); - } - - - Thread last = null; - - for(int i = 0; i < 10; i++) - { - Creator creator = new Creator(base.getPath(), i); - Thread thread = new Thread(creator); - thread.start(); - last = thread; - } - - if(last != null) - { - last.join(); - } - - ItemIterable result = session.query("select * from cmis:folder", false); - assertTrue(result.getTotalNumItems() > 0); - - //result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - //assertTrue(result.getTotalNumItems() > 0); - - } - - private static class Creator implements Runnable - { - int id; - - String path; - - Creator(String path, int id) - { - this.path = path; - this.id = id; - } - - /* (non-Javadoc) - * @see java.lang.Runnable#run() - */ - @Override - public void run() - { - Session session = getSession(""+id+id+id, ""+id+id+id); - - CmisObject object = session.getObjectByPath(path); - - Map baseProps = new HashMap(); - baseProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - baseProps.put(PropertyIds.NAME, "Thread"+id); - - - ObjectId base = session.createFolder(baseProps, object); - - for(int i = 0; i < 100; i++) - { - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, "Folder-"+i); - - ObjectId folder = session.createFolder(properties, base); - - System.out.println("Thread "+id +" @Folder "+i); - - for(int j = 0; j < 1000; j++) - { - Map folderProps = new HashMap(); - folderProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - folderProps.put(PropertyIds.NAME, "Folder-"+i); - - ObjectId child = session.createFolder(folderProps, folder); - - -// Map docProps = new HashMap(); -// docProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); -// docProps.put(PropertyIds.NAME, "Doc-"+j); -// -// ContentStreamImpl contentSream = new ContentStreamImpl(); -// contentSream.setFileName(GUID.generate()); -// contentSream.setLength(BigInteger.valueOf(10)); -// contentSream.setMimeType("text/plain"); -// contentSream.setStream(new StringInputStream("abcdefghij")); -// -// ObjectId document = session.createDocument(docProps, folder, contentSream, VersioningState.MAJOR); - - if(j % 20 == 0) - { - System.out.println(id+" @ "+j); - } - } - } - - } - - } - - - public void testQueryFolderProperties() - { - Session session = getSession("admin", "admin"); - - String folderName = getRootFolderName(); - Folder root = session.getRootFolder(); - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderName); - - // create the folder - Folder newFolder = root.createFolder(properties); - - ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - assertEquals(1, result.getTotalNumItems()); - - - String uniqueName = getUniqueName(); - Map uProperties = new HashMap(); - uProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - uProperties.put(PropertyIds.NAME, uniqueName); - Folder uniqueFolder = newFolder.createFolder(uProperties); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"'", false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND IN_FOLDER('"+ uniqueFolder.getParentId() + "')" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where IN_FOLDER('"+ uniqueFolder.getParentId() + "')" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:createdBy = '"+ uniqueFolder.getCreatedBy()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:objectId = '"+ uniqueFolder.getId()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:lastModifiedBy = '"+ uniqueFolder.getLastModifiedBy()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+ uniqueFolder.getName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - String creationDate = ISO8601DateFormat.format(uniqueFolder.getCreationDate().getTime()); - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:creationDate = '"+ creationDate +"'" , false); - assertEquals(1, result.getTotalNumItems()); - - String modificationDate = ISO8601DateFormat.format(uniqueFolder.getLastModificationDate().getTime()); - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:lastModificationDate = '"+ modificationDate+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:objectTypeId = '"+ uniqueFolder.getType().getQueryName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:baseTypeId = '"+ uniqueFolder.getBaseType().getQueryName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - } - - public void testQueryDocumentProperties() throws Exception - { - Session session = getSession("admin", "admin"); - - String folderName = getRootFolderName(); - Folder root = session.getRootFolder(); - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderName); - - // create the folder - Folder newFolder = root.createFolder(properties); - - ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - assertEquals(1, result.getTotalNumItems()); - - - Document uniqueDocument = createUniqueDocument(newFolder); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"'", false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND IN_FOLDER('"+ newFolder.getId() + "')" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where IN_FOLDER('"+ newFolder.getId() + "')" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:createdBy = '"+ uniqueDocument.getCreatedBy()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:objectId = '"+ uniqueDocument.getId()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:lastModifiedBy = '"+ uniqueDocument.getLastModifiedBy()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+ uniqueDocument.getName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - String creationDate = ISO8601DateFormat.format(uniqueDocument.getCreationDate().getTime()); - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:creationDate = '"+ creationDate +"'" , false); - assertEquals(1, result.getTotalNumItems()); - - String modificationDate = ISO8601DateFormat.format(uniqueDocument.getLastModificationDate().getTime()); - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:lastModificationDate = '"+ modificationDate+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:objectTypeId = '"+ uniqueDocument.getType().getQueryName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:baseTypeId = '"+ uniqueDocument.getBaseType().getQueryName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamFileName = '"+ uniqueDocument.getContentStreamFileName()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamLength = '"+ uniqueDocument.getContentStreamLength()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamMimeType = '"+ uniqueDocument.getContentStreamMimeType()+"'" , false); - assertEquals(1, result.getTotalNumItems()); - } - - public void testQueryForMultipleDocumentProperties() throws Exception - { - Session session = getSession("admin", "admin"); - - String folderName = getRootFolderName(); - Folder root = session.getRootFolder(); - - Map properties = new HashMap(); - properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); - properties.put(PropertyIds.NAME, folderName); - - // create the folder - Folder newFolder = root.createFolder(properties); - - ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); - assertEquals(1, result.getTotalNumItems()); - - - Document document1 = createUniqueDocument(newFolder); - Document document2 = createUniqueDocument(newFolder); - - //result = session.query("SELECT * FROM cmis:folder d join sys:hidden t on d.cmis:objectId = t.cmis:objectId", false); - //assertTrue(0 < result.getTotalNumItems()); - - //result = session.query("SELECT * FROM cmis:folder d join sys:hidden t on d.cmis:objectId = t.cmis:objectId where t.sys:cascadeHidden = false", false); - //assertTrue(0 < result.getTotalNumItems()); - - result = session.query("SELECT * FROM cmis:document d join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); - long count = result.getTotalNumItems(); - - Map props = new HashMap(); - props.put("exif:focalLength", 10.33d); - ArrayList aspects = new ArrayList(); - aspects.add("P:exif:exif"); - props.put("cmis:secondaryObjectTypeIds", aspects); - document1.updateProperties(props); - - result = session.query("SELECT * FROM cmis:document d join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); - assertEquals(count+1, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+document1.getName()+"' OR cmis:name = '"+document2.getName()+"'", false); - assertEquals(2, result.getTotalNumItems()); - - result = session.query("select * from cmis:document where cmis:name = '"+document1.getName()+"' AND cmis:contentStreamLength = 5 AND cmis:contentStreamMimeType = 'text/plain'", false); - assertEquals(1, result.getTotalNumItems()); - - result = session.query("SELECT * FROM cmis:document", false); - long docCount = result.getTotalNumItems(); - - result = session.query("SELECT * FROM cmis:document d left outer join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); - assertEquals(docCount, result.getTotalNumItems()); - } - - private Document createUniqueDocument(Folder newFolder) - throws UnsupportedEncodingException { - String uniqueName = getUniqueName(); - Map uProperties = new HashMap(); - uProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); - uProperties.put(PropertyIds.NAME, uniqueName); - - ContentStreamImpl contentStream = new ContentStreamImpl(); - contentStream.setFileName("bob"); - String shortString = "short"; - contentStream.setStream(new ByteArrayInputStream(shortString.getBytes("UTF-8"))); - contentStream.setLength(new BigInteger("5")); - contentStream.setMimeType("text/plain"); - - Document uniqueDocument = newFolder.createDocument(uProperties, contentStream, VersioningState.MAJOR); - return uniqueDocument; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.alfresco.util.GUID; +import org.alfresco.util.ISO8601DateFormat; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.chemistry.opencmis.client.api.Document; +import org.apache.chemistry.opencmis.client.api.Folder; +import org.apache.chemistry.opencmis.client.api.ItemIterable; +import org.apache.chemistry.opencmis.client.api.ObjectId; +import org.apache.chemistry.opencmis.client.api.QueryResult; +import org.apache.chemistry.opencmis.client.api.Repository; +import org.apache.chemistry.opencmis.client.api.Session; +import org.apache.chemistry.opencmis.client.api.SessionFactory; +import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl; +import org.apache.chemistry.opencmis.commons.BasicPermissions; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.SessionParameter; +import org.apache.chemistry.opencmis.commons.data.Ace; +import org.apache.chemistry.opencmis.commons.enums.AclPropagation; +import org.apache.chemistry.opencmis.commons.enums.BindingType; +import org.apache.chemistry.opencmis.commons.enums.VersioningState; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlEntryImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; + +/** + * @author Andy + * + */ +public class CMISDataCreatorTest extends TestCase +{ + private static Session getSession(String user, String pwd) + { + String url = "http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/atom"; + + SessionFactory factory = SessionFactoryImpl.newInstance(); + Map parameter = new HashMap(); + parameter.put(SessionParameter.USER, user); + parameter.put(SessionParameter.PASSWORD, pwd); + parameter.put(SessionParameter.ATOMPUB_URL, url); + parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value()); + + List repositories = factory.getRepositories(parameter); + Session session = repositories.get(0).createSession(); + + return session; + } + + private String getRootFolderName() + { + return "CMIS-Data-Creator-Test-"+System.currentTimeMillis(); + } + + private String getUniqueName() + { + return "CMIS-Data-Creator-Test-"+GUID.generate(); + } + + public void testCreate() + { + Session session = getSession("admin", "admin"); + + String folderName = getRootFolderName(); + Folder root = session.getRootFolder(); + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderName); + + // create the folder + Folder newFolder = root.createFolder(properties); + + for(int i = 0; i < 50; i++) + { + AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl("user"+i); + List permissions = new ArrayList(1); + permissions.add(BasicPermissions.READ); + List addAces = new ArrayList(1); + addAces.add(new AccessControlEntryImpl(principal, permissions)); + newFolder.addAcl(addAces, AclPropagation.PROPAGATE); + + Map updateProperties = new HashMap(); + updateProperties.put("cm:title", "Update title "+i); + newFolder.updateProperties(properties); + + if(i % 10 == 0) + { + System.out.println("@ "+i); + } + } + ItemIterable result = session.query("select * from cmis:folder", false); + assertTrue(result.getTotalNumItems() > 0); + + result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + assertTrue(result.getTotalNumItems() > 0); + + } + + public void testCreateLots() throws Exception + { + Session session = getSession("admin", "admin"); + + Folder root = session.getRootFolder(); + String folderNameBase = getRootFolderName(); + + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderNameBase); + + Folder base = root.createFolder(properties); + for(int i = 0; i < 10; i++) + { + AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl(""+i+i+i); + List permissions = new ArrayList(1); + permissions.add(BasicPermissions.ALL); + List addAces = new ArrayList(1); + addAces.add(new AccessControlEntryImpl(principal, permissions)); + base.addAcl(addAces, AclPropagation.PROPAGATE); + } + + + Thread last = null; + + for(int i = 0; i < 10; i++) + { + Creator creator = new Creator(base.getPath(), i); + Thread thread = new Thread(creator); + thread.start(); + last = thread; + } + + if(last != null) + { + last.join(); + } + + ItemIterable result = session.query("select * from cmis:folder", false); + assertTrue(result.getTotalNumItems() > 0); + + //result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + //assertTrue(result.getTotalNumItems() > 0); + + } + + private static class Creator implements Runnable + { + int id; + + String path; + + Creator(String path, int id) + { + this.path = path; + this.id = id; + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() + { + Session session = getSession(""+id+id+id, ""+id+id+id); + + CmisObject object = session.getObjectByPath(path); + + Map baseProps = new HashMap(); + baseProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + baseProps.put(PropertyIds.NAME, "Thread"+id); + + + ObjectId base = session.createFolder(baseProps, object); + + for(int i = 0; i < 100; i++) + { + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, "Folder-"+i); + + ObjectId folder = session.createFolder(properties, base); + + System.out.println("Thread "+id +" @Folder "+i); + + for(int j = 0; j < 1000; j++) + { + Map folderProps = new HashMap(); + folderProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + folderProps.put(PropertyIds.NAME, "Folder-"+i); + + ObjectId child = session.createFolder(folderProps, folder); + + +// Map docProps = new HashMap(); +// docProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); +// docProps.put(PropertyIds.NAME, "Doc-"+j); +// +// ContentStreamImpl contentSream = new ContentStreamImpl(); +// contentSream.setFileName(GUID.generate()); +// contentSream.setLength(BigInteger.valueOf(10)); +// contentSream.setMimeType("text/plain"); +// contentSream.setStream(new StringInputStream("abcdefghij")); +// +// ObjectId document = session.createDocument(docProps, folder, contentSream, VersioningState.MAJOR); + + if(j % 20 == 0) + { + System.out.println(id+" @ "+j); + } + } + } + + } + + } + + + public void testQueryFolderProperties() + { + Session session = getSession("admin", "admin"); + + String folderName = getRootFolderName(); + Folder root = session.getRootFolder(); + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderName); + + // create the folder + Folder newFolder = root.createFolder(properties); + + ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + assertEquals(1, result.getTotalNumItems()); + + + String uniqueName = getUniqueName(); + Map uProperties = new HashMap(); + uProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + uProperties.put(PropertyIds.NAME, uniqueName); + Folder uniqueFolder = newFolder.createFolder(uProperties); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"'", false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND IN_FOLDER('"+ uniqueFolder.getParentId() + "')" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where IN_FOLDER('"+ uniqueFolder.getParentId() + "')" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:createdBy = '"+ uniqueFolder.getCreatedBy()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:objectId = '"+ uniqueFolder.getId()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:lastModifiedBy = '"+ uniqueFolder.getLastModifiedBy()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+ uniqueFolder.getName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + String creationDate = ISO8601DateFormat.format(uniqueFolder.getCreationDate().getTime()); + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:creationDate = '"+ creationDate +"'" , false); + assertEquals(1, result.getTotalNumItems()); + + String modificationDate = ISO8601DateFormat.format(uniqueFolder.getLastModificationDate().getTime()); + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:lastModificationDate = '"+ modificationDate+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:objectTypeId = '"+ uniqueFolder.getType().getQueryName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:folder where cmis:name = '"+uniqueName+"' AND cmis:baseTypeId = '"+ uniqueFolder.getBaseType().getQueryName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + } + + public void testQueryDocumentProperties() throws Exception + { + Session session = getSession("admin", "admin"); + + String folderName = getRootFolderName(); + Folder root = session.getRootFolder(); + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderName); + + // create the folder + Folder newFolder = root.createFolder(properties); + + ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + assertEquals(1, result.getTotalNumItems()); + + + Document uniqueDocument = createUniqueDocument(newFolder); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"'", false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND IN_FOLDER('"+ newFolder.getId() + "')" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where IN_FOLDER('"+ newFolder.getId() + "')" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:createdBy = '"+ uniqueDocument.getCreatedBy()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:objectId = '"+ uniqueDocument.getId()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:lastModifiedBy = '"+ uniqueDocument.getLastModifiedBy()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+ uniqueDocument.getName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + String creationDate = ISO8601DateFormat.format(uniqueDocument.getCreationDate().getTime()); + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:creationDate = '"+ creationDate +"'" , false); + assertEquals(1, result.getTotalNumItems()); + + String modificationDate = ISO8601DateFormat.format(uniqueDocument.getLastModificationDate().getTime()); + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:lastModificationDate = '"+ modificationDate+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:objectTypeId = '"+ uniqueDocument.getType().getQueryName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:baseTypeId = '"+ uniqueDocument.getBaseType().getQueryName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamFileName = '"+ uniqueDocument.getContentStreamFileName()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamLength = '"+ uniqueDocument.getContentStreamLength()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+uniqueDocument.getName()+"' AND cmis:contentStreamMimeType = '"+ uniqueDocument.getContentStreamMimeType()+"'" , false); + assertEquals(1, result.getTotalNumItems()); + } + + public void testQueryForMultipleDocumentProperties() throws Exception + { + Session session = getSession("admin", "admin"); + + String folderName = getRootFolderName(); + Folder root = session.getRootFolder(); + + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"); + properties.put(PropertyIds.NAME, folderName); + + // create the folder + Folder newFolder = root.createFolder(properties); + + ItemIterable result = session.query("select * from cmis:folder where cmis:name = '"+folderName+"'", false); + assertEquals(1, result.getTotalNumItems()); + + + Document document1 = createUniqueDocument(newFolder); + Document document2 = createUniqueDocument(newFolder); + + //result = session.query("SELECT * FROM cmis:folder d join sys:hidden t on d.cmis:objectId = t.cmis:objectId", false); + //assertTrue(0 < result.getTotalNumItems()); + + //result = session.query("SELECT * FROM cmis:folder d join sys:hidden t on d.cmis:objectId = t.cmis:objectId where t.sys:cascadeHidden = false", false); + //assertTrue(0 < result.getTotalNumItems()); + + result = session.query("SELECT * FROM cmis:document d join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); + long count = result.getTotalNumItems(); + + Map props = new HashMap(); + props.put("exif:focalLength", 10.33d); + ArrayList aspects = new ArrayList(); + aspects.add("P:exif:exif"); + props.put("cmis:secondaryObjectTypeIds", aspects); + document1.updateProperties(props); + + result = session.query("SELECT * FROM cmis:document d join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); + assertEquals(count+1, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+document1.getName()+"' OR cmis:name = '"+document2.getName()+"'", false); + assertEquals(2, result.getTotalNumItems()); + + result = session.query("select * from cmis:document where cmis:name = '"+document1.getName()+"' AND cmis:contentStreamLength = 5 AND cmis:contentStreamMimeType = 'text/plain'", false); + assertEquals(1, result.getTotalNumItems()); + + result = session.query("SELECT * FROM cmis:document", false); + long docCount = result.getTotalNumItems(); + + result = session.query("SELECT * FROM cmis:document d left outer join exif:exif t on d.cmis:objectId = t.cmis:objectId", false); + assertEquals(docCount, result.getTotalNumItems()); + } + + private Document createUniqueDocument(Folder newFolder) + throws UnsupportedEncodingException { + String uniqueName = getUniqueName(); + Map uProperties = new HashMap(); + uProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); + uProperties.put(PropertyIds.NAME, uniqueName); + + ContentStreamImpl contentStream = new ContentStreamImpl(); + contentStream.setFileName("bob"); + String shortString = "short"; + contentStream.setStream(new ByteArrayInputStream(shortString.getBytes("UTF-8"))); + contentStream.setLength(new BigInteger("5")); + contentStream.setMimeType("text/plain"); + + Document uniqueDocument = newFolder.createDocument(uProperties, contentStream, VersioningState.MAJOR); + return uniqueDocument; + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateUpdateDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateUpdateDistributedIT.java index 6a528ab60..5eb36a952 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateUpdateDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateUpdateDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -217,4 +217,4 @@ public class CoresCreateUpdateDistributedIT extends AbstractAlfrescoDistributedI coreAdminHandler.handleCustomAction(request, response); TimeUnit.SECONDS.sleep(2); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateViaPropertyIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateViaPropertyIT.java index c6ba3df38..01a1562d4 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateViaPropertyIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/CoresCreateViaPropertyIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/DynamicCopyFieldsIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/DynamicCopyFieldsIT.java index 855600ce6..7b75204a0 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/DynamicCopyFieldsIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/DynamicCopyFieldsIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/HandlerOfResourcesTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/HandlerOfResourcesTest.java index 737c57f68..669f6cd20 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/HandlerOfResourcesTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/HandlerOfResourcesTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -74,4 +74,4 @@ public class HandlerOfResourcesTest assertFalse(HandlerOfResources.allowedProperties(props, HandlerOfResources.DISALLOWED_SHARED_UPDATES)); props.remove("alfresco.cross.locale.datatype.2"); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrDataModelTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrDataModelTest.java index 4ab9d6069..590b6398c 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrDataModelTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrDataModelTest.java @@ -1,56 +1,56 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import org.alfresco.service.namespace.QName; -import org.junit.Test; - -/** - * @author Andy - * - */ -public class SolrDataModelTest -{ - private static final QName CONTENT_STREAM_LENGTH = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}contentStreamLength"); - private static final QName IS_PRIVATE_WOKING_COPY = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}isPrivateWorkingCopy"); - private static final QName IS_IMMUTABLE = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}isImmutable"); - private static final QName CREATION_DATE = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}creationDate"); - private static final QName NAME = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}name"); - private static QName OBJECT_ID = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}objectId"); - - @Test - public void testParseTransactionId() - { - Long expectedId = 94032903249l; - String id = AlfrescoSolrDataModel.getTransactionDocumentId(expectedId); - Long actualId = AlfrescoSolrDataModel.parseTransactionId(id); - assertEquals(expectedId, actualId); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.alfresco.service.namespace.QName; +import org.junit.Test; + +/** + * @author Andy + * + */ +public class SolrDataModelTest +{ + private static final QName CONTENT_STREAM_LENGTH = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}contentStreamLength"); + private static final QName IS_PRIVATE_WOKING_COPY = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}isPrivateWorkingCopy"); + private static final QName IS_IMMUTABLE = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}isImmutable"); + private static final QName CREATION_DATE = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}creationDate"); + private static final QName NAME = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}name"); + private static QName OBJECT_ID = QName.createQName("{http://www.alfresco.org/model/cmis/1.0/cs01}objectId"); + + @Test + public void testParseTransactionId() + { + Long expectedId = 94032903249l; + String id = AlfrescoSolrDataModel.getTransactionDocumentId(expectedId); + Long actualId = AlfrescoSolrDataModel.parseTransactionId(id); + assertEquals(expectedId, actualId); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrITInitializer.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrITInitializer.java index da6cb1430..24d744f00 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrITInitializer.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrITInitializer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -610,4 +610,4 @@ public abstract class SolrITInitializer extends SolrTestCaseJ4 } } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrInformationServerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrInformationServerTest.java index fe4253a51..2e4ba4f21 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrInformationServerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrInformationServerTest.java @@ -1,421 +1,421 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import static java.util.Optional.ofNullable; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ANY; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ASSOC_REF; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.BOOLEAN; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CATEGORY; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CHILD_ASSOC_REF; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CONTENT; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.DOUBLE; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ENCRYPTED; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.FLOAT; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.INT; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LOCALE; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LONG; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.MLTEXT; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.NODE_REF; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PATH; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PERIOD; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.QNAME; -import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.TEXT; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_HOUR_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MONTH_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_SECOND_FIELD_SUFFIX; -import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_YEAR_FIELD_SUFFIX; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.solr.client.NodeMetaData; -import org.alfresco.solr.client.SOLRAPIClient; -import org.apache.solr.common.SolrDocument; -import org.apache.solr.common.SolrInputDocument; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.core.SolrCore; -import org.apache.solr.core.SolrResourceLoader; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.request.SolrRequestHandler; -import org.apache.solr.response.SolrQueryResponse; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -/** - * Unit tests for the {@link SolrInformationServer} class. - * - * @author Matt Ward - * @author Andrea Gazzarini - */ -@RunWith(MockitoJUnitRunner.class) -public class SolrInformationServerTest -{ - private SolrInformationServer infoServer; - - @Mock - private AlfrescoCoreAdminHandler adminHandler; - - @Mock - private SolrResourceLoader resourceLoader; - - @Mock - private SolrCore core; - - @Mock - private SOLRAPIClient client; - - @Mock - private SolrRequestHandler handler; - - @Mock - private SolrQueryResponse response; - - private SolrQueryRequest request; - - @Before - public void setUp() - { - when(core.getResourceLoader()).thenReturn(resourceLoader); - when(resourceLoader.getCoreProperties()).thenReturn(new Properties()); - infoServer = new SolrInformationServer(adminHandler, core, client) - { - // @Override - SolrQueryResponse newSolrQueryResponse() - { - return response; - } - }; - - request = infoServer.newSolrQueryRequest(); - } - - @Test - public void testGetStateOk() - { - String id = String.valueOf(System.currentTimeMillis()); - - SolrDocument state = new SolrDocument(); - - SimpleOrderedMap responseContent = new SimpleOrderedMap<>(); - responseContent.add(SolrInformationServer.RESPONSE_DEFAULT_ID, state); - - when(response.getValues()).thenReturn(responseContent); - when(core.getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET)).thenReturn(handler); - - SolrDocument document = infoServer.getState(core, request, id); - - assertEquals(id, request.getParams().get(CommonParams.ID)); - verify(core).getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET); - verify(response).getValues(); - - assertSame(state, document); - } - - @Test - public void setUnitOfTimeFieldsWithDatetimeField_shouldSetDateAndTimeFields() - { - DataTypeDefinition datatype = mock(DataTypeDefinition.class); - when(datatype.getName()).thenReturn(DataTypeDefinition.DATETIME); - - SolrInputDocument document = new SolrInputDocument(); - - String fieldPrefix = "datetime@sd@"; - String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created"; - String sourceFieldName = fieldPrefix + fieldSuffix; - - infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype); - - assertEquals(1972, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX)); - assertEquals(3, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX)); - assertEquals(9, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX)); - assertEquals(16, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX)); - assertEquals(17, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX)); - assertEquals(33, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX)); - assertEquals(18, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX)); - } - - @Test - public void setUnitOfTimeFieldsWithDatetimeField_shouldSetOnlyDateFields() - { - DataTypeDefinition datatype = mock(DataTypeDefinition.class); - when(datatype.getName()).thenReturn(DataTypeDefinition.DATE); - - SolrInputDocument document = new SolrInputDocument(); - - String fieldPrefix = "datetime@sd@"; - String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created"; - String sourceFieldName = fieldPrefix + fieldSuffix; - - infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype); - - assertEquals(1972, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX)); - assertEquals(3, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX)); - assertEquals(9, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX)); - assertEquals(16, - unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX)); - - assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX)); - assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX)); - assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX)); - } - - @Test - public void destructuringCannotBeAppliedToMultivaluedFields() - { - PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class); - when(multiValuedProperty.isMultiValued()).thenReturn(true); - - assertFalse(infoServer.canBeDestructured(multiValuedProperty, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created")); - } - - /** - * Destructuring cannot only be applied to those fields matching a dynamic field in the schema. - * The dynamic fields defined in the schema follow the naming: - * - * [datatype]@[options]@[namespace][local name] - * - * For example - * - * datetime@sd@{http://www.alfresco.org/model/content/1.0}created - * - * SearchServices calls those fields "PROPERTIES", while the static fields (even belonging to a Node) are called - * "FIELDS". - */ - @Test - public void destructuringCannotBeAppliedToProperties() - { - PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class); - when(multiValuedProperty.isMultiValued()).thenReturn(false); - - assertFalse( - "Destructuring can only be applied to Node properties (e.g. dynamic fields in Solr schema)", - infoServer.canBeDestructured(multiValuedProperty, "this_is_a_field_that_doesnt_follow_the_dynamic_naming")); - } - - @Test - public void destructuringCanBeAppliedToDateFields() - { - PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class); - when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false); - - DataTypeDefinition date = mock(DataTypeDefinition.class); - when(date.getName()).thenReturn(DataTypeDefinition.DATE); - when(propertyThatCanBeDestructured.getDataType()).thenReturn(date); - - assertTrue( - "Destructuring must be supported in Date fields!", - infoServer.canBeDestructured(propertyThatCanBeDestructured,"date@sd@{http://www.alfresco.org/model/content/1.0}created")); - } - - @Test - public void destructuringCanBeAppliedToDateTimeFields() - { - PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class); - when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false); - - DataTypeDefinition datetime = mock(DataTypeDefinition.class); - when(datetime.getName()).thenReturn(DataTypeDefinition.DATETIME); - when(propertyThatCanBeDestructured.getDataType()).thenReturn(datetime); - - assertTrue( - "Destructuring must be supported in Datetime fields!", - infoServer.canBeDestructured(propertyThatCanBeDestructured, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created")); - } - - @Test - public void destructuringCanBeAppliedOnlyToDateOrDatetimeFields() - { - Stream.of(ANY,ENCRYPTED,TEXT,MLTEXT,CONTENT,INT,LONG,FLOAT,DOUBLE, - BOOLEAN,QNAME,CATEGORY,NODE_REF,CHILD_ASSOC_REF,ASSOC_REF, - PATH,LOCALE,PERIOD) - .map(qname -> { - PropertyDefinition propertyThatCannotBeDestructured = mock(PropertyDefinition.class); - DataTypeDefinition def = Mockito.mock(DataTypeDefinition.class); - when(def.getName()).thenReturn(qname); - when(propertyThatCannotBeDestructured.getDataType()).thenReturn(def); - return propertyThatCannotBeDestructured;}) - .forEach(property -> assertFalse( - "Destructuring must be supported only on Date or Datetime fields!", - infoServer.canBeDestructured(property, "somedatatype@sd@{http://www.alfresco.org/model/content/1.0}somefield"))); - } - - private int unitOfTimeFieldValue(SolrInputDocument doc, String fieldName) { - return ofNullable(doc.getFieldValue(fieldName)) - .map(Number.class::cast) - .map(Number::intValue) - .orElseThrow(() -> new IllegalArgumentException(fieldName + " hasn't been set.")); - } - - /** - * GetState returns null in case the given id doesn't correspond to an existing state document. - */ - @Test - public void testGetStateWithStateNotFound_returnsNull() - { - String id = String.valueOf(System.currentTimeMillis()); - - SimpleOrderedMap responseContent = new SimpleOrderedMap<>(); - responseContent.add(SolrInformationServer.RESPONSE_DEFAULT_ID, null); - - when(response.getValues()).thenReturn(responseContent); - when(core.getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET)).thenReturn(handler); - - SolrDocument document = infoServer.getState(core, request, id); - - assertEquals(id, request.getParams().get(CommonParams.ID)); - verify(core).getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET); - verify(response).getValues(); - - assertNull(document); - } - - /** - * When storing ANAME and APATH fields, skipping to store duplicated entries should be granted. - * - * Test data has been simplified from a living input use case: - * - * { - * "apath": "/9ea65c3c/1f5eebed/d657ec29/7c7da7c4/3ca56633/85f3f802/5c3a9e15/da781274", - * "path": "/company_home/user_homes/user1/taskers/Tasker-1418058127641/responseSummary-1418332928505/responseSummary-1418332928552/response" - * }, - * { - * "apath": "/9ea65c3c/1f5eebed/d657ec29/572c38fc/4ff94a6e/85f3f802/5c3a9e15/da781274", - * "path": "/company_home/user_homes/user2/taskers/tasker/responseSummary-1418332928505/responseSummary-1418332928552/response" - * }, - * { - * "apath": "/9ea65c3c/1f5eebed/d657ec29/cebd969b/0decd203/85f3f802/5c3a9e15/da781274", - * "path": "/company_home/user_homes/user3/taskers/tasker/responseSummary-1418332928505/responseSummary-1418332928552/response" - * } - */ - @Test - public void testPathsFieldStorage() - { - - SolrInputDocument doc = new SolrInputDocument(); - NodeMetaData nodeMetaData = new NodeMetaData(); - nodeMetaData.setAncestorPaths(List.of("/1/2/4/7/10", "/1/2/5/8/10", "/1/2/6/9/10")); - nodeMetaData.setPaths(List.of()); - - List expectedANames = Stream.of("0/10", - "1/7/10", "1/8/10", "1/9/10", - "2/4/7/10", "2/5/8/10", "2/6/9/10", - "3/2/4/7/10", "3/2/5/8/10", "3/2/6/9/10", - "4/1/2/4/7/10", "4/1/2/5/8/10", "4/1/2/6/9/10", - "F/1/2/4/7/10", "F/1/2/5/8/10", "F/1/2/6/9/10") - .collect(Collectors.toCollection(ArrayList::new)); - - List expectedAPaths = Stream.of("0/1", - "1/1/2", - "2/1/2/4", "2/1/2/5", "2/1/2/6", - "3/1/2/4/7", "3/1/2/5/8", "3/1/2/6/9", - "4/1/2/4/7/10", "4/1/2/5/8/10", "4/1/2/6/9/10", - "F/1/2/4/7/10", "F/1/2/5/8/10", "F/1/2/6/9/10") - .collect(Collectors.toCollection(ArrayList::new)); - - infoServer.updatePathRelatedFields(nodeMetaData, doc); - - List anames = doc.getFieldValues(QueryConstants.FIELD_ANAME).stream() - .map(aname -> aname.toString()) - .collect(Collectors.toList()) - .stream() - .sorted() - .collect(Collectors.toList()); - - assertEquals(expectedANames, anames); - - List apaths = doc.getFieldValues(QueryConstants.FIELD_APATH).stream() - .map(aname -> aname.toString()) - .collect(Collectors.toList()) - .stream() - .sorted() - .collect(Collectors.toList()); - - assertEquals(expectedAPaths, apaths); - - - } - - /** - * Repeat the indexing operation 2 times to verify that updating and existing document - * removes previous information in ANAME and APATH fields - */ - @Test - public void testPathsFieldStorageIterations() - { - - SolrInputDocument doc = new SolrInputDocument(); - NodeMetaData nodeMetaData = new NodeMetaData(); - nodeMetaData.setAncestorPaths(List.of("/1/2/4/7/10", "/1/2/5/8/10", "/1/2/6/9/10")); - nodeMetaData.setPaths(List.of()); - - IntStream.range(0, 2).forEach(i -> - { - - infoServer.updatePathRelatedFields(nodeMetaData, doc); - - assertEquals(doc.getFieldValues(QueryConstants.FIELD_ANAME).size(), 16); - assertEquals(doc.getFieldValues(QueryConstants.FIELD_APATH).size(), 14); - - }); - - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import static java.util.Optional.ofNullable; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ANY; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ASSOC_REF; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.BOOLEAN; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CATEGORY; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CHILD_ASSOC_REF; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CONTENT; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.DOUBLE; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ENCRYPTED; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.FLOAT; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.INT; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LOCALE; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LONG; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.MLTEXT; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.NODE_REF; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PATH; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PERIOD; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.QNAME; +import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.TEXT; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_HOUR_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MINUTE_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MONTH_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_QUARTER_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_SECOND_FIELD_SUFFIX; +import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_YEAR_FIELD_SUFFIX; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.solr.client.NodeMetaData; +import org.alfresco.solr.client.SOLRAPIClient; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.SolrCore; +import org.apache.solr.core.SolrResourceLoader; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestHandler; +import org.apache.solr.response.SolrQueryResponse; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Unit tests for the {@link SolrInformationServer} class. + * + * @author Matt Ward + * @author Andrea Gazzarini + */ +@RunWith(MockitoJUnitRunner.class) +public class SolrInformationServerTest +{ + private SolrInformationServer infoServer; + + @Mock + private AlfrescoCoreAdminHandler adminHandler; + + @Mock + private SolrResourceLoader resourceLoader; + + @Mock + private SolrCore core; + + @Mock + private SOLRAPIClient client; + + @Mock + private SolrRequestHandler handler; + + @Mock + private SolrQueryResponse response; + + private SolrQueryRequest request; + + @Before + public void setUp() + { + when(core.getResourceLoader()).thenReturn(resourceLoader); + when(resourceLoader.getCoreProperties()).thenReturn(new Properties()); + infoServer = new SolrInformationServer(adminHandler, core, client) + { + // @Override + SolrQueryResponse newSolrQueryResponse() + { + return response; + } + }; + + request = infoServer.newSolrQueryRequest(); + } + + @Test + public void testGetStateOk() + { + String id = String.valueOf(System.currentTimeMillis()); + + SolrDocument state = new SolrDocument(); + + SimpleOrderedMap responseContent = new SimpleOrderedMap<>(); + responseContent.add(SolrInformationServer.RESPONSE_DEFAULT_ID, state); + + when(response.getValues()).thenReturn(responseContent); + when(core.getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET)).thenReturn(handler); + + SolrDocument document = infoServer.getState(core, request, id); + + assertEquals(id, request.getParams().get(CommonParams.ID)); + verify(core).getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET); + verify(response).getValues(); + + assertSame(state, document); + } + + @Test + public void setUnitOfTimeFieldsWithDatetimeField_shouldSetDateAndTimeFields() + { + DataTypeDefinition datatype = mock(DataTypeDefinition.class); + when(datatype.getName()).thenReturn(DataTypeDefinition.DATETIME); + + SolrInputDocument document = new SolrInputDocument(); + + String fieldPrefix = "datetime@sd@"; + String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created"; + String sourceFieldName = fieldPrefix + fieldSuffix; + + infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype); + + assertEquals(1972, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX)); + assertEquals(3, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX)); + assertEquals(9, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX)); + assertEquals(16, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX)); + assertEquals(17, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX)); + assertEquals(33, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX)); + assertEquals(18, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX)); + } + + @Test + public void setUnitOfTimeFieldsWithDatetimeField_shouldSetOnlyDateFields() + { + DataTypeDefinition datatype = mock(DataTypeDefinition.class); + when(datatype.getName()).thenReturn(DataTypeDefinition.DATE); + + SolrInputDocument document = new SolrInputDocument(); + + String fieldPrefix = "datetime@sd@"; + String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created"; + String sourceFieldName = fieldPrefix + fieldSuffix; + + infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype); + + assertEquals(1972, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX)); + assertEquals(3, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_QUARTER_FIELD_SUFFIX)); + assertEquals(9, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX)); + assertEquals(16, + unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX)); + + assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX)); + assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX)); + assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX)); + } + + @Test + public void destructuringCannotBeAppliedToMultivaluedFields() + { + PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class); + when(multiValuedProperty.isMultiValued()).thenReturn(true); + + assertFalse(infoServer.canBeDestructured(multiValuedProperty, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created")); + } + + /** + * Destructuring cannot only be applied to those fields matching a dynamic field in the schema. + * The dynamic fields defined in the schema follow the naming: + * + * [datatype]@[options]@[namespace][local name] + * + * For example + * + * datetime@sd@{http://www.alfresco.org/model/content/1.0}created + * + * SearchServices calls those fields "PROPERTIES", while the static fields (even belonging to a Node) are called + * "FIELDS". + */ + @Test + public void destructuringCannotBeAppliedToProperties() + { + PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class); + when(multiValuedProperty.isMultiValued()).thenReturn(false); + + assertFalse( + "Destructuring can only be applied to Node properties (e.g. dynamic fields in Solr schema)", + infoServer.canBeDestructured(multiValuedProperty, "this_is_a_field_that_doesnt_follow_the_dynamic_naming")); + } + + @Test + public void destructuringCanBeAppliedToDateFields() + { + PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class); + when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false); + + DataTypeDefinition date = mock(DataTypeDefinition.class); + when(date.getName()).thenReturn(DataTypeDefinition.DATE); + when(propertyThatCanBeDestructured.getDataType()).thenReturn(date); + + assertTrue( + "Destructuring must be supported in Date fields!", + infoServer.canBeDestructured(propertyThatCanBeDestructured,"date@sd@{http://www.alfresco.org/model/content/1.0}created")); + } + + @Test + public void destructuringCanBeAppliedToDateTimeFields() + { + PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class); + when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false); + + DataTypeDefinition datetime = mock(DataTypeDefinition.class); + when(datetime.getName()).thenReturn(DataTypeDefinition.DATETIME); + when(propertyThatCanBeDestructured.getDataType()).thenReturn(datetime); + + assertTrue( + "Destructuring must be supported in Datetime fields!", + infoServer.canBeDestructured(propertyThatCanBeDestructured, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created")); + } + + @Test + public void destructuringCanBeAppliedOnlyToDateOrDatetimeFields() + { + Stream.of(ANY,ENCRYPTED,TEXT,MLTEXT,CONTENT,INT,LONG,FLOAT,DOUBLE, + BOOLEAN,QNAME,CATEGORY,NODE_REF,CHILD_ASSOC_REF,ASSOC_REF, + PATH,LOCALE,PERIOD) + .map(qname -> { + PropertyDefinition propertyThatCannotBeDestructured = mock(PropertyDefinition.class); + DataTypeDefinition def = Mockito.mock(DataTypeDefinition.class); + when(def.getName()).thenReturn(qname); + when(propertyThatCannotBeDestructured.getDataType()).thenReturn(def); + return propertyThatCannotBeDestructured;}) + .forEach(property -> assertFalse( + "Destructuring must be supported only on Date or Datetime fields!", + infoServer.canBeDestructured(property, "somedatatype@sd@{http://www.alfresco.org/model/content/1.0}somefield"))); + } + + private int unitOfTimeFieldValue(SolrInputDocument doc, String fieldName) { + return ofNullable(doc.getFieldValue(fieldName)) + .map(Number.class::cast) + .map(Number::intValue) + .orElseThrow(() -> new IllegalArgumentException(fieldName + " hasn't been set.")); + } + + /** + * GetState returns null in case the given id doesn't correspond to an existing state document. + */ + @Test + public void testGetStateWithStateNotFound_returnsNull() + { + String id = String.valueOf(System.currentTimeMillis()); + + SimpleOrderedMap responseContent = new SimpleOrderedMap<>(); + responseContent.add(SolrInformationServer.RESPONSE_DEFAULT_ID, null); + + when(response.getValues()).thenReturn(responseContent); + when(core.getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET)).thenReturn(handler); + + SolrDocument document = infoServer.getState(core, request, id); + + assertEquals(id, request.getParams().get(CommonParams.ID)); + verify(core).getRequestHandler(SolrInformationServer.REQUEST_HANDLER_GET); + verify(response).getValues(); + + assertNull(document); + } + + /** + * When storing ANAME and APATH fields, skipping to store duplicated entries should be granted. + * + * Test data has been simplified from a living input use case: + * + * { + * "apath": "/9ea65c3c/1f5eebed/d657ec29/7c7da7c4/3ca56633/85f3f802/5c3a9e15/da781274", + * "path": "/company_home/user_homes/user1/taskers/Tasker-1418058127641/responseSummary-1418332928505/responseSummary-1418332928552/response" + * }, + * { + * "apath": "/9ea65c3c/1f5eebed/d657ec29/572c38fc/4ff94a6e/85f3f802/5c3a9e15/da781274", + * "path": "/company_home/user_homes/user2/taskers/tasker/responseSummary-1418332928505/responseSummary-1418332928552/response" + * }, + * { + * "apath": "/9ea65c3c/1f5eebed/d657ec29/cebd969b/0decd203/85f3f802/5c3a9e15/da781274", + * "path": "/company_home/user_homes/user3/taskers/tasker/responseSummary-1418332928505/responseSummary-1418332928552/response" + * } + */ + @Test + public void testPathsFieldStorage() + { + + SolrInputDocument doc = new SolrInputDocument(); + NodeMetaData nodeMetaData = new NodeMetaData(); + nodeMetaData.setAncestorPaths(List.of("/1/2/4/7/10", "/1/2/5/8/10", "/1/2/6/9/10")); + nodeMetaData.setPaths(List.of()); + + List expectedANames = Stream.of("0/10", + "1/7/10", "1/8/10", "1/9/10", + "2/4/7/10", "2/5/8/10", "2/6/9/10", + "3/2/4/7/10", "3/2/5/8/10", "3/2/6/9/10", + "4/1/2/4/7/10", "4/1/2/5/8/10", "4/1/2/6/9/10", + "F/1/2/4/7/10", "F/1/2/5/8/10", "F/1/2/6/9/10") + .collect(Collectors.toCollection(ArrayList::new)); + + List expectedAPaths = Stream.of("0/1", + "1/1/2", + "2/1/2/4", "2/1/2/5", "2/1/2/6", + "3/1/2/4/7", "3/1/2/5/8", "3/1/2/6/9", + "4/1/2/4/7/10", "4/1/2/5/8/10", "4/1/2/6/9/10", + "F/1/2/4/7/10", "F/1/2/5/8/10", "F/1/2/6/9/10") + .collect(Collectors.toCollection(ArrayList::new)); + + infoServer.updatePathRelatedFields(nodeMetaData, doc); + + List anames = doc.getFieldValues(QueryConstants.FIELD_ANAME).stream() + .map(aname -> aname.toString()) + .collect(Collectors.toList()) + .stream() + .sorted() + .collect(Collectors.toList()); + + assertEquals(expectedANames, anames); + + List apaths = doc.getFieldValues(QueryConstants.FIELD_APATH).stream() + .map(aname -> aname.toString()) + .collect(Collectors.toList()) + .stream() + .sorted() + .collect(Collectors.toList()); + + assertEquals(expectedAPaths, apaths); + + + } + + /** + * Repeat the indexing operation 2 times to verify that updating and existing document + * removes previous information in ANAME and APATH fields + */ + @Test + public void testPathsFieldStorageIterations() + { + + SolrInputDocument doc = new SolrInputDocument(); + NodeMetaData nodeMetaData = new NodeMetaData(); + nodeMetaData.setAncestorPaths(List.of("/1/2/4/7/10", "/1/2/5/8/10", "/1/2/6/9/10")); + nodeMetaData.setPaths(List.of()); + + IntStream.range(0, 2).forEach(i -> + { + + infoServer.updatePathRelatedFields(nodeMetaData, doc); + + assertEquals(doc.getFieldValues(QueryConstants.FIELD_ANAME).size(), 16); + assertEquals(doc.getFieldValues(QueryConstants.FIELD_APATH).size(), 14); + + }); + + } + +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrTestFiles.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrTestFiles.java index 0af2a1165..dba8f428b 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrTestFiles.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/SolrTestFiles.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/TemplatesDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/TemplatesDistributedIT.java index 48f74493f..fd270dc89 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/TemplatesDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/TemplatesDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/RandomSupplier.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/RandomSupplier.java index ab774ffff..c27333119 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/RandomSupplier.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/RandomSupplier.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/SolrResponsesComparator.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/SolrResponsesComparator.java index eb90c4b43..b9d98006c 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/SolrResponsesComparator.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/basics/SolrResponsesComparator.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/RewriteFacetParametersComponentTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/RewriteFacetParametersComponentTest.java index 798f01ed9..0465a0126 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/RewriteFacetParametersComponentTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/RewriteFacetParametersComponentTest.java @@ -1,330 +1,330 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component; - -import static java.util.Arrays.asList; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import java.util.HashMap; -import java.util.Map; - -import com.google.common.collect.ImmutableMap; -import com.sun.xml.xsom.impl.scd.Iterators; - -import org.apache.lucene.util.LuceneTestCase; -import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.ShardParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.handler.component.ShardRequest; -import org.apache.solr.request.SolrQueryRequest; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) -@SolrTestCaseJ4.SuppressSSL -public class RewriteFacetParametersComponentTest -{ - @InjectMocks - RewriteFacetParametersComponent rewriteFacetParametersComponent; - @Mock - SolrParams mockParams; - @Mock - SolrQueryRequest mockRequest; - - @Before - public void setUp() - { - initMocks(this); - when(mockRequest.getParams()).thenReturn(mockParams); - } - - /** - * Search-409, mange parsing of facet fields. - * RewriteFacetParametersComponent incorrectly splits fields when a comma - * is present within {}. Below example uses {max=100, percentiles=90,99} - */ - @Test - public void parseFacetField() - { - String a = "created,modified"; - String b = "cm:created,modified"; - String c = "modified"; - String d = "{crazy}created,modified,updated"; - String e = "{bob:\"fred\"}created,modified,updated"; - String f = "{perc=\"3,4,5\"}created,modified"; - String g = "{perc='3,4,5'}created,modified"; - - assertEquals(2, RewriteFacetParametersComponent.parseFacetField(a).length); - assertEquals(2, RewriteFacetParametersComponent.parseFacetField(b).length); - assertEquals(1, RewriteFacetParametersComponent.parseFacetField(c).length); - assertEquals(3, RewriteFacetParametersComponent.parseFacetField(d).length); - assertEquals(3, RewriteFacetParametersComponent.parseFacetField(e).length); - assertEquals(2, RewriteFacetParametersComponent.parseFacetField(f).length); - assertEquals(2, RewriteFacetParametersComponent.parseFacetField(g).length); - } - @Test(expected=RuntimeException.class) - public void parseEmpty() - { - RewriteFacetParametersComponent.parseFacetField(""); - } - @Test(expected=RuntimeException.class) - public void parseNull() - { - RewriteFacetParametersComponent.parseFacetField(null); - } - - /** Check that if no mincount is supplied for a field facet then it gets defaulted to 1. */ - @Test - public void rewriteMincountFacetFieldOption_mincountMissing_shouldSetGenericMinCountToOne() - { - // There are no existing facet parameters. - ModifiableSolrParams fixed = new ModifiableSolrParams(); - when(mockParams.getParameterNamesIterator()).thenReturn(Iterators.empty()); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1. - String actual = fixed.get("facet.mincount"); - assertEquals("Expected the existing mincount to be preserved.", "1", actual); - } - - /** Check that if the mincount is set as 0 then it is updated to be 1. */ - @Test - public void rewriteMincountFacetFieldOption_mincountSetZero_shouldSetMincountToOne() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); - when(mockParams.get("facet.mincount")).thenReturn("0"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualCount); - } - - @Test - public void rewriteShardedRequestParameters_mincountSetZero_shouldKeepMincountToZero() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); - when(mockParams.get("facet.mincount")).thenReturn("0"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(String.valueOf(ShardRequest.PURPOSE_GET_FACETS)); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.mincount"); - assertEquals("Expected no fixed value", null, actualCount); - } - - @Test - public void rewriteMincountFacetFieldOption_mincountSetTwo_shouldKeepIt() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); - when(mockParams.get("facet.mincount")).thenReturn("2"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.mincount"); - assertEquals("Expected the mincount to be 2.", "2", actualCount); - } - - @Test - public void rewriteMincountFacetFieldOption_perFieldMincountSetZero_shouldSetPerFieldMincountAndMincountToOne() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); - when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = ImmutableMap.of( - "NAME", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualNameCount); - String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualContentCount); - String actualCount = fixed.get("facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualCount); - } - - /** Check that if the mincount is set as 0 then it is updated to be 1. */ - @Test - public void rewriteMincountFacetFieldOption_perFieldMincountSetZero_shouldSetPerFieldMincountToOne() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); - when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = ImmutableMap.of( - "NAME", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualNameCount); - String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualContentCount); - } - - /** Check that if the user supplies a mincount of 2 then this is not changed. */ - @Test - public void rewriteMincountFacetFieldOption_perFieldMincountSetTwo_shouldKeepIt() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); - when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"2"}); - when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = ImmutableMap.of( - "NAME", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", - "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is kept as 2 and the field name is converted to the format stored by Solr. - String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); - assertEquals("Expected the mincount to be 2.", "2", actualNameCount); - String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualContentCount); - } - - - @Test - public void rewriteMincountFacetPivotOption_mincountMissing_shouldSetPivotMinCountToOne() - { - // There are no existing facet parameters. - ModifiableSolrParams fixed = new ModifiableSolrParams(); - when(mockParams.getParameterNamesIterator()).thenReturn(Iterators.empty()); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1. - String actual = fixed.get("facet.pivot.mincount"); - assertEquals("Expected the existing mincount to be preserved.", "1", actual); - } - - /** Check that if the mincount is set as 0 then it is updated to be 1. */ - @Test - public void rewriteMincountFacetPivotOption_mincountSetZero_shouldSetMincountToOne() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.pivot.mincount").iterator()); - when(mockParams.get("facet.pivot.mincount")).thenReturn("0"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.pivot.mincount"); - assertEquals("Expected the mincount to be 1.", "1", actualCount); - } - - @Test - public void rewriteMincountFacetPivotOption_mincountSetTwo_shouldKeepIt() - { - ModifiableSolrParams fixed = new ModifiableSolrParams(); - // The user has tried to set the mincount to zero. - when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.pivot.mincount").iterator()); - when(mockParams.get("facet.pivot.mincount")).thenReturn("2"); - when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); - - Map fieldMappings = new HashMap<>(); - - // Call the method under test. - rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, - mockRequest); - - // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. - String actualCount = fixed.get("facet.pivot.mincount"); - assertEquals("Expected the mincount to be 2.", "2", actualCount); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component; + +import static java.util.Arrays.asList; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.xml.xsom.impl.scd.Iterators; + +import org.apache.lucene.util.LuceneTestCase; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.handler.component.ShardRequest; +import org.apache.solr.request.SolrQueryRequest; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) +@SolrTestCaseJ4.SuppressSSL +public class RewriteFacetParametersComponentTest +{ + @InjectMocks + RewriteFacetParametersComponent rewriteFacetParametersComponent; + @Mock + SolrParams mockParams; + @Mock + SolrQueryRequest mockRequest; + + @Before + public void setUp() + { + initMocks(this); + when(mockRequest.getParams()).thenReturn(mockParams); + } + + /** + * Search-409, mange parsing of facet fields. + * RewriteFacetParametersComponent incorrectly splits fields when a comma + * is present within {}. Below example uses {max=100, percentiles=90,99} + */ + @Test + public void parseFacetField() + { + String a = "created,modified"; + String b = "cm:created,modified"; + String c = "modified"; + String d = "{crazy}created,modified,updated"; + String e = "{bob:\"fred\"}created,modified,updated"; + String f = "{perc=\"3,4,5\"}created,modified"; + String g = "{perc='3,4,5'}created,modified"; + + assertEquals(2, RewriteFacetParametersComponent.parseFacetField(a).length); + assertEquals(2, RewriteFacetParametersComponent.parseFacetField(b).length); + assertEquals(1, RewriteFacetParametersComponent.parseFacetField(c).length); + assertEquals(3, RewriteFacetParametersComponent.parseFacetField(d).length); + assertEquals(3, RewriteFacetParametersComponent.parseFacetField(e).length); + assertEquals(2, RewriteFacetParametersComponent.parseFacetField(f).length); + assertEquals(2, RewriteFacetParametersComponent.parseFacetField(g).length); + } + @Test(expected=RuntimeException.class) + public void parseEmpty() + { + RewriteFacetParametersComponent.parseFacetField(""); + } + @Test(expected=RuntimeException.class) + public void parseNull() + { + RewriteFacetParametersComponent.parseFacetField(null); + } + + /** Check that if no mincount is supplied for a field facet then it gets defaulted to 1. */ + @Test + public void rewriteMincountFacetFieldOption_mincountMissing_shouldSetGenericMinCountToOne() + { + // There are no existing facet parameters. + ModifiableSolrParams fixed = new ModifiableSolrParams(); + when(mockParams.getParameterNamesIterator()).thenReturn(Iterators.empty()); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1. + String actual = fixed.get("facet.mincount"); + assertEquals("Expected the existing mincount to be preserved.", "1", actual); + } + + /** Check that if the mincount is set as 0 then it is updated to be 1. */ + @Test + public void rewriteMincountFacetFieldOption_mincountSetZero_shouldSetMincountToOne() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); + when(mockParams.get("facet.mincount")).thenReturn("0"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualCount); + } + + @Test + public void rewriteShardedRequestParameters_mincountSetZero_shouldKeepMincountToZero() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); + when(mockParams.get("facet.mincount")).thenReturn("0"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(String.valueOf(ShardRequest.PURPOSE_GET_FACETS)); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.mincount"); + assertEquals("Expected no fixed value", null, actualCount); + } + + @Test + public void rewriteMincountFacetFieldOption_mincountSetTwo_shouldKeepIt() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.mincount").iterator()); + when(mockParams.get("facet.mincount")).thenReturn("2"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.mincount"); + assertEquals("Expected the mincount to be 2.", "2", actualCount); + } + + @Test + public void rewriteMincountFacetFieldOption_perFieldMincountSetZero_shouldSetPerFieldMincountAndMincountToOne() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); + when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = ImmutableMap.of( + "NAME", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualNameCount); + String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualContentCount); + String actualCount = fixed.get("facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualCount); + } + + /** Check that if the mincount is set as 0 then it is updated to be 1. */ + @Test + public void rewriteMincountFacetFieldOption_perFieldMincountSetZero_shouldSetPerFieldMincountToOne() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); + when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = ImmutableMap.of( + "NAME", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualNameCount); + String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualContentCount); + } + + /** Check that if the user supplies a mincount of 2 then this is not changed. */ + @Test + public void rewriteMincountFacetFieldOption_perFieldMincountSetTwo_shouldKeepIt() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("f.NAME.facet.mincount","f.CONTENT.facet.mincount").iterator()); + when(mockParams.getParams("f.NAME.facet.mincount")).thenReturn(new String[]{"2"}); + when(mockParams.getParams("f.CONTENT.facet.mincount")).thenReturn(new String[]{"0"}); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = ImmutableMap.of( + "NAME", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name","CONTENT", + "{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content"); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is kept as 2 and the field name is converted to the format stored by Solr. + String actualNameCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}name.facet.mincount"); + assertEquals("Expected the mincount to be 2.", "2", actualNameCount); + String actualContentCount = fixed.get("f.{!afts key=SEARCH.FACET_FIELDS.LOCATION}text@s____@{http://www.alfresco.org/model/content/1.0}content.facet.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualContentCount); + } + + + @Test + public void rewriteMincountFacetPivotOption_mincountMissing_shouldSetPivotMinCountToOne() + { + // There are no existing facet parameters. + ModifiableSolrParams fixed = new ModifiableSolrParams(); + when(mockParams.getParameterNamesIterator()).thenReturn(Iterators.empty()); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1. + String actual = fixed.get("facet.pivot.mincount"); + assertEquals("Expected the existing mincount to be preserved.", "1", actual); + } + + /** Check that if the mincount is set as 0 then it is updated to be 1. */ + @Test + public void rewriteMincountFacetPivotOption_mincountSetZero_shouldSetMincountToOne() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.pivot.mincount").iterator()); + when(mockParams.get("facet.pivot.mincount")).thenReturn("0"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.pivot.mincount"); + assertEquals("Expected the mincount to be 1.", "1", actualCount); + } + + @Test + public void rewriteMincountFacetPivotOption_mincountSetTwo_shouldKeepIt() + { + ModifiableSolrParams fixed = new ModifiableSolrParams(); + // The user has tried to set the mincount to zero. + when(mockParams.getParameterNamesIterator()).thenReturn(asList("facet.pivot.mincount").iterator()); + when(mockParams.get("facet.pivot.mincount")).thenReturn("2"); + when(mockParams.get(ShardParams.SHARDS_PURPOSE)).thenReturn(null); + + Map fieldMappings = new HashMap<>(); + + // Call the method under test. + rewriteFacetParametersComponent.rewriteMincountFacetFieldOption(fixed, mockParams, "facet.pivot.mincount", fieldMappings, + mockRequest); + + // Check that the mincount is set to 1 and the field name is converted to the format stored by Solr. + String actualCount = fixed.get("facet.pivot.mincount"); + assertEquals("Expected the mincount to be 2.", "2", actualCount); + } + +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/TempFileWarningLoggerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/TempFileWarningLoggerTest.java index 1c0e89fef..6275f308f 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/TempFileWarningLoggerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/component/TempFileWarningLoggerTest.java @@ -1,179 +1,179 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.component; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; - -public class TempFileWarningLoggerTest -{ - private Logger log; - private Path path; - - @Before - public void setUp() - { - path = Paths.get(System.getProperty("java.io.tmpdir")); - - // Simulate warn-level logging (Although currently we only log debug messages). - log = mock(Logger.class, withSettings().lenient()); - when(log.isWarnEnabled()).thenReturn(true); - when(log.isErrorEnabled()).thenReturn(true); - } - - @Test - public void checkGlobBuiltCorrectly() - { - TempFileWarningLogger warner = - new TempFileWarningLogger( - log, - "MyPrefix*", - new String[] { "temp", "remove-me", "~notrequired" }, - path); - - assertEquals("MyPrefix*.{temp,remove-me,~notrequired}", warner.getGlob()); - } - - @Test - public void checkFindFiles() throws IOException - { - File f = File.createTempFile("MyPrefix", ".remove-me", path.toFile()); - f.deleteOnExit(); - - try - { - TempFileWarningLogger warner = - new TempFileWarningLogger( - log, - "MyPrefix*", - new String[] { "temp", "remove-me", "~notrequired" }, - path); - - boolean found = warner.checkFiles(); - - assertTrue("Should have found matching files", found); - // Should be a warn-level log message. - verify(log, never()).warn(anyString()); - } - finally - { - f.delete(); - } - } - - - @Test - public void checkWhenNoFilesToFind() throws IOException - { - File f = new File(path.toFile(), "TestFile.random"); - - // It would be very odd if this file exists! - assertFalse("Unable to perform test as file exists: " + f, f.exists()); - - TempFileWarningLogger warner = - new TempFileWarningLogger( - log, - "TestFile", - new String[] { "random" }, - path); - - boolean found = warner.checkFiles(); - - assertFalse("Should NOT have found matching file", found); - // Should be no warn-level log message. - verify(log, never()).warn(anyString()); - } - - @Test - public void removeManyFiles() throws IOException - { - File f = File.createTempFile("WFSTInputIterator", ".input", path.toFile()); - File f2 = File.createTempFile("WFSTInputIterator", ".sorted", path.toFile()); - f.deleteOnExit(); - f2.deleteOnExit(); - - TempFileWarningLogger warner = new TempFileWarningLogger(log, - "WFSTInputIterator*", - new String[] { "input", "sorted" }, - path); - boolean found = warner.checkFiles(); - assertTrue("Should have found matching file", found); - assertTrue(f.exists()); - assertTrue(f2.exists()); - if(found) - { - warner.removeFiles(); - } - assertFalse(f.exists()); - assertFalse(f2.exists()); - - boolean found2 = warner.checkFiles(); - assertFalse("Should NOT have found a matching file", found2); - - } - @Test - public void notToRemoveFilesThatDontMatch() throws IOException - { - File f = File.createTempFile("someotherfile", ".input", path.toFile()); - File f2 = File.createTempFile("someotherfile", ".sorted", path.toFile()); - f.deleteOnExit(); - f2.deleteOnExit(); - - TempFileWarningLogger warner = new TempFileWarningLogger(log, - "WFSTInputIterator*", - new String[] { "input", "sorted" }, - path); - - - assertTrue(f.exists()); - assertTrue(f2.exists()); - warner.removeFiles(); - assertTrue(f.exists()); - assertTrue(f2.exists()); - - boolean found2 = warner.checkFiles(); - assertFalse("Should NOT have found a matching file", found2); - - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.component; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; + +public class TempFileWarningLoggerTest +{ + private Logger log; + private Path path; + + @Before + public void setUp() + { + path = Paths.get(System.getProperty("java.io.tmpdir")); + + // Simulate warn-level logging (Although currently we only log debug messages). + log = mock(Logger.class, withSettings().lenient()); + when(log.isWarnEnabled()).thenReturn(true); + when(log.isErrorEnabled()).thenReturn(true); + } + + @Test + public void checkGlobBuiltCorrectly() + { + TempFileWarningLogger warner = + new TempFileWarningLogger( + log, + "MyPrefix*", + new String[] { "temp", "remove-me", "~notrequired" }, + path); + + assertEquals("MyPrefix*.{temp,remove-me,~notrequired}", warner.getGlob()); + } + + @Test + public void checkFindFiles() throws IOException + { + File f = File.createTempFile("MyPrefix", ".remove-me", path.toFile()); + f.deleteOnExit(); + + try + { + TempFileWarningLogger warner = + new TempFileWarningLogger( + log, + "MyPrefix*", + new String[] { "temp", "remove-me", "~notrequired" }, + path); + + boolean found = warner.checkFiles(); + + assertTrue("Should have found matching files", found); + // Should be a warn-level log message. + verify(log, never()).warn(anyString()); + } + finally + { + f.delete(); + } + } + + + @Test + public void checkWhenNoFilesToFind() throws IOException + { + File f = new File(path.toFile(), "TestFile.random"); + + // It would be very odd if this file exists! + assertFalse("Unable to perform test as file exists: " + f, f.exists()); + + TempFileWarningLogger warner = + new TempFileWarningLogger( + log, + "TestFile", + new String[] { "random" }, + path); + + boolean found = warner.checkFiles(); + + assertFalse("Should NOT have found matching file", found); + // Should be no warn-level log message. + verify(log, never()).warn(anyString()); + } + + @Test + public void removeManyFiles() throws IOException + { + File f = File.createTempFile("WFSTInputIterator", ".input", path.toFile()); + File f2 = File.createTempFile("WFSTInputIterator", ".sorted", path.toFile()); + f.deleteOnExit(); + f2.deleteOnExit(); + + TempFileWarningLogger warner = new TempFileWarningLogger(log, + "WFSTInputIterator*", + new String[] { "input", "sorted" }, + path); + boolean found = warner.checkFiles(); + assertTrue("Should have found matching file", found); + assertTrue(f.exists()); + assertTrue(f2.exists()); + if(found) + { + warner.removeFiles(); + } + assertFalse(f.exists()); + assertFalse(f2.exists()); + + boolean found2 = warner.checkFiles(); + assertFalse("Should NOT have found a matching file", found2); + + } + @Test + public void notToRemoveFilesThatDontMatch() throws IOException + { + File f = File.createTempFile("someotherfile", ".input", path.toFile()); + File f2 = File.createTempFile("someotherfile", ".sorted", path.toFile()); + f.deleteOnExit(); + f2.deleteOnExit(); + + TempFileWarningLogger warner = new TempFileWarningLogger(log, + "WFSTInputIterator*", + new String[] { "input", "sorted" }, + path); + + + assertTrue(f.exists()); + assertTrue(f2.exists()); + warner.removeFiles(); + assertTrue(f.exists()); + assertTrue(f2.exists()); + + boolean found2 = warner.checkFiles(); + assertFalse("Should NOT have found a matching file", found2); + + } + +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/config/ConfigUtilTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/config/ConfigUtilTest.java index a47e3e24a..3d55b634d 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/config/ConfigUtilTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/config/ConfigUtilTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -75,4 +75,4 @@ public class ConfigUtilTest { assertEquals("SOLR_SOLR_BASEURL",ConfigUtil.convertPropertyNameToEnvironmentParam(SolrInformationServer.SOLR_BASEURL)); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/dataload/TestDataProvider.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/dataload/TestDataProvider.java index 26a3d6de7..a99533957 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/dataload/TestDataProvider.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/dataload/TestDataProvider.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -906,4 +906,4 @@ public class TestDataProvider implements AlfrescoSolrConstants { return new StringPropertyValue(value); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighligherDistributedIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighligherDistributedIT.java index aa43e1711..28d4b0c1f 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighligherDistributedIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighligherDistributedIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighlighterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighlighterIT.java index 3a7801408..a7773508c 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighlighterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/highlight/AlfrescoHighlighterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -425,4 +425,4 @@ public class AlfrescoHighlighterIT extends AbstractAlfrescoSolrIT "*[count(//lst[@name='highlighting']/lst/arr[@name='name'])=1]", "//lst[@name='highlighting']/lst[1]/arr[@name='name']/str[.='some {very} long {name}']"); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java index 05882adf3..bc46468dc 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -211,4 +211,4 @@ public class SolrCoreLoadListenerTest SolrConfig solrConfig = new SolrConfig(configName, new InputSource(getClass().getResourceAsStream("/test-files/" + configName))); when(core.getSolrConfig()).thenReturn(solrConfig); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoLuceneQParserTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoLuceneQParserTest.java index abc682462..5ecc9e98d 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoLuceneQParserTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoLuceneQParserTest.java @@ -1,62 +1,62 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import static org.junit.Assert.assertEquals; - -import org.alfresco.solr.query.AlfrescoLuceneQParserPlugin.AlfrescoLuceneQParser; -import org.junit.Before; -import org.junit.Test; - - -public class AlfrescoLuceneQParserTest -{ - private AlfrescoLuceneQParser parser; - - @Before - public void setUp() throws Exception - { - parser = new AlfrescoLuceneQParser(null, null, null, null, null); - } - - @Test - public void forwardSlashesEscapedCorrectly() - { - assertEquals("Nothing to escape", "abcdef", parser.escape("abcdef")); - assertEquals("Escape single slash", "abc\\/def", parser.escape("abc/def")); - assertEquals("Do not escape quoted slash", "abc\"/\"def", parser.escape("abc\"/\"def")); - assertEquals("Do not escape escaped slash", "abc\\/def", parser.escape("abc\\/def")); - assertEquals("Do not escape quoted escaped slash", "abc\"\\/\"def", parser.escape("abc\"\\/\"def")); - assertEquals("Escape multiple consecutive slashes", "abc\\/\\/def", parser.escape("abc//def")); - assertEquals("abc\\/\\/\\/\\/\\/def\\/\\/\\/:\"bl/ah\"", parser.escape("abc/////def/\\//:\"bl/ah\"")); - - // Test case from ACE-3071 - final String input = "(@\\{http\\://www.alfresco.org/model/imap/1.0\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\" OR @\\{http\\://www.westernacher.com/alfresco/models/wpsmail\\-v2\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\")"; - final String expected = "(@\\{http\\:\\/\\/www.alfresco.org\\/model\\/imap\\/1.0\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\" OR @\\{http\\:\\/\\/www.westernacher.com\\/alfresco\\/models\\/wpsmail\\-v2\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\")"; - assertEquals(expected, parser.escape(input)); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import static org.junit.Assert.assertEquals; + +import org.alfresco.solr.query.AlfrescoLuceneQParserPlugin.AlfrescoLuceneQParser; +import org.junit.Before; +import org.junit.Test; + + +public class AlfrescoLuceneQParserTest +{ + private AlfrescoLuceneQParser parser; + + @Before + public void setUp() throws Exception + { + parser = new AlfrescoLuceneQParser(null, null, null, null, null); + } + + @Test + public void forwardSlashesEscapedCorrectly() + { + assertEquals("Nothing to escape", "abcdef", parser.escape("abcdef")); + assertEquals("Escape single slash", "abc\\/def", parser.escape("abc/def")); + assertEquals("Do not escape quoted slash", "abc\"/\"def", parser.escape("abc\"/\"def")); + assertEquals("Do not escape escaped slash", "abc\\/def", parser.escape("abc\\/def")); + assertEquals("Do not escape quoted escaped slash", "abc\"\\/\"def", parser.escape("abc\"\\/\"def")); + assertEquals("Escape multiple consecutive slashes", "abc\\/\\/def", parser.escape("abc//def")); + assertEquals("abc\\/\\/\\/\\/\\/def\\/\\/\\/:\"bl/ah\"", parser.escape("abc/////def/\\//:\"bl/ah\"")); + + // Test case from ACE-3071 + final String input = "(@\\{http\\://www.alfresco.org/model/imap/1.0\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\" OR @\\{http\\://www.westernacher.com/alfresco/models/wpsmail\\-v2\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\")"; + final String expected = "(@\\{http\\:\\/\\/www.alfresco.org\\/model\\/imap\\/1.0\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\" OR @\\{http\\:\\/\\/www.westernacher.com\\/alfresco\\/models\\/wpsmail\\-v2\\}messageId:\"000000005E157E8B296B6B4DA1F00A30E4F21FE1C4002000\")"; + assertEquals(expected, parser.escape(input)); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoReRankQParserPluginIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoReRankQParserPluginIT.java index d80341527..5ffc5fc01 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoReRankQParserPluginIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/AlfrescoReRankQParserPluginIT.java @@ -1,705 +1,705 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.query; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import org.alfresco.solr.AbstractAlfrescoSolrIT; -import org.apache.lucene.util.LuceneTestCase; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.ResultContext; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocList; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - - -@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) -public class AlfrescoReRankQParserPluginIT extends AbstractAlfrescoSolrIT -{ - @BeforeClass - public static void beforeClass() throws Exception - { - initAlfrescoCore("schema-rerank.xml"); - } - @Before - public void setUp() throws Exception - { - - // if you override setUp or tearDown, you better call - // the super classes version - clearIndex(); - assertU(commit()); - } - - @Test - public void testReRankQueries() throws Exception { - - assertU(delQ("*:*")); - assertU(commit()); - - String[] doc = {"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc)); - assertU(commit()); - String[] doc1 = {"id","2", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc1)); - - String[] doc2 = {"id","3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc2)); - assertU(commit()); - String[] doc3 = {"id","4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; - assertU(adoc(doc3)); - - String[] doc4 = {"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc4)); - assertU(commit()); - String[] doc5 = {"id","6", "term_s","YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc5)); - assertU(commit()); - - - Random random = new Random(); - boolean scale = random.nextBoolean(); - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=200 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "term_s:YYYY"); - params.add("rqq", "{!edismax bf=$bff}*:*"); - params.add("bff", "field(test_ti)"); - params.add("start", "0"); - params.add("rows", "6"); - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='3.0']", - "//result/doc[2]/float[@name='id'][.='4.0']", - "//result/doc[3]/float[@name='id'][.='2.0']", - "//result/doc[4]/float[@name='id'][.='6.0']", - "//result/doc[5]/float[@name='id'][.='1.0']", - "//result/doc[6]/float[@name='id'][.='5.0']" - ); - - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "{!edismax bq=$bqq2}*:*"); - params.add("bqq2", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='5.0']", - "//result/doc[4]/float[@name='id'][.='4.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - //Test with sort by score. - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "{!edismax bq=$bqq2}*:*"); - params.add("bqq2", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("sort", "score desc"); - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='5.0']", - "//result/doc[4]/float[@name='id'][.='4.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - - //Test with compound sort. - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "{!edismax bq=$bqq2}*:*"); - params.add("bqq2", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("sort", "score desc,test_ti asc"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='5.0']", - "//result/doc[4]/float[@name='id'][.='4.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - - //Test with elevation - /* - - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=50 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "{!edismax bq=$bqq2}*:*"); - params.add("bqq2", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("qt", "/elevate"); - params.add("elevateIds", "1"); - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='1.0']", - "//result/doc[2]/float[@name='id'][.='2.0']", - "//result/doc[3]/float[@name='id'][.='6.0']", - "//result/doc[4]/float[@name='id'][.='5.0']", - "//result/doc[5]/float[@name='id'][.='4.0']", - "//result/doc[6]/float[@name='id'][.='3.0']" - - ); - */ - - - //Test TermQuery rqq - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='5.0']", - "//result/doc[4]/float[@name='id'][.='4.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - - //Test Elevation - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "1,4"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='1.0']", //Elevated - "//result/doc[2]/float[@name='id'][.='4.0']", //Elevated - "//result/doc[3]/float[@name='id'][.='2.0']", //Boosted during rerank. - "//result/doc[4]/float[@name='id'][.='6.0']", - "//result/doc[5]/float[@name='id'][.='5.0']", - "//result/doc[6]/float[@name='id'][.='3.0']" - ); - */ - - //Test Elevation swapped - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "4,1"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='4.0']", //Elevated - "//result/doc[2]/float[@name='id'][.='1.0']", //Elevated - "//result/doc[3]/float[@name='id'][.='2.0']", //Boosted during rerank. - "//result/doc[4]/float[@name='id'][.='6.0']", - "//result/doc[5]/float[@name='id'][.='5.0']", - "//result/doc[6]/float[@name='id'][.='3.0']" - ); - */ - - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=4 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "4,1"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='4.0']", //Elevated - "//result/doc[2]/float[@name='id'][.='1.0']", //Elevated - "//result/doc[3]/float[@name='id'][.='6.0']", - "//result/doc[4]/float[@name='id'][.='5.0']", - "//result/doc[5]/float[@name='id'][.='3.0']", - "//result/doc[6]/float[@name='id'][.='2.0']" //Not in reRankeDocs - ); - - */ - - - //Test Elevation with start beyond the rerank docs - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "4"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "4,1"); - - assertQ(req(params), "*[count(//doc)=2]", - "//result/doc[1]/float[@name='id'][.='3.0']", - "//result/doc[2]/float[@name='id'][.='2.0']" //Was not in reRankDocs - ); - */ - //Test Elevation with zero results - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}nada"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "4"); - params.add("rows", "10"); - params.add("qt","/elevate"); - params.add("elevateIds", "4,1"); - - assertQ(req(params), "*[count(//doc)=0]"); - */ - - - //Pass in reRankDocs lower then the length being collected. - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=1 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=0 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=2 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:4^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "10"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='5.0']", - "//result/doc[2]/float[@name='id'][.='6.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - //Test reRankWeight of 0, reranking will have no effect. - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=0 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "5"); - - assertQ(req(params), "*[count(//doc)=5]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']" - ); - - - - //Test range query - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "test_ti:[0 TO 2000]"); - params.add("rqq", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "6"); - - assertQ(req(params), "*[count(//doc)=5]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='2.0']", - "//result/doc[5]/float[@name='id'][.='1.0']" - ); - - //Test range query embedded in larger query - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "*:* OR test_ti:[0 TO 2000]"); - params.add("rqq", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "6"); - - assertQ(req(params), "*[count(//doc)=6]", - "//result/doc[1]/float[@name='id'][.='6.0']", - "//result/doc[2]/float[@name='id'][.='5.0']", - "//result/doc[3]/float[@name='id'][.='4.0']", - "//result/doc[4]/float[@name='id'][.='3.0']", - "//result/doc[5]/float[@name='id'][.='2.0']", - "//result/doc[6]/float[@name='id'][.='1.0']" - ); - - - //Test with start beyond reRankDocs - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); - params.add("rqq", "id:1^1000"); - params.add("fl", "id,score"); - params.add("start", "4"); - params.add("rows", "5"); - - assertQ(req(params), "*[count(//doc)=2]", - "//result/doc[1]/float[@name='id'][.='2.0']", - "//result/doc[2]/float[@name='id'][.='1.0']" - ); - - //Test ReRankDocs > docs returned - - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50"); - params.add("rqq", "id:1^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "1"); - - assertQ(req(params), "*[count(//doc)=1]", - "//result/doc[1]/float[@name='id'][.='1.0']" - ); - - - - //Test with zero results - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "term_s:NNNN"); - params.add("rqq", "id:1^1000"); - params.add("fl", "id,score"); - params.add("start", "4"); - params.add("rows", "5"); - - assertQ(req(params), "*[count(//doc)=0]"); - - } - - @Test - public void testOverRank() throws Exception { - - assertU(delQ("*:*")); - assertU(commit()); - - //Test the scenario that where we rank more documents then we return. - - boolean scale = new Random().nextBoolean(); - - String[] doc = {"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc)); - - String[] doc1 = {"id","2", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc1)); - - String[] doc2 = {"id","3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc2)); - String[] doc3 = {"id","4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; - assertU(adoc(doc3)); - - - String[] doc4 = {"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc4)); - - String[] doc5 = {"id","6", "term_s","YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc5)); - - String[] doc6 = {"id","7", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc6)); - - - String[] doc7 = {"id","8", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc7)); - - String[] doc8 = {"id","9", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc8)); - String[] doc9 = {"id","10", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; - assertU(adoc(doc9)); - - String[] doc10 = {"id","11", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc10)); - assertU(commit()); - - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=11 reRankWeight=2 scale="+scale+"}"); - params.add("df", "TEXT"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60 id:7^70 id:8^80 id:9^90 id:10^100 id:11^110"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "2"); - - assertQ(req(params), "*[count(//doc)=2]", - "//result/doc[1]/float[@name='id'][.='8.0']", - "//result/doc[2]/float[@name='id'][.='2.0']" - ); - - //Test Elevation - /* - params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=11 reRankWeight=2 scale="+scale+"}"); - params.add("q", "{!edismax bq=$bqq1}*:*"); - params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60 id:7^70 id:8^80 id:9^90 id:10^100 id:11^110"); - params.add("rqq", "test_ti:50^1000"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "3"); - params.add("qt","/elevate"); - params.add("elevateIds", "1,4"); - - assertQ(req(params), "*[count(//doc)=3]", - "//result/doc[1]/float[@name='id'][.='1.0']", //Elevated - "//result/doc[2]/float[@name='id'][.='4.0']", //Elevated - "//result/doc[3]/float[@name='id'][.='8.0']"); //Boosted during rerank. - */ - - } - - @Test - public void testScale() throws Exception { - - assertU(delQ("*:*")); - assertU(commit()); - - String[] doc = {"id", "1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc)); - assertU(commit()); - String[] doc1 = {"id", "2", "term_s", "YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc1)); - - String[] doc2 = {"id", "3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc2)); - assertU(commit()); - String[] doc3 = {"id", "4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; - assertU(adoc(doc3)); - - String[] doc4 = {"id", "5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; - assertU(adoc(doc4)); - assertU(commit()); - String[] doc5 = {"id", "6", "term_s", "YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; - assertU(adoc(doc5)); - assertU(commit()); - - - //Calculate the scales manually - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=200 scale=false}"); - params.add("df", "TEXT"); - params.add("q", "term_s:YYYY"); - params.add("rqq", "{!edismax bf=$bff}id:(1 2 4 5 6)"); - params.add("bff", "field(test_ti)"); - params.add("fl", "id,score"); - params.add("start", "0"); - params.add("rows", "6"); - - SolrQueryRequest req = req(params); - SolrQueryResponse res = null; - - try { - res = queryAndResponse(null, req); - } finally { - req.close(); - } - - @SuppressWarnings("rawtypes") - NamedList vals = res.getValues(); - ResultContext resultContext = (ResultContext)vals.get("response"); - - DocList docs = resultContext.getDocList(); - DocIterator it = docs.iterator(); - float max = -Float.MAX_VALUE; - List scores = new ArrayList(); - - while(it.hasNext()) { - it.next(); - float score = it.score(); - max = Math.max(score, max); - scores.add(score); - } - - - float[] scaledScores = new float[scores.size()]; - - for(int i=0; i. + * #L% + */ + +package org.alfresco.solr.query; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.alfresco.solr.AbstractAlfrescoSolrIT; +import org.apache.lucene.util.LuceneTestCase; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.ResultContext; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + + +@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) +public class AlfrescoReRankQParserPluginIT extends AbstractAlfrescoSolrIT +{ + @BeforeClass + public static void beforeClass() throws Exception + { + initAlfrescoCore("schema-rerank.xml"); + } + @Before + public void setUp() throws Exception + { + + // if you override setUp or tearDown, you better call + // the super classes version + clearIndex(); + assertU(commit()); + } + + @Test + public void testReRankQueries() throws Exception { + + assertU(delQ("*:*")); + assertU(commit()); + + String[] doc = {"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc)); + assertU(commit()); + String[] doc1 = {"id","2", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc1)); + + String[] doc2 = {"id","3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc2)); + assertU(commit()); + String[] doc3 = {"id","4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; + assertU(adoc(doc3)); + + String[] doc4 = {"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc4)); + assertU(commit()); + String[] doc5 = {"id","6", "term_s","YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc5)); + assertU(commit()); + + + Random random = new Random(); + boolean scale = random.nextBoolean(); + + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=200 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "term_s:YYYY"); + params.add("rqq", "{!edismax bf=$bff}*:*"); + params.add("bff", "field(test_ti)"); + params.add("start", "0"); + params.add("rows", "6"); + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='3.0']", + "//result/doc[2]/float[@name='id'][.='4.0']", + "//result/doc[3]/float[@name='id'][.='2.0']", + "//result/doc[4]/float[@name='id'][.='6.0']", + "//result/doc[5]/float[@name='id'][.='1.0']", + "//result/doc[6]/float[@name='id'][.='5.0']" + ); + + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "{!edismax bq=$bqq2}*:*"); + params.add("bqq2", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='5.0']", + "//result/doc[4]/float[@name='id'][.='4.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + //Test with sort by score. + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "{!edismax bq=$bqq2}*:*"); + params.add("bqq2", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("sort", "score desc"); + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='5.0']", + "//result/doc[4]/float[@name='id'][.='4.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + + //Test with compound sort. + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "{!edismax bq=$bqq2}*:*"); + params.add("bqq2", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("sort", "score desc,test_ti asc"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='5.0']", + "//result/doc[4]/float[@name='id'][.='4.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + + //Test with elevation + /* + + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=50 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "{!edismax bq=$bqq2}*:*"); + params.add("bqq2", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("qt", "/elevate"); + params.add("elevateIds", "1"); + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='1.0']", + "//result/doc[2]/float[@name='id'][.='2.0']", + "//result/doc[3]/float[@name='id'][.='6.0']", + "//result/doc[4]/float[@name='id'][.='5.0']", + "//result/doc[5]/float[@name='id'][.='4.0']", + "//result/doc[6]/float[@name='id'][.='3.0']" + + ); + */ + + + //Test TermQuery rqq + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='5.0']", + "//result/doc[4]/float[@name='id'][.='4.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + + //Test Elevation + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "1,4"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='1.0']", //Elevated + "//result/doc[2]/float[@name='id'][.='4.0']", //Elevated + "//result/doc[3]/float[@name='id'][.='2.0']", //Boosted during rerank. + "//result/doc[4]/float[@name='id'][.='6.0']", + "//result/doc[5]/float[@name='id'][.='5.0']", + "//result/doc[6]/float[@name='id'][.='3.0']" + ); + */ + + //Test Elevation swapped + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "4,1"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='4.0']", //Elevated + "//result/doc[2]/float[@name='id'][.='1.0']", //Elevated + "//result/doc[3]/float[@name='id'][.='2.0']", //Boosted during rerank. + "//result/doc[4]/float[@name='id'][.='6.0']", + "//result/doc[5]/float[@name='id'][.='5.0']", + "//result/doc[6]/float[@name='id'][.='3.0']" + ); + */ + + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=4 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "4,1"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='4.0']", //Elevated + "//result/doc[2]/float[@name='id'][.='1.0']", //Elevated + "//result/doc[3]/float[@name='id'][.='6.0']", + "//result/doc[4]/float[@name='id'][.='5.0']", + "//result/doc[5]/float[@name='id'][.='3.0']", + "//result/doc[6]/float[@name='id'][.='2.0']" //Not in reRankeDocs + ); + + */ + + + //Test Elevation with start beyond the rerank docs + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "4"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "4,1"); + + assertQ(req(params), "*[count(//doc)=2]", + "//result/doc[1]/float[@name='id'][.='3.0']", + "//result/doc[2]/float[@name='id'][.='2.0']" //Was not in reRankDocs + ); + */ + //Test Elevation with zero results + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}nada"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "4"); + params.add("rows", "10"); + params.add("qt","/elevate"); + params.add("elevateIds", "4,1"); + + assertQ(req(params), "*[count(//doc)=0]"); + */ + + + //Pass in reRankDocs lower then the length being collected. + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=1 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=0 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=2 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:4^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "10"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='5.0']", + "//result/doc[2]/float[@name='id'][.='6.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + //Test reRankWeight of 0, reranking will have no effect. + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=0 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "5"); + + assertQ(req(params), "*[count(//doc)=5]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']" + ); + + + + //Test range query + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "test_ti:[0 TO 2000]"); + params.add("rqq", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "6"); + + assertQ(req(params), "*[count(//doc)=5]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='2.0']", + "//result/doc[5]/float[@name='id'][.='1.0']" + ); + + //Test range query embedded in larger query + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "*:* OR test_ti:[0 TO 2000]"); + params.add("rqq", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "6"); + + assertQ(req(params), "*[count(//doc)=6]", + "//result/doc[1]/float[@name='id'][.='6.0']", + "//result/doc[2]/float[@name='id'][.='5.0']", + "//result/doc[3]/float[@name='id'][.='4.0']", + "//result/doc[4]/float[@name='id'][.='3.0']", + "//result/doc[5]/float[@name='id'][.='2.0']", + "//result/doc[6]/float[@name='id'][.='1.0']" + ); + + + //Test with start beyond reRankDocs + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60"); + params.add("rqq", "id:1^1000"); + params.add("fl", "id,score"); + params.add("start", "4"); + params.add("rows", "5"); + + assertQ(req(params), "*[count(//doc)=2]", + "//result/doc[1]/float[@name='id'][.='2.0']", + "//result/doc[2]/float[@name='id'][.='1.0']" + ); + + //Test ReRankDocs > docs returned + + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=6 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50"); + params.add("rqq", "id:1^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "1"); + + assertQ(req(params), "*[count(//doc)=1]", + "//result/doc[1]/float[@name='id'][.='1.0']" + ); + + + + //Test with zero results + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=3 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "term_s:NNNN"); + params.add("rqq", "id:1^1000"); + params.add("fl", "id,score"); + params.add("start", "4"); + params.add("rows", "5"); + + assertQ(req(params), "*[count(//doc)=0]"); + + } + + @Test + public void testOverRank() throws Exception { + + assertU(delQ("*:*")); + assertU(commit()); + + //Test the scenario that where we rank more documents then we return. + + boolean scale = new Random().nextBoolean(); + + String[] doc = {"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc)); + + String[] doc1 = {"id","2", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc1)); + + String[] doc2 = {"id","3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc2)); + String[] doc3 = {"id","4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; + assertU(adoc(doc3)); + + + String[] doc4 = {"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc4)); + + String[] doc5 = {"id","6", "term_s","YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc5)); + + String[] doc6 = {"id","7", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc6)); + + + String[] doc7 = {"id","8", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc7)); + + String[] doc8 = {"id","9", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc8)); + String[] doc9 = {"id","10", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; + assertU(adoc(doc9)); + + String[] doc10 = {"id","11", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc10)); + assertU(commit()); + + + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=11 reRankWeight=2 scale="+scale+"}"); + params.add("df", "TEXT"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60 id:7^70 id:8^80 id:9^90 id:10^100 id:11^110"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "2"); + + assertQ(req(params), "*[count(//doc)=2]", + "//result/doc[1]/float[@name='id'][.='8.0']", + "//result/doc[2]/float[@name='id'][.='2.0']" + ); + + //Test Elevation + /* + params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=11 reRankWeight=2 scale="+scale+"}"); + params.add("q", "{!edismax bq=$bqq1}*:*"); + params.add("bqq1", "id:1^10 id:2^20 id:3^30 id:4^40 id:5^50 id:6^60 id:7^70 id:8^80 id:9^90 id:10^100 id:11^110"); + params.add("rqq", "test_ti:50^1000"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "3"); + params.add("qt","/elevate"); + params.add("elevateIds", "1,4"); + + assertQ(req(params), "*[count(//doc)=3]", + "//result/doc[1]/float[@name='id'][.='1.0']", //Elevated + "//result/doc[2]/float[@name='id'][.='4.0']", //Elevated + "//result/doc[3]/float[@name='id'][.='8.0']"); //Boosted during rerank. + */ + + } + + @Test + public void testScale() throws Exception { + + assertU(delQ("*:*")); + assertU(commit()); + + String[] doc = {"id", "1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc)); + assertU(commit()); + String[] doc1 = {"id", "2", "term_s", "YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc1)); + + String[] doc2 = {"id", "3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc2)); + assertU(commit()); + String[] doc3 = {"id", "4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"}; + assertU(adoc(doc3)); + + String[] doc4 = {"id", "5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"}; + assertU(adoc(doc4)); + assertU(commit()); + String[] doc5 = {"id", "6", "term_s", "YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"}; + assertU(adoc(doc5)); + assertU(commit()); + + + //Calculate the scales manually + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add("rq", "{!alfrescoReRank reRankQuery=$rqq reRankDocs=200 scale=false}"); + params.add("df", "TEXT"); + params.add("q", "term_s:YYYY"); + params.add("rqq", "{!edismax bf=$bff}id:(1 2 4 5 6)"); + params.add("bff", "field(test_ti)"); + params.add("fl", "id,score"); + params.add("start", "0"); + params.add("rows", "6"); + + SolrQueryRequest req = req(params); + SolrQueryResponse res = null; + + try { + res = queryAndResponse(null, req); + } finally { + req.close(); + } + + @SuppressWarnings("rawtypes") + NamedList vals = res.getValues(); + ResultContext resultContext = (ResultContext)vals.get("response"); + + DocList docs = resultContext.getDocList(); + DocIterator it = docs.iterator(); + float max = -Float.MAX_VALUE; + List scores = new ArrayList(); + + while(it.hasNext()) { + it.next(); + float score = it.score(); + max = Math.max(score, max); + scores.add(score); + } + + + float[] scaledScores = new float[scores.size()]; + + for(int i=0; i. - * #L% - */ - -package org.alfresco.solr.query; - -import org.alfresco.repo.search.adaptor.QueryConstants; -import org.alfresco.solr.AbstractAlfrescoSolrIT; -import org.apache.lucene.util.LuceneTestCase; -import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) -@SolrTestCaseJ4.SuppressSSL - -public class SolrAuthIT extends AbstractAlfrescoSolrIT -{ - - @BeforeClass - public static void beforeClass() throws Exception - { - initAlfrescoCore("schema.xml"); - } - - @Before - public void setUp() throws Exception - { - // if you override setUp or tearDown, you better call - // the super classes version - //clearIndex(); - //assertU(commit()); - } - - @Before - public void loadAuth() throws Exception - { - assertU(delQ("*:*")); - assertU(commit()); - - String[] acldoc = {"id", "100", QueryConstants.FIELD_VERSION,"0","READER", "GROUP_R1", "READER", "GROUP_R2", "ACLID", "5000"}; - assertU(adoc(acldoc)); - assertU(commit()); - - String[] acldoc1 = {"id", "101", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R4", "ACLID", "6000", "DENIED", "GROUP_D1"}; - assertU(adoc(acldoc1)); - assertU(commit()); - - String[] acldoc2 = {"id", "102", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R2", "ACLID", "7000"}; - assertU(adoc(acldoc2)); - assertU(commit()); - - String[] acldoc3 = {"id", "103", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R5", "ACLID", "8000"}; - assertU(adoc(acldoc3)); - assertU(commit()); - - String[] acldoc4 = {"id", "104", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R5", "READER", "GROUP_R1", "ACLID", "9000"}; - assertU(adoc(acldoc4)); - assertU(commit()); - - - //Index Main Documents - String[] doc = {"id", "1", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "5000", "OWNER", "jim"}; - assertU(adoc(doc)); - assertU(commit()); - String[] doc1 = {"id", "2", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "6000", "OWNER", "dave"}; - assertU(adoc(doc1)); - - String[] doc2 = {"id", "3", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "7000", "OWNER", "mary"}; - assertU(adoc(doc2)); - assertU(commit()); - String[] doc3 = {"id", "4", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "8000", "OWNER", "bill"}; - assertU(adoc(doc3)); - - String[] doc4 = {"id", "5", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "9000", "OWNER", "steve"}; - assertU(adoc(doc4)); - assertU(commit()); - String[] doc5 = {"id", "6", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "10000", "OWNER", "sara"}; - assertU(adoc(doc5)); - assertU(commit()); - } - @Test - public void testAuthInFilter() - { - testAuth("false"); - } - @Test - public void testAuthPostFilter() - { - testAuth("true"); - } - - public void testAuth(String postFilter){ - System.setProperty("alfresco.postfilter", postFilter); - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq", "{!afts}AUTHORITY_FILTER_FROM_JSON"); - SolrServletRequest req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\" ], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=3]", - "//result/doc[1]/str[@name='id'][.='1']", - "//result/doc[2]/str[@name='id'][.='2']", - "//result/doc[3]/str[@name='id'][.='3']"); - - - //Test with owner - params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); - req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\", \"steve\" ], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=4]", - "//result/doc[1]/str[@name='id'][.='1']", - "//result/doc[2]/str[@name='id'][.='2']", - "//result/doc[3]/str[@name='id'][.='3']", - "//result/doc[4]/str[@name='id'][.='5']"); - // Test Deny - - params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); - req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\", \"GROUP_D1\"], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=2]", - "//result/doc[1]/str[@name='id'][.='1']", - "//result/doc[2]/str[@name='id'][.='3']"); - - //Test global read authority - params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); - req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"ROLE_ADMINISTRATOR\"], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=6]"); - - //Test zero hits - params = new ModifiableSolrParams(); - params.add("q", "t1:YYYY"); - params.add("qt", "/afts"); - params.add("start", "0"); - params.add("rows", "6"); - params.add("sort", "id asc"); - params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); - req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"blah\"], \"tenants\": [ \"\" ]}"); - assertQ(req, "*[count(//doc)=0]"); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.query; + +import org.alfresco.repo.search.adaptor.QueryConstants; +import org.alfresco.solr.AbstractAlfrescoSolrIT; +import org.apache.lucene.util.LuceneTestCase; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"}) +@SolrTestCaseJ4.SuppressSSL + +public class SolrAuthIT extends AbstractAlfrescoSolrIT +{ + + @BeforeClass + public static void beforeClass() throws Exception + { + initAlfrescoCore("schema.xml"); + } + + @Before + public void setUp() throws Exception + { + // if you override setUp or tearDown, you better call + // the super classes version + //clearIndex(); + //assertU(commit()); + } + + @Before + public void loadAuth() throws Exception + { + assertU(delQ("*:*")); + assertU(commit()); + + String[] acldoc = {"id", "100", QueryConstants.FIELD_VERSION,"0","READER", "GROUP_R1", "READER", "GROUP_R2", "ACLID", "5000"}; + assertU(adoc(acldoc)); + assertU(commit()); + + String[] acldoc1 = {"id", "101", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R4", "ACLID", "6000", "DENIED", "GROUP_D1"}; + assertU(adoc(acldoc1)); + assertU(commit()); + + String[] acldoc2 = {"id", "102", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R2", "ACLID", "7000"}; + assertU(adoc(acldoc2)); + assertU(commit()); + + String[] acldoc3 = {"id", "103", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R3", "READER", "GROUP_R5", "ACLID", "8000"}; + assertU(adoc(acldoc3)); + assertU(commit()); + + String[] acldoc4 = {"id", "104", QueryConstants.FIELD_VERSION,"0", "READER", "GROUP_R5", "READER", "GROUP_R1", "ACLID", "9000"}; + assertU(adoc(acldoc4)); + assertU(commit()); + + + //Index Main Documents + String[] doc = {"id", "1", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "5000", "OWNER", "jim"}; + assertU(adoc(doc)); + assertU(commit()); + String[] doc1 = {"id", "2", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "6000", "OWNER", "dave"}; + assertU(adoc(doc1)); + + String[] doc2 = {"id", "3", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "7000", "OWNER", "mary"}; + assertU(adoc(doc2)); + assertU(commit()); + String[] doc3 = {"id", "4", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "8000", "OWNER", "bill"}; + assertU(adoc(doc3)); + + String[] doc4 = {"id", "5", QueryConstants.FIELD_VERSION,"0","content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "9000", "OWNER", "steve"}; + assertU(adoc(doc4)); + assertU(commit()); + String[] doc5 = {"id", "6", QueryConstants.FIELD_VERSION,"0", "content@s___t@{http://www.alfresco.org/model/content/1.0}content", "YYYY", "ACLID", "10000", "OWNER", "sara"}; + assertU(adoc(doc5)); + assertU(commit()); + } + @Test + public void testAuthInFilter() + { + testAuth("false"); + } + @Test + public void testAuthPostFilter() + { + testAuth("true"); + } + + public void testAuth(String postFilter){ + System.setProperty("alfresco.postfilter", postFilter); + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq", "{!afts}AUTHORITY_FILTER_FROM_JSON"); + SolrServletRequest req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\" ], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=3]", + "//result/doc[1]/str[@name='id'][.='1']", + "//result/doc[2]/str[@name='id'][.='2']", + "//result/doc[3]/str[@name='id'][.='3']"); + + + //Test with owner + params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); + req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\", \"steve\" ], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=4]", + "//result/doc[1]/str[@name='id'][.='1']", + "//result/doc[2]/str[@name='id'][.='2']", + "//result/doc[3]/str[@name='id'][.='3']", + "//result/doc[4]/str[@name='id'][.='5']"); + // Test Deny + + params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); + req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"GROUP_R2\",\"GROUP_R4\", \"GROUP_D1\"], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=2]", + "//result/doc[1]/str[@name='id'][.='1']", + "//result/doc[2]/str[@name='id'][.='3']"); + + //Test global read authority + params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); + req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"ROLE_ADMINISTRATOR\"], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=6]"); + + //Test zero hits + params = new ModifiableSolrParams(); + params.add("q", "t1:YYYY"); + params.add("qt", "/afts"); + params.add("start", "0"); + params.add("rows", "6"); + params.add("sort", "id asc"); + params.add("fq","{!afts}AUTHORITY_FILTER_FROM_JSON"); + req = areq(params, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}], \"authorities\": [ \"blah\"], \"tenants\": [ \"\" ]}"); + assertQ(req, "*[count(//doc)=0]"); + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/UntokenisedFieldIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/UntokenisedFieldIT.java index f6bd5b518..34630d704 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/UntokenisedFieldIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/UntokenisedFieldIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/AbstractQParserPluginIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/AbstractQParserPluginIT.java index 8582a92d1..7865b4060 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/AbstractQParserPluginIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/AbstractQParserPluginIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/FieldNameEscapingIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/FieldNameEscapingIT.java index 29c2525a8..fe2bfb9d8 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/FieldNameEscapingIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/FieldNameEscapingIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -54,4 +54,4 @@ public class FieldNameEscapingIT extends AbstractQParserPluginIT implements Quer { assertAQuery("PATH:\"/cm:" + ISO9075.encode(DATASETS_PROVIDER.getNumericLocalName()) + "\"", 1); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/QParserPluginIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/QParserPluginIT.java index c36d3a715..ff7172625 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/QParserPluginIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/qparser/QParserPluginIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -711,4 +711,4 @@ public class QParserPluginIT extends AbstractQParserPluginIT implements QueryCon assertAQuery("@" + escape(QName.createQName(TEST_NAMESPACE, "period-ista")) + ":\"period|12\"", 1); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDefaultTextQueryIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDefaultTextQueryIT.java index 7a55fd177..8bdd3ca17 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDefaultTextQueryIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDefaultTextQueryIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDisjunctionIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDisjunctionIT.java index df71a6809..aa35829e4 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDisjunctionIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSDisjunctionIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -85,4 +85,4 @@ public class AFTSDisjunctionIT extends AbstractRequestHandlerIT assertResponseCardinality("cm:name:test* AND NOT (id:1)", 3); assertResponseCardinality("cm:name:test* AND (NOT id:1)", 3); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSIdentifierFieldsIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSIdentifierFieldsIT.java index 2a243da96..b2c776e9b 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSIdentifierFieldsIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSIdentifierFieldsIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -95,4 +95,4 @@ public class AFTSIdentifierFieldsIT extends AbstractRequestHandlerIT assertResponseCardinality("creator:'daniel*'", 1); assertResponseCardinality("creator:'daniel.armstrong'", 1); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRangeQueryIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRangeQueryIT.java index 80f147134..d17a66e20 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRangeQueryIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRangeQueryIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -106,4 +106,4 @@ public class AFTSRangeQueryIT extends AbstractRequestHandlerIT assertResponseCardinality("cm:creator:[Luigi2 TO Mario]", 1); assertResponseCardinality("cm:creator:[* TO Mario]", 2); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRequestHandlerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRequestHandlerIT.java index 28ab73425..ce6557fe8 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRequestHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AFTSRequestHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -917,4 +917,4 @@ public class AFTSRequestHandlerIT extends AbstractRequestHandlerIT implements Qu assertResponseCardinality("ANAME:0/" + DATASETS_PROVIDER.getNode03().getId(), 0); assertResponseCardinality("ANAME:1/" + TEST_ROOT_NODEREF.getId()+"/" + DATASETS_PROVIDER.getNode01().getId(), 2); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AbstractRequestHandlerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AbstractRequestHandlerIT.java index f47195f06..ac44c1b71 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AbstractRequestHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/AbstractRequestHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/MNTIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/MNTIT.java index 3730e2060..3dbb2a977 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/MNTIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/afts/requestHandler/MNTIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -92,4 +92,4 @@ public class MNTIT extends AbstractRequestHandlerIT assertResponseCardinality("\"AnalystName\" AND !\"AnalystName Craig\"", 1); assertResponseCardinality("cm:name:\"BASF*.txt\"", 4); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/AlfrescoCMISQParserPluginIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/AlfrescoCMISQParserPluginIT.java index f1527619e..1d8a4e6e4 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/AlfrescoCMISQParserPluginIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/AlfrescoCMISQParserPluginIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -795,4 +795,4 @@ public class AlfrescoCMISQParserPluginIT extends LoadCMISData implements QueryCo assertQ(qurySolr("SELECT * FROM cmis:document where contains('\\'two\\' \\'zebra\\'')"), expectedDocCount(1)); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/CmisIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/CmisIT.java index f8ec1c897..0a6c2dbb9 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/CmisIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/CmisIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/LoadCMISData.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/LoadCMISData.java index af4bf1242..78da12105 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/LoadCMISData.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/LoadCMISData.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/SortCMISTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/SortCMISTest.java index f706d379e..e644b1658 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/SortCMISTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/SortCMISTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/TrieFieldsCMISTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/TrieFieldsCMISTest.java index 564f75afb..37b040cd7 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/TrieFieldsCMISTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/query/cmis/TrieFieldsCMISTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/CompositeReaderTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/CompositeReaderTest.java index bf5c46698..eaffa0a52 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/CompositeReaderTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/CompositeReaderTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStreamTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStreamTest.java index 4c5e7a381..87968bb54 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStreamTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/schema/highlight/LanguagePrefixedTokenStreamTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/servlet/Solr4X509ServletFilterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/servlet/Solr4X509ServletFilterTest.java index 764322826..dd2ac4d5c 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/servlet/Solr4X509ServletFilterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/servlet/Solr4X509ServletFilterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ACLIDMurmurRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ACLIDMurmurRouterTest.java index 928802913..c3ef0d2fb 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ACLIDMurmurRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ACLIDMurmurRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -136,4 +136,4 @@ public class ACLIDMurmurRouterTest assertTrue(aclDistributionMap.values().toString() + ", SD = " + aclsDeviation, aclsDeviation/(howManyDocumentsPerShard) * 100 < 30); assertTrue(nodeDistributionMap.values().toString() + ", SD = " + nodesDeviation,nodesDeviation/(howManyDocumentsPerShard) * 100 < 30); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclModCountRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclModCountRouterIT.java index 4e793bfe6..07b0612f1 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclModCountRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclModCountRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -130,4 +130,4 @@ public class AclModCountRouterIT assertEquals(shardIdentifiers.length, nodeDistributionMap.size()); nodeDistributionMap.forEach((k, v) -> assertEquals(howManyDocumentsPerShard, v.intValue())); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ActivatableTrackerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ActivatableTrackerTest.java index 38f6e30f0..33bddf27e 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ActivatableTrackerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ActivatableTrackerTest.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 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% diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerExceptionIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerExceptionIT.java index 4794295e9..6e5ed57d8 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerExceptionIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerExceptionIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerIT.java index b83e6596d..3e0d935be 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerRollbackIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerRollbackIT.java index 70e29fa84..af1595563 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerRollbackIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerRollbackIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerStateIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerStateIT.java index a2fa9127d..d45fe0f9e 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerStateIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AlfrescoSolrTrackerStateIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -203,4 +203,4 @@ public class AlfrescoSolrTrackerStateIT extends AbstractAlfrescoSolrIT makeSureTransactionHasBeenIndexed(txn.getId()); makeSureNodesHaveBeenIndexed(data.getKey().size(), sampleTextContent); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/CascadingIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/CascadingIT.java index 7e10fb4d3..d44a0cad6 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/CascadingIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/CascadingIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -211,4 +211,4 @@ public class CascadingIT extends AbstractAlfrescoSolrIT Query waitForQuery = builder.build(); waitForDocCount(waitForQuery, 1, MAX_WAIT_TIME); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentPropertyValueTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentPropertyValueTrackerIT.java index 7dba73fa0..87cc9c270 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentPropertyValueTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentPropertyValueTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -127,4 +127,4 @@ public class ContentPropertyValueTrackerIT extends AbstractAlfrescoDistributedIT // Check the document is still indexed after the update. waitForDocCount(new TermQuery(new Term(authorField, authorAfterUpdate)), 1, MAX_WAIT_TIME); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentTrackerIT.java index c435721bc..c574311d1 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ContentTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRangeRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRangeRouterTest.java index 659e7bda1..b2596cdba 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRangeRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRangeRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -93,4 +93,4 @@ public class DBIDRangeRouterTest when(node.getId()).thenReturn(543L); assertTrue(router.routeNode(randomizer.nextInt(), randomizer.nextInt(), node)); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRouterTest.java index 0d1e1fdda..1de135a26 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DBIDRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -136,4 +136,4 @@ public class DBIDRouterTest nodeDistributionMap.values().toString() + ", SD = " + deviation + ", SD_NORM = " + norm + "%", norm < 30); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DateMonthRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DateMonthRouterIT.java index 5b1afcec9..247d8da95 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DateMonthRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DateMonthRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -148,4 +148,4 @@ public class DateMonthRouterIT deviation/(howManyDocuments/shardCount) * 100 < 30); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAclIdAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAclIdAlfrescoSolrTrackerIT.java index e6a620e7f..d8e94c226 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAclIdAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAclIdAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrJsonIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrJsonIT.java index 9f51540c1..381e8f517 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrJsonIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrJsonIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerIT.java index 1b36f32fa..992e02738 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -173,4 +173,4 @@ public class DistributedAlfrescoSolrTrackerIT extends AbstractAlfrescoDistribute waitForDocCountAllCores(new TermQuery(new Term(QueryConstants.FIELD_READER, "joel")), numAcls+1, 80000); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerLargeTxnIdIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerLargeTxnIdIT.java index 9c23adb60..c0b96edd4 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerLargeTxnIdIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerLargeTxnIdIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -126,4 +126,4 @@ public class DistributedAlfrescoSolrTrackerLargeTxnIdIT extends AbstractAlfresco waitForDocCountAllCores(waitForQuery, 1, 80000); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerRaceIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerRaceIT.java index 95264a10d..6015171ee 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerRaceIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerRaceIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -151,4 +151,4 @@ public class DistributedAlfrescoSolrTrackerRaceIT extends AbstractAlfrescoDistri "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}]}", params("q", "t1:world", "qt", "/afts", "shards.qt", "/afts", "start", "0", "rows", "6", "sort", "id asc")); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerStateIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerStateIT.java index ee3e0d973..610022573 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerStateIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoSolrTrackerStateIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -193,4 +193,4 @@ public class DistributedAlfrescoSolrTrackerStateIT extends AbstractAlfrescoDistr throw new RuntimeException(exception); } } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoTrackerWithDelayedTransactionsIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoTrackerWithDelayedTransactionsIT.java index bead5eca6..66a2e2d92 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoTrackerWithDelayedTransactionsIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedAlfrescoTrackerWithDelayedTransactionsIT.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 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% diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedCascadeTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedCascadeTrackerIT.java index e207405a3..c572dd368 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedCascadeTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedCascadeTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -205,4 +205,4 @@ public class DistributedCascadeTrackerIT extends AbstractAlfrescoDistributedIT prop.put("shard.method", "DB_ID_RANGE"); return prop; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateAbstractSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateAbstractSolrTrackerIT.java index 4cfd67260..266699084 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateAbstractSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateAbstractSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateMonthAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateMonthAlfrescoSolrTrackerIT.java index bdae45105..43a797b24 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateMonthAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateMonthAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateQuarterAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateQuarterAlfrescoSolrTrackerIT.java index 3cd130313..f2533c8e2 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateQuarterAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateQuarterAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateSplitYearAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateSplitYearAlfrescoSolrTrackerIT.java index a1bf5f098..d1aac24b2 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateSplitYearAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDateSplitYearAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDbidRangeAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDbidRangeAlfrescoSolrTrackerIT.java index e7162dc28..fc5d52c90 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDbidRangeAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedDbidRangeAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExpandDbidRangeAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExpandDbidRangeAlfrescoSolrTrackerIT.java index f5ac6b8e4..a1a234203 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExpandDbidRangeAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExpandDbidRangeAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardIdWithStaticPropertyRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardIdWithStaticPropertyRouterIT.java index ce321be17..302c19bb2 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardIdWithStaticPropertyRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardIdWithStaticPropertyRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -170,4 +170,4 @@ public class DistributedExplicitShardIdWithStaticPropertyRouterIT extends Abstra prop.put("shard.method", ShardMethodEnum.LAST_REGISTERED_INDEXING_SHARD.toString()); return prop; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardRoutingTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardRoutingTrackerIT.java index 039c5d07e..e9f269a95 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardRoutingTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedExplicitShardRoutingTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedPropertyBasedAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedPropertyBasedAlfrescoSolrTrackerIT.java index 1d066c244..979c90774 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedPropertyBasedAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedPropertyBasedAlfrescoSolrTrackerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedRemovePropertiesAlfrescoSolrTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedRemovePropertiesAlfrescoSolrTrackerIT.java index db37d4a4d..2aebe0b26 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedRemovePropertiesAlfrescoSolrTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DistributedRemovePropertiesAlfrescoSolrTrackerIT.java @@ -2,23 +2,23 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 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% @@ -111,4 +111,4 @@ public class DistributedRemovePropertiesAlfrescoSolrTrackerIT extends AbstractAl // Check that author is removed from index. waitForDocCount(new TermQuery(new Term(authorField, author)), 0, MAX_WAIT_TIME); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DocRouterFactoryTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DocRouterFactoryTest.java index ea5fc350a..a1a13e8d0 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DocRouterFactoryTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/DocRouterFactoryTest.java @@ -1,68 +1,68 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker; - -import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_RANGE_KEY; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.Properties; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.index.shard.ShardMethodEnum; -import org.junit.Test; - -/** Unit tests for the {@link DocRouterFactory}. */ -public class DocRouterFactoryTest -{ - /** Check that a DB_ID_RANGE router can be created. */ - @Test - public void testDBIDRANGEWithShardRangeKey() - { - Properties mockProperties = mock(Properties.class); - when(mockProperties.containsKey(SHARD_RANGE_KEY)).thenReturn(true); - when(mockProperties.getProperty(SHARD_RANGE_KEY)).thenReturn("100000000-150000000"); - - // Call the method under test. - DocRouter docRouter = DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE); - - assertTrue("Expected to get a DBIDRangeRouter.", docRouter instanceof DBIDRangeRouter); - DBIDRangeRouter dbidRangeRouter = (DBIDRangeRouter) docRouter; - assertEquals("Unexpected start of range.", dbidRangeRouter.getStartRange(), 100000000L); - assertEquals("Unexpected end of range.", dbidRangeRouter.getEndRange(), 150000000L); - } - - /** Check that an exception is raised if the range information is missing. */ - @Test(expected = AlfrescoRuntimeException.class) - public void testDBIDRANGEWithoutShardRangeKey() - { - Properties mockProperties = mock(Properties.class); - DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker; + +import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_RANGE_KEY; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Properties; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.index.shard.ShardMethodEnum; +import org.junit.Test; + +/** Unit tests for the {@link DocRouterFactory}. */ +public class DocRouterFactoryTest +{ + /** Check that a DB_ID_RANGE router can be created. */ + @Test + public void testDBIDRANGEWithShardRangeKey() + { + Properties mockProperties = mock(Properties.class); + when(mockProperties.containsKey(SHARD_RANGE_KEY)).thenReturn(true); + when(mockProperties.getProperty(SHARD_RANGE_KEY)).thenReturn("100000000-150000000"); + + // Call the method under test. + DocRouter docRouter = DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE); + + assertTrue("Expected to get a DBIDRangeRouter.", docRouter instanceof DBIDRangeRouter); + DBIDRangeRouter dbidRangeRouter = (DBIDRangeRouter) docRouter; + assertEquals("Unexpected start of range.", dbidRangeRouter.getStartRange(), 100000000L); + assertEquals("Unexpected end of range.", dbidRangeRouter.getEndRange(), 150000000L); + } + + /** Check that an exception is raised if the range information is missing. */ + @Test(expected = AlfrescoRuntimeException.class) + public void testDBIDRANGEWithoutShardRangeKey() + { + Properties mockProperties = mock(Properties.class); + DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDRouterIT.java index f412528ea..f8d2a9962 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -182,4 +182,4 @@ public class ExplicitIDRouterIT // Asserts that all documents have been assigned to the explicit shard ID. assertEquals(shardCount * howManyDocumentsPerShard, nodeDistributionMap.get(node.getExplicitShardId()).intValue()); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDWithLRISRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDWithLRISRouterIT.java index f43e529d8..966b915c3 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDWithLRISRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitIDWithLRISRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -185,4 +185,4 @@ public class ExplicitIDWithLRISRouterIT nodeDistributionMap.values().toString() + ", SD = " + deviation + ", SD_NORM = " + norm + "%", norm < 30); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouterTest.java index 16454ff11..fafd0aec1 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithDynamicPropertyRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -155,4 +155,4 @@ public class ExplicitShardIdWithDynamicPropertyRouterTest assertFalse(router.routeNode(shardCount, shardInstance + 1, node)); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouterTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouterTest.java index ed5a10d64..0b2d5bfb4 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouterTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ExplicitShardIdWithStaticPropertyRouterTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -111,4 +111,4 @@ public class ExplicitShardIdWithStaticPropertyRouterTest assertFalse(router.routeNode(shardCount, shardInstance + 1, node)); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/MetadataTrackerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/MetadataTrackerTest.java index 55cb37764..163e30bad 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/MetadataTrackerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/MetadataTrackerTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ModelTrackerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ModelTrackerIT.java index fb09fa42d..69ec1f7c0 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ModelTrackerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/ModelTrackerIT.java @@ -1,200 +1,200 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Properties; - -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.service.namespace.QName; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.client.AlfrescoModel; -import org.alfresco.solr.client.AlfrescoModelDiff; -import org.alfresco.solr.client.AlfrescoModelDiff.TYPE; -import org.alfresco.solr.client.SOLRAPIClient; -import org.json.JSONException; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class ModelTrackerIT -{ - private ModelTracker modelTracker; - - @Mock - private SOLRAPIClient repositoryClient; - private String coreName = "theCoreName"; - @Mock - private InformationServer srv; - @Mock - private Properties props; - @Mock - private TrackerStats trackerStats; - - private static File alfrescoModelDir; - - @BeforeClass - public static void setUpBeforeClass() throws Exception - { - alfrescoModelDir = new File("target/testalfrescoModels"); - alfrescoModelDir.mkdir(); - } - - @AfterClass - public static void tearDownAfterClass() throws Exception - { - try { - File[] listFiles = alfrescoModelDir.listFiles(); - for (File file : listFiles) - { - file.delete(); - } - alfrescoModelDir.delete(); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - - @Before - public void setUp() throws Exception - { - when(props.getProperty("alfresco.stores", "workspace://SpacesStore")).thenReturn("workspace://SpacesStore"); - when(props.getProperty("alfresco.batch.count", "5000")).thenReturn("5000"); - when(props.getProperty("alfresco.maxLiveSearchers", "2")).thenReturn("2"); - when(props.getProperty("shard.count", "1")).thenReturn("1"); - when(props.getProperty("shard.instance", "0")).thenReturn("0"); - when(this.srv.getTrackerStats()).thenReturn(trackerStats); - System.setProperty("solr.model.dir", alfrescoModelDir.getAbsolutePath()); - this.modelTracker = new ModelTracker(null, props, repositoryClient, coreName, srv); - } - - @After - public void tearDown() throws Exception - { - } - - @Test - public void testDoTrack() throws AuthenticationException, IOException, JSONException - { - ModelTracker spiedModelTracker = spy(this.modelTracker); - spiedModelTracker.doTrack("AnIterationId"); - - verify(this.srv).getRegisteredSearcherCount(); - verify(spiedModelTracker).trackModels(false); - } - - @Test - public void testTrackModels() throws AuthenticationException, IOException, JSONException - { - final String name = setUpTestTrackModels(); - - this.modelTracker.trackModels(false); - - verifyTestTrackModels(name); - } - - private void verifyTestTrackModels(final String name) - { - verify(this.srv).afterInitModels(); - - assertTrue(alfrescoModelDir.isDirectory()); - - File[] modelRepresentations = alfrescoModelDir.listFiles(new FileFilter() - { - @Override - public boolean accept(File pathname) - { - return pathname.getName().startsWith(name); - } - }); - assertEquals(1, modelRepresentations.length); - } - - @SuppressWarnings("unchecked") - private String setUpTestTrackModels() throws AuthenticationException, IOException, JSONException - { - QName modelName = QName.createQName("qname"); - TYPE type = TYPE.CHANGED; - Long oldChecksum = new Long(0); - Long newChecksum = new Long(1); - AlfrescoModelDiff diff = new AlfrescoModelDiff(modelName, type, oldChecksum, newChecksum); - List modelDiffs = new ArrayList<>(); - modelDiffs.add(diff); - when(this.repositoryClient.getModelsDiff(any(String.class),any(List.class))).thenReturn(modelDiffs); - - final String name = "a model name"; - M2Model model = M2Model.createModel(name); - M2Model spiedModel = spy(model); - model.createNamespace("uri", "prefix"); - AlfrescoModel alfrescoModel = new AlfrescoModel(spiedModel, newChecksum); - when(this.repositoryClient.getModel(any(String.class),eq(modelName))).thenReturn(alfrescoModel); - - NamespaceDAO namespaceDao = mock(NamespaceDAO.class); - Collection values = new ArrayList<>(); - values.add("prefix"); - when(namespaceDao.getPrefixes(anyString())).thenReturn(values); - when(this.srv.getNamespaceDAO()).thenReturn(namespaceDao); - when(this.srv.getM2Model(modelName)).thenReturn(spiedModel); - when(this.srv.putModel(spiedModel)).thenReturn(true); - return name; - } - - @Test - public void testEnsureFirstModelSync() throws AuthenticationException, IOException, JSONException - { - final String name = setUpTestTrackModels(); - - this.modelTracker.ensureFirstModelSync(); - - verifyTestTrackModels(name); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Properties; + +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.service.namespace.QName; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.client.AlfrescoModel; +import org.alfresco.solr.client.AlfrescoModelDiff; +import org.alfresco.solr.client.AlfrescoModelDiff.TYPE; +import org.alfresco.solr.client.SOLRAPIClient; +import org.json.JSONException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ModelTrackerIT +{ + private ModelTracker modelTracker; + + @Mock + private SOLRAPIClient repositoryClient; + private String coreName = "theCoreName"; + @Mock + private InformationServer srv; + @Mock + private Properties props; + @Mock + private TrackerStats trackerStats; + + private static File alfrescoModelDir; + + @BeforeClass + public static void setUpBeforeClass() throws Exception + { + alfrescoModelDir = new File("target/testalfrescoModels"); + alfrescoModelDir.mkdir(); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception + { + try { + File[] listFiles = alfrescoModelDir.listFiles(); + for (File file : listFiles) + { + file.delete(); + } + alfrescoModelDir.delete(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + @Before + public void setUp() throws Exception + { + when(props.getProperty("alfresco.stores", "workspace://SpacesStore")).thenReturn("workspace://SpacesStore"); + when(props.getProperty("alfresco.batch.count", "5000")).thenReturn("5000"); + when(props.getProperty("alfresco.maxLiveSearchers", "2")).thenReturn("2"); + when(props.getProperty("shard.count", "1")).thenReturn("1"); + when(props.getProperty("shard.instance", "0")).thenReturn("0"); + when(this.srv.getTrackerStats()).thenReturn(trackerStats); + System.setProperty("solr.model.dir", alfrescoModelDir.getAbsolutePath()); + this.modelTracker = new ModelTracker(null, props, repositoryClient, coreName, srv); + } + + @After + public void tearDown() throws Exception + { + } + + @Test + public void testDoTrack() throws AuthenticationException, IOException, JSONException + { + ModelTracker spiedModelTracker = spy(this.modelTracker); + spiedModelTracker.doTrack("AnIterationId"); + + verify(this.srv).getRegisteredSearcherCount(); + verify(spiedModelTracker).trackModels(false); + } + + @Test + public void testTrackModels() throws AuthenticationException, IOException, JSONException + { + final String name = setUpTestTrackModels(); + + this.modelTracker.trackModels(false); + + verifyTestTrackModels(name); + } + + private void verifyTestTrackModels(final String name) + { + verify(this.srv).afterInitModels(); + + assertTrue(alfrescoModelDir.isDirectory()); + + File[] modelRepresentations = alfrescoModelDir.listFiles(new FileFilter() + { + @Override + public boolean accept(File pathname) + { + return pathname.getName().startsWith(name); + } + }); + assertEquals(1, modelRepresentations.length); + } + + @SuppressWarnings("unchecked") + private String setUpTestTrackModels() throws AuthenticationException, IOException, JSONException + { + QName modelName = QName.createQName("qname"); + TYPE type = TYPE.CHANGED; + Long oldChecksum = new Long(0); + Long newChecksum = new Long(1); + AlfrescoModelDiff diff = new AlfrescoModelDiff(modelName, type, oldChecksum, newChecksum); + List modelDiffs = new ArrayList<>(); + modelDiffs.add(diff); + when(this.repositoryClient.getModelsDiff(any(String.class),any(List.class))).thenReturn(modelDiffs); + + final String name = "a model name"; + M2Model model = M2Model.createModel(name); + M2Model spiedModel = spy(model); + model.createNamespace("uri", "prefix"); + AlfrescoModel alfrescoModel = new AlfrescoModel(spiedModel, newChecksum); + when(this.repositoryClient.getModel(any(String.class),eq(modelName))).thenReturn(alfrescoModel); + + NamespaceDAO namespaceDao = mock(NamespaceDAO.class); + Collection values = new ArrayList<>(); + values.add("prefix"); + when(namespaceDao.getPrefixes(anyString())).thenReturn(values); + when(this.srv.getNamespaceDAO()).thenReturn(namespaceDao); + when(this.srv.getM2Model(modelName)).thenReturn(spiedModel); + when(this.srv.putModel(spiedModel)).thenReturn(true); + return name; + } + + @Test + public void testEnsureFirstModelSync() throws AuthenticationException, IOException, JSONException + { + final String name = setUpTestTrackModels(); + + this.modelTracker.ensureFirstModelSync(); + + verifyTestTrackModels(name); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/PropertyRouterIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/PropertyRouterIT.java index 0c0774d96..33731a695 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/PropertyRouterIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/PropertyRouterIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -248,4 +248,4 @@ public class PropertyRouterIT nodeDistributionMap.values().toString() + ", SD = " + deviation + ", SD_NORM = " + norm + "%", norm < 30); } -} +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/SolrTrackerSchedulerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/SolrTrackerSchedulerTest.java index 9ba01ffff..9aa8ab969 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/SolrTrackerSchedulerTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/SolrTrackerSchedulerTest.java @@ -1,216 +1,216 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.util.Arrays; -import java.util.List; -import java.util.Properties; - -import org.alfresco.solr.AlfrescoCoreAdminHandler; -import org.alfresco.solr.InformationServer; -import org.alfresco.solr.client.SOLRAPIClient; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.quartz.CronTrigger; -import org.quartz.JobDetail; -import org.quartz.JobKey; -import org.quartz.Scheduler; -import org.quartz.SchedulerException; -import org.quartz.Trigger; -import org.quartz.impl.matchers.GroupMatcher; - - -@RunWith(MockitoJUnitRunner.class) -public class SolrTrackerSchedulerTest -{ - @Mock - private AlfrescoCoreAdminHandler adminHandler; - private SolrTrackerScheduler trackerScheduler; - private String CORE_NAME = "coreName"; - private Scheduler spiedQuartzScheduler; - @Mock - SOLRAPIClient client; - String coreName = "alfresco"; - @Mock - InformationServer informationServer; - Properties props; - @Before - public void setUp() throws Exception - { - this.trackerScheduler = new SolrTrackerScheduler(adminHandler); - this.spiedQuartzScheduler = spy(this.trackerScheduler.scheduler); - this.trackerScheduler.scheduler = spiedQuartzScheduler; - props = new Properties(); - props.put("alfresco.stores", "workspace://SpacesStore"); - props.put("alfresco.batch.count", "5000"); - props.put("alfresco.maxLiveSearchers", "2"); - props.put("shard.count", "1"); - props.put("shard.instance", "0"); - props.put("shard.method", "SHARD_METHOD_DBID"); - props.put("alfresco.template", ""); - props.put("alfresco.index.transformContent", "true"); - } - - @After - public void tearDown() throws Exception - { - if (this.trackerScheduler != null && !this.trackerScheduler.isShutdown()) - { - this.trackerScheduler.shutdown(); - } - } - - @Test - public void testAclSchedule() throws SchedulerException - { - String exp = "0/12 * * * * ? *"; - props.put("alfresco.acl.tracker.cron", exp); - AclTracker aclTracker = new AclTracker(props, client, coreName, informationServer); - this.trackerScheduler.schedule(aclTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } - @Test - public void testContentTrackerSchedule() throws SchedulerException - { - String exp = "0/5 * * * * ? *"; - props.put("alfresco.content.tracker.cron", exp); - ContentTracker contentTracker = new ContentTracker(props, client, coreName, informationServer); - this.trackerScheduler.schedule(contentTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } - - private void checkCronExpression(String exp) throws SchedulerException - { - for (JobKey jobKey : this.trackerScheduler.scheduler.getJobKeys(GroupMatcher.jobGroupEquals(SolrTrackerScheduler.SOLR_JOB_GROUP))) - { - List triggers = this.trackerScheduler.scheduler.getTriggersOfJob(jobKey); - CronTrigger t = (CronTrigger) triggers.get(0); - String cronExp = t.getCronExpression(); - Assert.assertEquals(exp, cronExp); - } - } - @Test - public void testMetaDataTrackerSchedule() throws SchedulerException - { - String exp = "0/4 * * * * ? *"; - props.put("alfresco.metadata.tracker.cron", exp); - MetadataTracker metadataTracker = new MetadataTracker(props, client, exp, informationServer); - this.trackerScheduler.schedule(metadataTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } - @Test - public void testSchedule() throws SchedulerException - { - String exp = "0/1 * * * * ? *"; - props.put("alfresco.cascade.tracker.cron",exp); - CascadeTracker cascadeTracker = new CascadeTracker(props, client, exp, informationServer); - this.trackerScheduler.schedule(cascadeTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } - @Test - public void testShutdown() throws SchedulerException - { - this.trackerScheduler.shutdown(); - assertTrue(trackerScheduler.isShutdown()); - } - - @Test - public void testDeleteTrackerJobs() throws SchedulerException - { - ContentTracker contentTracker = new ContentTracker(); - MetadataTracker metadataTracker = new MetadataTracker(); - AclTracker aclTracker = new AclTracker(); - this.trackerScheduler.deleteTrackerJobs(CORE_NAME, - Arrays.asList(new Tracker[] { contentTracker, metadataTracker, aclTracker })); - verify(spiedQuartzScheduler, times(3)).deleteJob(any(JobKey.class)); - } - - @Test - public void testDeleteTrackerJob() throws SchedulerException - { - ModelTracker modelTracker = new ModelTracker(); - this.trackerScheduler.deleteTrackerJob(CORE_NAME, modelTracker); - verify(spiedQuartzScheduler).deleteJob(any(JobKey.class)); - } - - @Test - public void testDeleteTrackerInstanceJob() throws SchedulerException - { - CascadeTracker cascadeTracker = new CascadeTracker(props, client, coreName, informationServer); - CascadeTracker cascadeTracker2 = new CascadeTracker(props, client, coreName, informationServer); - this.trackerScheduler.schedule(cascadeTracker, CORE_NAME, props); - - //Try deleting the same class but a different instance. It not possible. - this.trackerScheduler.deleteJobForTrackerInstance(CORE_NAME, cascadeTracker2); - verify(spiedQuartzScheduler, never()).deleteJob(any(JobKey.class)); - - //No try deleting the exact instance of the tracker class - this.trackerScheduler.deleteJobForTrackerInstance(CORE_NAME, cascadeTracker); - verify(spiedQuartzScheduler, times(1)).deleteJob(any(JobKey.class)); - - } - - @Test - public void newSchedulerIsNotShutdown() throws SchedulerException - { - assertFalse(trackerScheduler.isShutdown()); - } - - @Test - public void testPauseAll() throws SchedulerException - { - this.trackerScheduler.pauseAll(); - verify(this.spiedQuartzScheduler).pauseAll(); - } - @Test - public void testModeltTrackerSchedule() throws SchedulerException - { - String exp = "0/20 * * * * ? *"; - props.put("alfresco.model.tracker.cron", exp); - ModelTracker modelTracker = new ModelTracker("alfresco", props, client, CORE_NAME, informationServer); - this.trackerScheduler.schedule(modelTracker, CORE_NAME, props); - verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); - checkCronExpression(exp); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.alfresco.solr.AlfrescoCoreAdminHandler; +import org.alfresco.solr.InformationServer; +import org.alfresco.solr.client.SOLRAPIClient; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.quartz.CronTrigger; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.impl.matchers.GroupMatcher; + + +@RunWith(MockitoJUnitRunner.class) +public class SolrTrackerSchedulerTest +{ + @Mock + private AlfrescoCoreAdminHandler adminHandler; + private SolrTrackerScheduler trackerScheduler; + private String CORE_NAME = "coreName"; + private Scheduler spiedQuartzScheduler; + @Mock + SOLRAPIClient client; + String coreName = "alfresco"; + @Mock + InformationServer informationServer; + Properties props; + @Before + public void setUp() throws Exception + { + this.trackerScheduler = new SolrTrackerScheduler(adminHandler); + this.spiedQuartzScheduler = spy(this.trackerScheduler.scheduler); + this.trackerScheduler.scheduler = spiedQuartzScheduler; + props = new Properties(); + props.put("alfresco.stores", "workspace://SpacesStore"); + props.put("alfresco.batch.count", "5000"); + props.put("alfresco.maxLiveSearchers", "2"); + props.put("shard.count", "1"); + props.put("shard.instance", "0"); + props.put("shard.method", "SHARD_METHOD_DBID"); + props.put("alfresco.template", ""); + props.put("alfresco.index.transformContent", "true"); + } + + @After + public void tearDown() throws Exception + { + if (this.trackerScheduler != null && !this.trackerScheduler.isShutdown()) + { + this.trackerScheduler.shutdown(); + } + } + + @Test + public void testAclSchedule() throws SchedulerException + { + String exp = "0/12 * * * * ? *"; + props.put("alfresco.acl.tracker.cron", exp); + AclTracker aclTracker = new AclTracker(props, client, coreName, informationServer); + this.trackerScheduler.schedule(aclTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } + @Test + public void testContentTrackerSchedule() throws SchedulerException + { + String exp = "0/5 * * * * ? *"; + props.put("alfresco.content.tracker.cron", exp); + ContentTracker contentTracker = new ContentTracker(props, client, coreName, informationServer); + this.trackerScheduler.schedule(contentTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } + + private void checkCronExpression(String exp) throws SchedulerException + { + for (JobKey jobKey : this.trackerScheduler.scheduler.getJobKeys(GroupMatcher.jobGroupEquals(SolrTrackerScheduler.SOLR_JOB_GROUP))) + { + List triggers = this.trackerScheduler.scheduler.getTriggersOfJob(jobKey); + CronTrigger t = (CronTrigger) triggers.get(0); + String cronExp = t.getCronExpression(); + Assert.assertEquals(exp, cronExp); + } + } + @Test + public void testMetaDataTrackerSchedule() throws SchedulerException + { + String exp = "0/4 * * * * ? *"; + props.put("alfresco.metadata.tracker.cron", exp); + MetadataTracker metadataTracker = new MetadataTracker(props, client, exp, informationServer); + this.trackerScheduler.schedule(metadataTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } + @Test + public void testSchedule() throws SchedulerException + { + String exp = "0/1 * * * * ? *"; + props.put("alfresco.cascade.tracker.cron",exp); + CascadeTracker cascadeTracker = new CascadeTracker(props, client, exp, informationServer); + this.trackerScheduler.schedule(cascadeTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } + @Test + public void testShutdown() throws SchedulerException + { + this.trackerScheduler.shutdown(); + assertTrue(trackerScheduler.isShutdown()); + } + + @Test + public void testDeleteTrackerJobs() throws SchedulerException + { + ContentTracker contentTracker = new ContentTracker(); + MetadataTracker metadataTracker = new MetadataTracker(); + AclTracker aclTracker = new AclTracker(); + this.trackerScheduler.deleteTrackerJobs(CORE_NAME, + Arrays.asList(new Tracker[] { contentTracker, metadataTracker, aclTracker })); + verify(spiedQuartzScheduler, times(3)).deleteJob(any(JobKey.class)); + } + + @Test + public void testDeleteTrackerJob() throws SchedulerException + { + ModelTracker modelTracker = new ModelTracker(); + this.trackerScheduler.deleteTrackerJob(CORE_NAME, modelTracker); + verify(spiedQuartzScheduler).deleteJob(any(JobKey.class)); + } + + @Test + public void testDeleteTrackerInstanceJob() throws SchedulerException + { + CascadeTracker cascadeTracker = new CascadeTracker(props, client, coreName, informationServer); + CascadeTracker cascadeTracker2 = new CascadeTracker(props, client, coreName, informationServer); + this.trackerScheduler.schedule(cascadeTracker, CORE_NAME, props); + + //Try deleting the same class but a different instance. It not possible. + this.trackerScheduler.deleteJobForTrackerInstance(CORE_NAME, cascadeTracker2); + verify(spiedQuartzScheduler, never()).deleteJob(any(JobKey.class)); + + //No try deleting the exact instance of the tracker class + this.trackerScheduler.deleteJobForTrackerInstance(CORE_NAME, cascadeTracker); + verify(spiedQuartzScheduler, times(1)).deleteJob(any(JobKey.class)); + + } + + @Test + public void newSchedulerIsNotShutdown() throws SchedulerException + { + assertFalse(trackerScheduler.isShutdown()); + } + + @Test + public void testPauseAll() throws SchedulerException + { + this.trackerScheduler.pauseAll(); + verify(this.spiedQuartzScheduler).pauseAll(); + } + @Test + public void testModeltTrackerSchedule() throws SchedulerException + { + String exp = "0/20 * * * * ? *"; + props.put("alfresco.model.tracker.cron", exp); + ModelTracker modelTracker = new ModelTracker("alfresco", props, client, CORE_NAME, informationServer); + this.trackerScheduler.schedule(modelTracker, CORE_NAME, props); + verify(spiedQuartzScheduler).scheduleJob(any(JobDetail.class), any(Trigger.class)); + checkCronExpression(exp); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerJobTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerJobTest.java index a83158741..0146e5a79 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerJobTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerJobTest.java @@ -1,79 +1,79 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.quartz.JobDataMap; -import org.quartz.JobDetail; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; - -/** - * Tests for the {@link TrackerJob} class. - * - * @author Matt Ward - */ -@RunWith(MockitoJUnitRunner.class) -public class TrackerJobTest -{ - private TrackerJob trackerJob; // Class under test - private @Mock Tracker tracker; - private @Mock JobExecutionContext jec; - private @Mock JobDetail jobDetail; - private JobDataMap jobDataMap; - - @Before - public void setUp() throws Exception - - { - trackerJob = new TrackerJob(); - - jobDataMap = new JobDataMap(); - jobDataMap.put("TRACKER", tracker); - - when(jec.getJobDetail()).thenReturn(jobDetail); - when(jobDetail.getJobDataMap()).thenReturn(jobDataMap); - } - - @Test - public void canExecuteTrackerJob() throws JobExecutionException - { - trackerJob.execute(jec); - - // When the TrackerJob is triggered, then the Tracker's execute() method - // should be invoked. - verify(tracker).track(); - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * Tests for the {@link TrackerJob} class. + * + * @author Matt Ward + */ +@RunWith(MockitoJUnitRunner.class) +public class TrackerJobTest +{ + private TrackerJob trackerJob; // Class under test + private @Mock Tracker tracker; + private @Mock JobExecutionContext jec; + private @Mock JobDetail jobDetail; + private JobDataMap jobDataMap; + + @Before + public void setUp() throws Exception + + { + trackerJob = new TrackerJob(); + + jobDataMap = new JobDataMap(); + jobDataMap.put("TRACKER", tracker); + + when(jec.getJobDetail()).thenReturn(jobDetail); + when(jobDetail.getJobDataMap()).thenReturn(jobDataMap); + } + + @Test + public void canExecuteTrackerJob() throws JobExecutionException + { + trackerJob.execute(jec); + + // When the TrackerJob is triggered, then the Tracker's execute() method + // should be invoked. + verify(tracker).track(); + } + +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerRegistryTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerRegistryTest.java index 580b802af..d002b31c2 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerRegistryTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/TrackerRegistryTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactoryTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactoryTest.java index 46a016c21..5e89a44f7 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactoryTest.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/pool/DefaultTrackerPoolFactoryTest.java @@ -1,161 +1,161 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.tracker.pool; - -import static org.junit.Assert.assertEquals; - -import java.util.Properties; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Tests for the {@link DefaultTrackerPoolFactory}. - * - * @author Matt Ward - */ -public class DefaultTrackerPoolFactoryTest -{ - private DefaultTrackerPoolFactory poolFactory; - private Properties properties; - ThreadPoolExecutor tpe; - @Before - public void setup() - { - poolFactory = null; // Ensure we don't accidentally reuse between runs. - properties = new Properties(); - } - @After - public void teardown() - { - tpe.shutdownNow(); - tpe = null; - } - - @Test - public void testDefaults() - { - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "TrackerName"); - - tpe = poolFactory.create(); - - assertEquals(4, tpe.getCorePoolSize()); - assertEquals(4, tpe.getMaximumPoolSize()); - assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - - @Test - public void testNonDefaultProperties() - { - properties.put("alfresco.corePoolSize", "30"); - properties.put("alfresco.maximumPoolSize", "40"); - properties.put("alfresco.keepAliveTime", "200"); - - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "TrackerName"); - - tpe = poolFactory.create(); - - assertEquals(30, tpe.getCorePoolSize()); - assertEquals(40, tpe.getMaximumPoolSize()); - assertEquals(200, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testAclDefaultProperties() - { - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker"); - - tpe = poolFactory.create(); - assertEquals(4, tpe.getCorePoolSize()); - assertEquals(4, tpe.getMaximumPoolSize()); - assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testAclProperties() - { - properties.put("alfresco.acl.tracker.corePoolSize", "30"); - properties.put("alfresco.acl.tracker.maximumPoolSize", "40"); - properties.put("alfresco.acl.tracker.keepAliveTime", "200"); - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker"); - tpe = poolFactory.create(); - - assertEquals(30, tpe.getCorePoolSize()); - assertEquals(40, tpe.getMaximumPoolSize()); - assertEquals(200, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testContentDefaultProperties() - { - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "ContentTracker"); - - tpe = poolFactory.create(); - - assertEquals(4, tpe.getCorePoolSize()); - assertEquals(4, tpe.getMaximumPoolSize()); - assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testContentProperties() - { - properties.put("alfresco.content.tracker.corePoolSize", "100"); - properties.put("alfresco.content.tracker.maximumPoolSize", "140"); - properties.put("alfresco.content.tracker.keepAliveTime", "201"); - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "ContentTracker"); - tpe = poolFactory.create(); - - assertEquals(100, tpe.getCorePoolSize()); - assertEquals(140, tpe.getMaximumPoolSize()); - assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testMetaDataDefaultProperties() - { - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker"); - - tpe = poolFactory.create(); - - assertEquals(4, tpe.getCorePoolSize()); - assertEquals(4, tpe.getMaximumPoolSize()); - assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } - @Test - public void testMetaDataProperties() - { - properties.put("alfresco.metadata.tracker.corePoolSize", "100"); - properties.put("alfresco.metadata.tracker.maximumPoolSize", "140"); - properties.put("alfresco.metadata.tracker.keepAliveTime", "201"); - poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker"); - - tpe = poolFactory.create(); - - assertEquals(100, tpe.getCorePoolSize()); - assertEquals(140, tpe.getMaximumPoolSize()); - assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS)); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.tracker.pool; + +import static org.junit.Assert.assertEquals; + +import java.util.Properties; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link DefaultTrackerPoolFactory}. + * + * @author Matt Ward + */ +public class DefaultTrackerPoolFactoryTest +{ + private DefaultTrackerPoolFactory poolFactory; + private Properties properties; + ThreadPoolExecutor tpe; + @Before + public void setup() + { + poolFactory = null; // Ensure we don't accidentally reuse between runs. + properties = new Properties(); + } + @After + public void teardown() + { + tpe.shutdownNow(); + tpe = null; + } + + @Test + public void testDefaults() + { + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "TrackerName"); + + tpe = poolFactory.create(); + + assertEquals(4, tpe.getCorePoolSize()); + assertEquals(4, tpe.getMaximumPoolSize()); + assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + + @Test + public void testNonDefaultProperties() + { + properties.put("alfresco.corePoolSize", "30"); + properties.put("alfresco.maximumPoolSize", "40"); + properties.put("alfresco.keepAliveTime", "200"); + + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "TrackerName"); + + tpe = poolFactory.create(); + + assertEquals(30, tpe.getCorePoolSize()); + assertEquals(40, tpe.getMaximumPoolSize()); + assertEquals(200, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testAclDefaultProperties() + { + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker"); + + tpe = poolFactory.create(); + assertEquals(4, tpe.getCorePoolSize()); + assertEquals(4, tpe.getMaximumPoolSize()); + assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testAclProperties() + { + properties.put("alfresco.acl.tracker.corePoolSize", "30"); + properties.put("alfresco.acl.tracker.maximumPoolSize", "40"); + properties.put("alfresco.acl.tracker.keepAliveTime", "200"); + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker"); + tpe = poolFactory.create(); + + assertEquals(30, tpe.getCorePoolSize()); + assertEquals(40, tpe.getMaximumPoolSize()); + assertEquals(200, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testContentDefaultProperties() + { + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "ContentTracker"); + + tpe = poolFactory.create(); + + assertEquals(4, tpe.getCorePoolSize()); + assertEquals(4, tpe.getMaximumPoolSize()); + assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testContentProperties() + { + properties.put("alfresco.content.tracker.corePoolSize", "100"); + properties.put("alfresco.content.tracker.maximumPoolSize", "140"); + properties.put("alfresco.content.tracker.keepAliveTime", "201"); + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "ContentTracker"); + tpe = poolFactory.create(); + + assertEquals(100, tpe.getCorePoolSize()); + assertEquals(140, tpe.getMaximumPoolSize()); + assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testMetaDataDefaultProperties() + { + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker"); + + tpe = poolFactory.create(); + + assertEquals(4, tpe.getCorePoolSize()); + assertEquals(4, tpe.getMaximumPoolSize()); + assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } + @Test + public void testMetaDataProperties() + { + properties.put("alfresco.metadata.tracker.corePoolSize", "100"); + properties.put("alfresco.metadata.tracker.maximumPoolSize", "140"); + properties.put("alfresco.metadata.tracker.keepAliveTime", "201"); + poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker"); + + tpe = poolFactory.create(); + + assertEquals(100, tpe.getCorePoolSize()); + assertEquals(140, tpe.getMaximumPoolSize()); + assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS)); + } +} diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java index 3d47f504f..b2f69d937 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/transformer/AlfrescoFieldMapperTransformerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandlerIT.java b/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandlerIT.java index f237a7112..7ed7d0358 100644 --- a/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoLukeRequestHandlerIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoSearchHandlerFacetQueryIT.java b/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoSearchHandlerFacetQueryIT.java index dba98a7c5..d910cdd21 100644 --- a/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoSearchHandlerFacetQueryIT.java +++ b/search-services/alfresco-search/src/test/java/org/apache/solr/handler/component/AlfrescoSearchHandlerFacetQueryIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AclReport.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AclReport.java index 2bdf9891a..5e0cdc407 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AclReport.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AclReport.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AlfrescoClientDataModelServicesFactory.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AlfrescoClientDataModelServicesFactory.java index fdcff5877..6897461a4 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AlfrescoClientDataModelServicesFactory.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/AlfrescoClientDataModelServicesFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/BoundedDeque.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/BoundedDeque.java index decdbbf64..a4eb7dd89 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/BoundedDeque.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/BoundedDeque.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/IndexTrackingShutdownException.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/IndexTrackingShutdownException.java index 5462a746e..c528ea19e 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/IndexTrackingShutdownException.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/IndexTrackingShutdownException.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/InformationServerCollectionProvider.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/InformationServerCollectionProvider.java index 9ad50919c..a43cbcc3b 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/InformationServerCollectionProvider.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/InformationServerCollectionProvider.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/NodeReport.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/NodeReport.java index 52105b4b6..8bda05107 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/NodeReport.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/NodeReport.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/TrackerState.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/TrackerState.java index 1674d62cc..b7c2a9909 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/TrackerState.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/TrackerState.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/IOpenBitSet.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/IOpenBitSet.java index 180340658..a2ac19197 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/IOpenBitSet.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/IOpenBitSet.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -23,25 +23,25 @@ * along with Alfresco. If not, see . * #L% */ - -package org.alfresco.solr.adapters; - -/** - * The reason we have this interface is so that lucene-free dependent classes can be dependent on IOpenBitSet instead of the - * lucene-version-specific OpenBitSet. - * @author Ahmed Owian - */ -public interface IOpenBitSet -{ - + +package org.alfresco.solr.adapters; + +/** + * The reason we have this interface is so that lucene-free dependent classes can be dependent on IOpenBitSet instead of the + * lucene-version-specific OpenBitSet. + * @author Ahmed Owian + */ +public interface IOpenBitSet +{ + public void set(long txid); - + public void or(IOpenBitSet duplicatedTxInIndex); - + public long nextSetBit(long l); - + public long cardinality(); - + public boolean get(long i); - -} + +} \ No newline at end of file diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/ISimpleOrderedMap.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/ISimpleOrderedMap.java index b73dde5c1..645409e5d 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/ISimpleOrderedMap.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/adapters/ISimpleOrderedMap.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,17 +24,17 @@ * #L% */ -package org.alfresco.solr.adapters; - - -/** - * The reason we have this interface is so that lucene-free dependent classes can be dependent on ISimpleOrderedMap instead of the - * lucene-version-specific SimpleOrderedMap. - * @author Ahmed Owian - */ -public interface ISimpleOrderedMap -{ - - void add(String name, T val); - -} +package org.alfresco.solr.adapters; + + +/** + * The reason we have this interface is so that lucene-free dependent classes can be dependent on ISimpleOrderedMap instead of the + * lucene-version-specific SimpleOrderedMap. + * @author Ahmed Owian + */ +public interface ISimpleOrderedMap +{ + + void add(String name, T val); + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Acl.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Acl.java index 7bbbc00a9..ae9b54a6e 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Acl.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Acl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,59 +24,59 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * SOLR-side representation of basic ACL information. - * - * @since 4.0 - */ -public class Acl -{ - private final long aclChangeSetId; - private final long id; - - public Acl(long aclChangeSetId, long id) - { - this.aclChangeSetId = aclChangeSetId; - this.id = id; - } - - @Override - public String toString() - { - return "Acl [aclChangeSetId=" + aclChangeSetId + ", id=" + id + "]"; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + (int) (aclChangeSetId ^ (aclChangeSetId >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - Acl other = (Acl) obj; - if (aclChangeSetId != other.aclChangeSetId) return false; - if (id != other.id) return false; - return true; - } - - public long getAclChangeSetId() - { - return aclChangeSetId; - } - - public long getId() - { - return id; - } -} +package org.alfresco.solr.client; + +/** + * SOLR-side representation of basic ACL information. + * + * @since 4.0 + */ +public class Acl +{ + private final long aclChangeSetId; + private final long id; + + public Acl(long aclChangeSetId, long id) + { + this.aclChangeSetId = aclChangeSetId; + this.id = id; + } + + @Override + public String toString() + { + return "Acl [aclChangeSetId=" + aclChangeSetId + ", id=" + id + "]"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (int) (aclChangeSetId ^ (aclChangeSetId >>> 32)); + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Acl other = (Acl) obj; + if (aclChangeSetId != other.aclChangeSetId) return false; + if (id != other.id) return false; + return true; + } + + public long getAclChangeSetId() + { + return aclChangeSetId; + } + + public long getId() + { + return id; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSet.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSet.java index 2ab27de82..029d937c4 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSet.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSet.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,68 +24,68 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * SOLR-side representation of ACL ChangeSet information. - * - * @since 4.0 - */ -public class AclChangeSet -{ - private final long id; - private final long commitTimeMs; - private final int aclCount; - - public AclChangeSet(long id, long commitTimeMs, int aclCount) - { - super(); - this.id = id; - this.commitTimeMs = commitTimeMs; - this.aclCount = aclCount; - } - - @Override - public String toString() - { - return "AclChangeSet [id=" + id + ", commitTimeMs=" + commitTimeMs + ", aclCount=" - + aclCount + "]"; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + aclCount; - result = prime * result + (int) (commitTimeMs ^ (commitTimeMs >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - AclChangeSet other = (AclChangeSet) obj; - if (aclCount != other.aclCount) return false; - if (commitTimeMs != other.commitTimeMs) return false; - if (id != other.id) return false; - return true; - } - - public long getId() - { - return id; - } - public long getCommitTimeMs() - { - return commitTimeMs; - } - public int getAclCount() - { - return aclCount; - } -} +package org.alfresco.solr.client; + +/** + * SOLR-side representation of ACL ChangeSet information. + * + * @since 4.0 + */ +public class AclChangeSet +{ + private final long id; + private final long commitTimeMs; + private final int aclCount; + + public AclChangeSet(long id, long commitTimeMs, int aclCount) + { + super(); + this.id = id; + this.commitTimeMs = commitTimeMs; + this.aclCount = aclCount; + } + + @Override + public String toString() + { + return "AclChangeSet [id=" + id + ", commitTimeMs=" + commitTimeMs + ", aclCount=" + + aclCount + "]"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + aclCount; + result = prime * result + (int) (commitTimeMs ^ (commitTimeMs >>> 32)); + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + AclChangeSet other = (AclChangeSet) obj; + if (aclCount != other.aclCount) return false; + if (commitTimeMs != other.commitTimeMs) return false; + if (id != other.id) return false; + return true; + } + + public long getId() + { + return id; + } + public long getCommitTimeMs() + { + return commitTimeMs; + } + public int getAclCount() + { + return aclCount; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java index b3a54f7d2..052064051 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclReaders.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclReaders.java index 682ac418e..04f5c1db1 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclReaders.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclReaders.java @@ -1,125 +1,125 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -import static java.util.Collections.unmodifiableList; - -import java.util.ArrayList; -import java.util.List; - -/** - * SOLR-side representation of ACL Readers information. - * - * @since 4.0 - */ -public class AclReaders -{ - private final long id; - - private List readers; - - private final List denied; - - private final long aclChangeSetId; - - private final String tenantDomain; - - public AclReaders(long id, List readers, List denied, long aclChangeSetId, String tenantDomain) - { - this.id = id; - this.readers = (readers == null ? null : new ArrayList<>(readers)); - this.denied = (denied == null ? null : new ArrayList<>(denied)); - this.aclChangeSetId = aclChangeSetId; - this.tenantDomain = tenantDomain; - } - - @Override - public String toString() - { - return "AclReaders [id=" + id + ", readers=" + readers + ", denied=" + denied + ", tenantDomain=" + tenantDomain + "]"; - } - - /** - * ID should be enough for hashCode() and equals(). - */ - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + (int) (id ^ (id >>> 32)); - return result; - } - - /** - * ID should be enough for hashCode() and equals(). - */ - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - AclReaders other = (AclReaders) obj; - if (id != other.id) - return false; - return true; - } - - public long getId() - { - return id; - } - - public List getReaders() - { - return unmodifiableList(readers); - } - - public void setReaders(List readers) - { - this.readers = new ArrayList<>(readers); - } - - public List getDenied() - { - return unmodifiableList(denied); - } - - public long getAclChangeSetId() - { - return aclChangeSetId; - } - - public String getTenantDomain() - { - return tenantDomain; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +import static java.util.Collections.unmodifiableList; + +import java.util.ArrayList; +import java.util.List; + +/** + * SOLR-side representation of ACL Readers information. + * + * @since 4.0 + */ +public class AclReaders +{ + private final long id; + + private List readers; + + private final List denied; + + private final long aclChangeSetId; + + private final String tenantDomain; + + public AclReaders(long id, List readers, List denied, long aclChangeSetId, String tenantDomain) + { + this.id = id; + this.readers = (readers == null ? null : new ArrayList<>(readers)); + this.denied = (denied == null ? null : new ArrayList<>(denied)); + this.aclChangeSetId = aclChangeSetId; + this.tenantDomain = tenantDomain; + } + + @Override + public String toString() + { + return "AclReaders [id=" + id + ", readers=" + readers + ", denied=" + denied + ", tenantDomain=" + tenantDomain + "]"; + } + + /** + * ID should be enough for hashCode() and equals(). + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + /** + * ID should be enough for hashCode() and equals(). + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AclReaders other = (AclReaders) obj; + if (id != other.id) + return false; + return true; + } + + public long getId() + { + return id; + } + + public List getReaders() + { + return unmodifiableList(readers); + } + + public void setReaders(List readers) + { + this.readers = new ArrayList<>(readers); + } + + public List getDenied() + { + return unmodifiableList(denied); + } + + public long getAclChangeSetId() + { + return aclChangeSetId; + } + + public String getTenantDomain() + { + return tenantDomain; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModel.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModel.java index 38e401978..d789064e4 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModel.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModel.java @@ -1,83 +1,83 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -import org.alfresco.repo.dictionary.M2Model; - -/** - * Represents an alfresco model and checksum. - * - * @since 4.0 - */ -public class AlfrescoModel -{ - private M2Model model; - private Long checksum; - - public AlfrescoModel(M2Model model, Long checksum) - { - this.model = model; - this.checksum = checksum; - } - - public M2Model getModel() - { - return model; - } - - public Long getChecksum() - { - return checksum; - } - - @Override - public boolean equals(Object other) - { - if (this == other) - { - return true; - } - - if(!(other instanceof AlfrescoModel)) - { - return false; - } - - AlfrescoModel model = (AlfrescoModel)other; - return (this.model.getName().equals(model.getModel().getName()) && - checksum.equals(model.getChecksum())); - } - - @Override - public int hashCode() - { - int result = 17; - result = 31 * result + model.hashCode(); - result = 31 * result + Long.valueOf(checksum).hashCode(); - return result; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +import org.alfresco.repo.dictionary.M2Model; + +/** + * Represents an alfresco model and checksum. + * + * @since 4.0 + */ +public class AlfrescoModel +{ + private M2Model model; + private Long checksum; + + public AlfrescoModel(M2Model model, Long checksum) + { + this.model = model; + this.checksum = checksum; + } + + public M2Model getModel() + { + return model; + } + + public Long getChecksum() + { + return checksum; + } + + @Override + public boolean equals(Object other) + { + if (this == other) + { + return true; + } + + if(!(other instanceof AlfrescoModel)) + { + return false; + } + + AlfrescoModel model = (AlfrescoModel)other; + return (this.model.getName().equals(model.getModel().getName()) && + checksum.equals(model.getChecksum())); + } + + @Override + public int hashCode() + { + int result = 17; + result = 31 * result + model.hashCode(); + result = 31 * result + Long.valueOf(checksum).hashCode(); + return result; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModelDiff.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModelDiff.java index a13830907..6bfcd7e18 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModelDiff.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AlfrescoModelDiff.java @@ -1,106 +1,106 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -import org.alfresco.service.namespace.QName; - -/** - * Represents a diff between the set of current repository Alfresco models and the set maintained in SOLR. - * The diff can represent a new, changed or removed Alfresco model. For a new model the newChecksum is - * populated; for a changed model both checksums are populated; for a removed model neither checksum is populated. - * - * @since 4.0 - */ -public class AlfrescoModelDiff -{ - public enum TYPE - { - NEW, CHANGED, REMOVED; - } - - private QName modelName; - private TYPE type; - private Long oldChecksum; - private Long newChecksum; - - public AlfrescoModelDiff(QName modelName, TYPE type, Long oldChecksum, Long newChecksum) - { - super(); - this.modelName = modelName; - this.type = type; - this.oldChecksum = oldChecksum; - this.newChecksum = newChecksum; - } - - public QName getModelName() - { - return modelName; - } - - public TYPE getType() - { - return type; - } - - public Long getOldChecksum() - { - return oldChecksum; - } - - public Long getNewChecksum() - { - return newChecksum; - } - - @Override - public boolean equals(Object other) - { - if(this == other) - { - return true; - } - if(!(other instanceof AlfrescoModelDiff)) - { - return false; - } - AlfrescoModelDiff diff = (AlfrescoModelDiff)other; - return(diff.getModelName().equals(getModelName()) && - diff.getType().equals(getType()) && - diff.getOldChecksum().equals(getOldChecksum()) && - diff.getNewChecksum().equals(getNewChecksum())); - } - - @Override - public int hashCode() - { - int result = 17; - result = 31 * result + getModelName().hashCode(); - result = 31 * result + getType().hashCode(); - result = 31 * result + (int)(getOldChecksum().longValue() ^ (getOldChecksum().longValue() >>> 32)); - return result; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +import org.alfresco.service.namespace.QName; + +/** + * Represents a diff between the set of current repository Alfresco models and the set maintained in SOLR. + * The diff can represent a new, changed or removed Alfresco model. For a new model the newChecksum is + * populated; for a changed model both checksums are populated; for a removed model neither checksum is populated. + * + * @since 4.0 + */ +public class AlfrescoModelDiff +{ + public enum TYPE + { + NEW, CHANGED, REMOVED; + } + + private QName modelName; + private TYPE type; + private Long oldChecksum; + private Long newChecksum; + + public AlfrescoModelDiff(QName modelName, TYPE type, Long oldChecksum, Long newChecksum) + { + super(); + this.modelName = modelName; + this.type = type; + this.oldChecksum = oldChecksum; + this.newChecksum = newChecksum; + } + + public QName getModelName() + { + return modelName; + } + + public TYPE getType() + { + return type; + } + + public Long getOldChecksum() + { + return oldChecksum; + } + + public Long getNewChecksum() + { + return newChecksum; + } + + @Override + public boolean equals(Object other) + { + if(this == other) + { + return true; + } + if(!(other instanceof AlfrescoModelDiff)) + { + return false; + } + AlfrescoModelDiff diff = (AlfrescoModelDiff)other; + return(diff.getModelName().equals(getModelName()) && + diff.getType().equals(getType()) && + diff.getOldChecksum().equals(getOldChecksum()) && + diff.getNewChecksum().equals(getNewChecksum())); + } + + @Override + public int hashCode() + { + int result = 17; + result = 31 * result + getModelName().hashCode(); + result = 31 * result + getType().hashCode(); + result = 31 * result + (int)(getOldChecksum().longValue() ^ (getOldChecksum().longValue() >>> 32)); + return result; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/ContentPropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/ContentPropertyValue.java index 610ab38ac..fa8e1d32f 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/ContentPropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/ContentPropertyValue.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,62 +24,62 @@ * #L% */ -package org.alfresco.solr.client; - -import java.util.Locale; - -/** - * Represents a content property value, including locale, length, content id, encoding, mime type - * - * @since 4.0 - */ -public class ContentPropertyValue extends PropertyValue -{ - private Locale locale; - private long length; - private String encoding; - private String mimetype; - private Long id; - - public ContentPropertyValue(Locale locale, long length, String encoding, String mimetype, Long id) - { - super(); - this.locale = locale; - this.length = length; - this.encoding = encoding; - this.mimetype = mimetype; - this.id = id; - } - - public String getEncoding() - { - return encoding; - } - - public String getMimetype() - { - return mimetype; - } - - public Locale getLocale() - { - return locale; - } - - public long getLength() - { - return length; - } - - public Long getId() - { - return id; - } - - @Override - public String toString() - { - return "ContentPropertyValue [locale=" + locale + ", length=" + length + ", encoding=" - + encoding + ", mimetype=" + mimetype + ", id="+id+"]"; - } -} +package org.alfresco.solr.client; + +import java.util.Locale; + +/** + * Represents a content property value, including locale, length, content id, encoding, mime type + * + * @since 4.0 + */ +public class ContentPropertyValue extends PropertyValue +{ + private Locale locale; + private long length; + private String encoding; + private String mimetype; + private Long id; + + public ContentPropertyValue(Locale locale, long length, String encoding, String mimetype, Long id) + { + super(); + this.locale = locale; + this.length = length; + this.encoding = encoding; + this.mimetype = mimetype; + this.id = id; + } + + public String getEncoding() + { + return encoding; + } + + public String getMimetype() + { + return mimetype; + } + + public Locale getLocale() + { + return locale; + } + + public long getLength() + { + return length; + } + + public Long getId() + { + return id; + } + + @Override + public String toString() + { + return "ContentPropertyValue [locale=" + locale + ", length=" + length + ", encoding=" + + encoding + ", mimetype=" + mimetype + ", id="+id+"]"; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/GetNodesParameters.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/GetNodesParameters.java index ad109e976..2e168e4d6 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/GetNodesParameters.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/GetNodesParameters.java @@ -1,173 +1,173 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -import java.util.List; -import java.util.Set; - -import org.alfresco.service.namespace.QName; - -/** - * Query parameters for fetching nodes. - * - * @since 4.0 - */ -public class GetNodesParameters -{ - private List transactionIds; - private Long fromNodeId; - private Long toNodeId; - - private String storeProtocol; - private String storeIdentifier; - - private Set includeNodeTypes; - private Set excludeNodeTypes; - - private Set includeAspects; - private Set excludeAspects; - - private QName shardProperty; - private String coreName; - - public boolean getStoreFilter() - { - return (storeProtocol != null || storeIdentifier != null); - } - - public void setStoreProtocol(String storeProtocol) - { - this.storeProtocol = storeProtocol; - } - - public String getStoreProtocol() - { - return storeProtocol; - } - - public void setStoreIdentifier(String storeIdentifier) - { - this.storeIdentifier = storeIdentifier; - } - - public String getStoreIdentifier() - { - return storeIdentifier; - } - - public void setTransactionIds(List txnIds) - { - this.transactionIds = txnIds; - } - - public List getTransactionIds() - { - return transactionIds; - } - - public Long getFromNodeId() - { - return fromNodeId; - } - - public void setFromNodeId(Long fromNodeId) - { - this.fromNodeId = fromNodeId; - } - - public Long getToNodeId() - { - return toNodeId; - } - - public void setToNodeId(Long toNodeId) - { - this.toNodeId = toNodeId; - } - - public Set getIncludeNodeTypes() - { - return includeNodeTypes; - } - - public Set getExcludeNodeTypes() - { - return excludeNodeTypes; - } - - public Set getIncludeAspects() - { - return includeAspects; - } - - public Set getExcludeAspects() - { - return excludeAspects; - } - - public void setIncludeNodeTypes(Set includeNodeTypes) - { - this.includeNodeTypes = includeNodeTypes; - } - - public void setExcludeNodeTypes(Set excludeNodeTypes) - { - this.excludeNodeTypes = excludeNodeTypes; - } - - public void setIncludeAspects(Set includeAspects) - { - this.includeAspects = includeAspects; - } - - public void setExcludeAspects(Set excludeAspects) - { - this.excludeAspects = excludeAspects; - } - - public QName getShardProperty() - { - return this.shardProperty; - } - - public void setShardProperty(QName shardProperty) - { - this.shardProperty = shardProperty; - } - - public String getCoreName() - { - return this.coreName; - } - - public void setCoreName(String coreName) - { - this.coreName = coreName; - } - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +import java.util.List; +import java.util.Set; + +import org.alfresco.service.namespace.QName; + +/** + * Query parameters for fetching nodes. + * + * @since 4.0 + */ +public class GetNodesParameters +{ + private List transactionIds; + private Long fromNodeId; + private Long toNodeId; + + private String storeProtocol; + private String storeIdentifier; + + private Set includeNodeTypes; + private Set excludeNodeTypes; + + private Set includeAspects; + private Set excludeAspects; + + private QName shardProperty; + private String coreName; + + public boolean getStoreFilter() + { + return (storeProtocol != null || storeIdentifier != null); + } + + public void setStoreProtocol(String storeProtocol) + { + this.storeProtocol = storeProtocol; + } + + public String getStoreProtocol() + { + return storeProtocol; + } + + public void setStoreIdentifier(String storeIdentifier) + { + this.storeIdentifier = storeIdentifier; + } + + public String getStoreIdentifier() + { + return storeIdentifier; + } + + public void setTransactionIds(List txnIds) + { + this.transactionIds = txnIds; + } + + public List getTransactionIds() + { + return transactionIds; + } + + public Long getFromNodeId() + { + return fromNodeId; + } + + public void setFromNodeId(Long fromNodeId) + { + this.fromNodeId = fromNodeId; + } + + public Long getToNodeId() + { + return toNodeId; + } + + public void setToNodeId(Long toNodeId) + { + this.toNodeId = toNodeId; + } + + public Set getIncludeNodeTypes() + { + return includeNodeTypes; + } + + public Set getExcludeNodeTypes() + { + return excludeNodeTypes; + } + + public Set getIncludeAspects() + { + return includeAspects; + } + + public Set getExcludeAspects() + { + return excludeAspects; + } + + public void setIncludeNodeTypes(Set includeNodeTypes) + { + this.includeNodeTypes = includeNodeTypes; + } + + public void setExcludeNodeTypes(Set excludeNodeTypes) + { + this.excludeNodeTypes = excludeNodeTypes; + } + + public void setIncludeAspects(Set includeAspects) + { + this.includeAspects = includeAspects; + } + + public void setExcludeAspects(Set excludeAspects) + { + this.excludeAspects = excludeAspects; + } + + public QName getShardProperty() + { + return this.shardProperty; + } + + public void setShardProperty(QName shardProperty) + { + this.shardProperty = shardProperty; + } + + public String getCoreName() + { + return this.coreName; + } + + public void setCoreName(String coreName) + { + this.coreName = coreName; + } + + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/LookAheadBufferedReader.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/LookAheadBufferedReader.java index 80adc9f57..ab24a1119 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/LookAheadBufferedReader.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/LookAheadBufferedReader.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -250,4 +250,4 @@ public class LookAheadBufferedReader extends BufferedReader { return bufferingMode instanceof NoOp; } -} +} \ No newline at end of file diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MLTextPropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MLTextPropertyValue.java index 9b419e27f..1403e3818 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MLTextPropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MLTextPropertyValue.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,57 +24,57 @@ * #L% */ -package org.alfresco.solr.client; - -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -/** - * Represents a multi-lingual property value, comprising a map from locale to string value - * - * @since 4.0 - */ -public class MLTextPropertyValue extends PropertyValue -{ - private Map values; - - public MLTextPropertyValue() - { - super(); - values = new HashMap(10); - } - - public MLTextPropertyValue(Map values) - { - super(); - this.values = values; - } - - public void addValue(Locale locale, String value) - { - values.put(locale, value); - } - - public Map getValues() - { - return values; - } - - public Set getLocales() - { - return values.keySet(); - } - - public String getValue(Locale locale) - { - return values.get(locale); - } - - @Override - public String toString() - { - return "MLTextPropertyValue [values=" + values + "]"; - } -} +package org.alfresco.solr.client; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + * Represents a multi-lingual property value, comprising a map from locale to string value + * + * @since 4.0 + */ +public class MLTextPropertyValue extends PropertyValue +{ + private Map values; + + public MLTextPropertyValue() + { + super(); + values = new HashMap(10); + } + + public MLTextPropertyValue(Map values) + { + super(); + this.values = values; + } + + public void addValue(Locale locale, String value) + { + values.put(locale, value); + } + + public Map getValues() + { + return values; + } + + public Set getLocales() + { + return values.keySet(); + } + + public String getValue(Locale locale) + { + return values.get(locale); + } + + @Override + public String toString() + { + return "MLTextPropertyValue [values=" + values + "]"; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MultiPropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MultiPropertyValue.java index 023059221..98580073f 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MultiPropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/MultiPropertyValue.java @@ -1,66 +1,66 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents a multi property value, comprising a list of other property values - * - * @since 4.0 - */ -public class MultiPropertyValue extends PropertyValue -{ - private final List values; - - public MultiPropertyValue() - { - this.values = new ArrayList<>(); - } - - public MultiPropertyValue(List values) - { - this.values = values; - } - - public void addValue(PropertyValue value) - { - values.add(value); - } - - public List getValues() - { - return values; - } - - @Override - public String toString() - { - return "MultiPropertyValue [values=" + values + "]"; - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a multi property value, comprising a list of other property values + * + * @since 4.0 + */ +public class MultiPropertyValue extends PropertyValue +{ + private final List values; + + public MultiPropertyValue() + { + this.values = new ArrayList<>(); + } + + public MultiPropertyValue(List values) + { + this.values = values; + } + + public void addValue(PropertyValue value) + { + values.add(value); + } + + public List getValues() + { + return values; + } + + @Override + public String toString() + { + return "MultiPropertyValue [values=" + values + "]"; + } +} \ No newline at end of file diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Node.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Node.java index 0bd3ecf4a..15dd43b47 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Node.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Node.java @@ -1,144 +1,144 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -public class Node -{ - public static enum SolrApiNodeStatus - { - UPDATED, DELETED, UNKNOWN, NON_SHARD_DELETED, NON_SHARD_UPDATED; - }; - - private long id; - private String nodeRef; - private long txnId; - private SolrApiNodeStatus status; - private String tenant; - private long aclId; - private String shardPropertyValue; - private Integer explicitShardId; - - public long getId() - { - return id; - } - public void setId(long id) - { - this.id = id; - } - public String getNodeRef() - { - return nodeRef; - } - public void setNodeRef(String nodeRef) - { - this.nodeRef = nodeRef; - } - public long getTxnId() - { - return txnId; - } - public void setTxnId(long txnId) - { - this.txnId = txnId; - } - public SolrApiNodeStatus getStatus() - { - return status; - } - public void setStatus(SolrApiNodeStatus status) - { - this.status = status; - } - /** - * @return the tenant - */ - public String getTenant() - { - return tenant; - } - /** - * @param tenant the tenant to set - */ - public void setTenant(String tenant) - { - this.tenant = tenant; - } - /** - * @return the aclId - */ - public long getAclId() - { - return aclId; - } - /** - * @param aclId the aclId to set - */ - public void setAclId(long aclId) - { - this.aclId = aclId; - } - - - /** - * The property value to use for sharding - as requested - * - * @return null - if the node does not have the property, the standard "String" value of the property if it is present on the node. - * For dates and datetime properties this will be the ISO formatted datetime. - */ - public String getShardPropertyValue() - { - return this.shardPropertyValue; - } - - public void setShardPropertyValue(String shardPropertyValue) - { - this.shardPropertyValue = shardPropertyValue; - } - - public Integer getExplicitShardId() - { - return this.explicitShardId; - } - - public void setExplicitShardId(Integer explicitShardId) - { - this.explicitShardId = explicitShardId; - } - - @Override - public String toString() - { - return "Node [id=" + this.id + ", nodeRef=" + this.nodeRef + ", txnId=" + this.txnId - + ", status=" + this.status + ", tenant=" + this.tenant + ", aclId=" - + this.aclId + ", shardPropertyValue=" + this.shardPropertyValue - + ", explicitShardId=" + this.explicitShardId + "]"; - } - - - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +public class Node +{ + public static enum SolrApiNodeStatus + { + UPDATED, DELETED, UNKNOWN, NON_SHARD_DELETED, NON_SHARD_UPDATED; + }; + + private long id; + private String nodeRef; + private long txnId; + private SolrApiNodeStatus status; + private String tenant; + private long aclId; + private String shardPropertyValue; + private Integer explicitShardId; + + public long getId() + { + return id; + } + public void setId(long id) + { + this.id = id; + } + public String getNodeRef() + { + return nodeRef; + } + public void setNodeRef(String nodeRef) + { + this.nodeRef = nodeRef; + } + public long getTxnId() + { + return txnId; + } + public void setTxnId(long txnId) + { + this.txnId = txnId; + } + public SolrApiNodeStatus getStatus() + { + return status; + } + public void setStatus(SolrApiNodeStatus status) + { + this.status = status; + } + /** + * @return the tenant + */ + public String getTenant() + { + return tenant; + } + /** + * @param tenant the tenant to set + */ + public void setTenant(String tenant) + { + this.tenant = tenant; + } + /** + * @return the aclId + */ + public long getAclId() + { + return aclId; + } + /** + * @param aclId the aclId to set + */ + public void setAclId(long aclId) + { + this.aclId = aclId; + } + + + /** + * The property value to use for sharding - as requested + * + * @return null - if the node does not have the property, the standard "String" value of the property if it is present on the node. + * For dates and datetime properties this will be the ISO formatted datetime. + */ + public String getShardPropertyValue() + { + return this.shardPropertyValue; + } + + public void setShardPropertyValue(String shardPropertyValue) + { + this.shardPropertyValue = shardPropertyValue; + } + + public Integer getExplicitShardId() + { + return this.explicitShardId; + } + + public void setExplicitShardId(Integer explicitShardId) + { + this.explicitShardId = explicitShardId; + } + + @Override + public String toString() + { + return "Node [id=" + this.id + ", nodeRef=" + this.nodeRef + ", txnId=" + this.txnId + + ", status=" + this.status + ", tenant=" + this.tenant + ", aclId=" + + this.aclId + ", shardPropertyValue=" + this.shardPropertyValue + + ", explicitShardId=" + this.explicitShardId + "]"; + } + + + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaData.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaData.java index 0078d13ae..44578a649 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaData.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaData.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,101 +24,101 @@ * #L% */ -package org.alfresco.solr.client; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.Pair; - -/** - * SOLR-side representation of node metadata information. - * - * @since 4.0 - */ -public class NodeMetaData -{ - private long id; - private NodeRef nodeRef; - private QName type; - private long aclId; - private Map properties; - private Set aspects; - private List> paths; - private List> namePaths; - private long parentAssocsCrc; - private List parentAssocs; - private List childAssocs; - private List childIds; - private String owner; - private long txnId; - private Set ancestors; +package org.alfresco.solr.client; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +/** + * SOLR-side representation of node metadata information. + * + * @since 4.0 + */ +public class NodeMetaData +{ + private long id; + private NodeRef nodeRef; + private QName type; + private long aclId; + private Map properties; + private Set aspects; + private List> paths; + private List> namePaths; + private long parentAssocsCrc; + private List parentAssocs; + private List childAssocs; + private List childIds; + private String owner; + private long txnId; + private Set ancestors; private String tenantDomain; - private List ancestorPaths; - - public long getId() - { - return id; - } - public void setId(long id) - { - this.id = id; - } - public NodeRef getNodeRef() - { - return nodeRef; - } - public void setNodeRef(NodeRef nodeRef) - { - this.nodeRef = nodeRef; - } - public QName getType() - { - return type; - } - public void setType(QName type) - { - this.type = type; - } - public long getAclId() - { - return aclId; - } - public void setAclId(long aclId) - { - this.aclId = aclId; - } - - /** - * A property value is either null or a subclass of PropertyValue - */ - public Map getProperties() - { - return properties; - } - public void setProperties(Map properties) - { - this.properties = properties; - } - public Set getAspects() - { - return aspects; - } - public void setAspects(Set aspects) - { - this.aspects = aspects; - } - public List> getPaths() - { - return paths; - } - public void setPaths(List> paths) - { - this.paths = paths; + private List ancestorPaths; + + public long getId() + { + return id; + } + public void setId(long id) + { + this.id = id; + } + public NodeRef getNodeRef() + { + return nodeRef; + } + public void setNodeRef(NodeRef nodeRef) + { + this.nodeRef = nodeRef; + } + public QName getType() + { + return type; + } + public void setType(QName type) + { + this.type = type; + } + public long getAclId() + { + return aclId; + } + public void setAclId(long aclId) + { + this.aclId = aclId; + } + + /** + * A property value is either null or a subclass of PropertyValue + */ + public Map getProperties() + { + return properties; + } + public void setProperties(Map properties) + { + this.properties = properties; + } + public Set getAspects() + { + return aspects; + } + public void setAspects(Set aspects) + { + this.aspects = aspects; + } + public List> getPaths() + { + return paths; + } + public void setPaths(List> paths) + { + this.paths = paths; } public List getAncestorPaths() @@ -129,90 +129,90 @@ public class NodeMetaData public void setAncestorPaths(List ancestorPaths) { this.ancestorPaths = ancestorPaths; - } - - public List> getNamePaths() - { - return namePaths; - } - - public void setNamePaths(List> namePaths) - { - this.namePaths = namePaths; - } - public void setParentAssocsCrc(long parentAssocsCrc) - { - this.parentAssocsCrc = parentAssocsCrc; - } - public long getParentAssocsCrc() - { - return parentAssocsCrc; - } - public void setParentAssocs(List parentAssocs) - { - this.parentAssocs = parentAssocs; - } - public List getParentAssocs() - { - return parentAssocs; - } - public void setChildAssocs(List childAssocs) - { - this.childAssocs = childAssocs; - } - public List getChildAssocs() - { - return childAssocs; - } - public List getChildIds() - { - return childIds; - } - public void setChildIds(List childIds) - { - this.childIds = childIds; - } - - public String getOwner() - { - return owner; - } - public void setOwner(String owner) - { - this.owner = owner; - } - - public long getTxnId() - { - return txnId; - } - public void setTxnId(long txnId) - { - this.txnId = txnId; - } - public Set getAncestors() - { - return ancestors; - } - public void setAncestors(Set ancestors) - { - this.ancestors = ancestors; - } - public String getTenantDomain() - { - return tenantDomain; - } - public void setTenantDomain(String tenantDomain) - { - this.tenantDomain = tenantDomain; - } - - @Override - public String toString() - { - return "NodeMetaData [id=" - + id + ", nodeRef=" + nodeRef + ", type=" + type + ", aclId=" + aclId + ", properties=" + properties + ", aspects=" + aspects + ", paths=" + paths - + ", parentAssocsCrc=" + parentAssocsCrc + ", parentAssocs=" + parentAssocs + ", childAssocs=" + childAssocs + ", childIds=" + childIds + ", owner=" + owner - + ", txnId=" + txnId + ", ancestors=" + ancestors + ", tenantDomain=" + tenantDomain + "]"; - } -} + } + + public List> getNamePaths() + { + return namePaths; + } + + public void setNamePaths(List> namePaths) + { + this.namePaths = namePaths; + } + public void setParentAssocsCrc(long parentAssocsCrc) + { + this.parentAssocsCrc = parentAssocsCrc; + } + public long getParentAssocsCrc() + { + return parentAssocsCrc; + } + public void setParentAssocs(List parentAssocs) + { + this.parentAssocs = parentAssocs; + } + public List getParentAssocs() + { + return parentAssocs; + } + public void setChildAssocs(List childAssocs) + { + this.childAssocs = childAssocs; + } + public List getChildAssocs() + { + return childAssocs; + } + public List getChildIds() + { + return childIds; + } + public void setChildIds(List childIds) + { + this.childIds = childIds; + } + + public String getOwner() + { + return owner; + } + public void setOwner(String owner) + { + this.owner = owner; + } + + public long getTxnId() + { + return txnId; + } + public void setTxnId(long txnId) + { + this.txnId = txnId; + } + public Set getAncestors() + { + return ancestors; + } + public void setAncestors(Set ancestors) + { + this.ancestors = ancestors; + } + public String getTenantDomain() + { + return tenantDomain; + } + public void setTenantDomain(String tenantDomain) + { + this.tenantDomain = tenantDomain; + } + + @Override + public String toString() + { + return "NodeMetaData [id=" + + id + ", nodeRef=" + nodeRef + ", type=" + type + ", aclId=" + aclId + ", properties=" + properties + ", aspects=" + aspects + ", paths=" + paths + + ", parentAssocsCrc=" + parentAssocsCrc + ", parentAssocs=" + parentAssocs + ", childAssocs=" + childAssocs + ", childIds=" + childIds + ", owner=" + owner + + ", txnId=" + txnId + ", ancestors=" + ancestors + ", tenantDomain=" + tenantDomain + "]"; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaDataParameters.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaDataParameters.java index 1a1020ed6..b9938ea13 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaDataParameters.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/NodeMetaDataParameters.java @@ -1,242 +1,242 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -import java.util.List; -import java.util.OptionalInt; - -/** - * Stores node meta data query parameters for use in SOLR remote api calls - * - * @since 4.0 - */ -public class NodeMetaDataParameters -{ - private List transactionIds; - private Long fromTxnId; - private Long toTxnId; - - // default is 'all' results - private OptionalInt maxResults = OptionalInt.empty(); - - private Long fromNodeId; - private Long toNodeId; - private List nodeIds; - - private boolean includeAclId = true; - private boolean includeAspects = true; - private boolean includeNodeRef = true; - private boolean includeOwner = true; - private boolean includeProperties = true; - private boolean includePaths = true; - private boolean includeType = true; - private boolean includeChildAssociations = true; - private boolean includeParentAssociations = true; - private boolean includeChildIds = true; - private boolean includeTxnId = true; - - public boolean isIncludeAclId() - { - return includeAclId; - } - - public void setIncludeAclId(boolean includeAclId) - { - this.includeAclId = includeAclId; - } - - public boolean isIncludeAspects() - { - return includeAspects; - } - - public void setIncludeAspects(boolean includeAspects) - { - this.includeAspects = includeAspects; - } - - public boolean isIncludeNodeRef() - { - return includeNodeRef; - } - - public void setIncludeNodeRef(boolean includeNodeRef) - { - this.includeNodeRef = includeNodeRef; - } - - public boolean isIncludeOwner() - { - return includeOwner; - } - - public void setIncludeOwner(boolean includeOwner) - { - this.includeOwner = includeOwner; - } - - public boolean isIncludeProperties() - { - return includeProperties; - } - - public void setIncludeProperties(boolean includeProperties) - { - this.includeProperties = includeProperties; - } - - public boolean isIncludePaths() - { - return includePaths; - } - - public void setIncludePaths(boolean includePaths) - { - this.includePaths = includePaths; - } - - public boolean isIncludeType() - { - return includeType; - } - - public void setIncludeType(boolean includeType) - { - this.includeType = includeType; - } - - public boolean isIncludeChildAssociations() - { - return includeChildAssociations; - } - - public void setIncludeChildAssociations(boolean includeChildAssociations) - { - this.includeChildAssociations = includeChildAssociations; - } - - public boolean isIncludeParentAssociations() - { - return includeParentAssociations; - } - - public void setIncludeParentAssociations(boolean includeParentAssociations) - { - this.includeParentAssociations = includeParentAssociations; - } - - public OptionalInt getMaxResults() - { - return maxResults; - } - - public void setMaxResults(int maxResults) - { - this.maxResults = OptionalInt.of(maxResults); - } - - public List getNodeIds() - { - return nodeIds; - } - - public void setNodeIds(List nodeIds) - { - this.nodeIds = nodeIds; - } - - public void setTransactionIds(List txnIds) - { - this.transactionIds = txnIds; - } - - public List getTransactionIds() - { - return transactionIds; - } - - public Long getFromTxnId() - { - return fromTxnId; - } - - public void setFromTxnId(Long fromTxnId) - { - this.fromTxnId = fromTxnId; - } - - public Long getToTxnId() - { - return toTxnId; - } - - public void setToTxnId(Long toTxnId) - { - this.toTxnId = toTxnId; - } - - public Long getFromNodeId() - { - return fromNodeId; - } - - public void setFromNodeId(Long fromNodeId) - { - this.fromNodeId = fromNodeId; - } - - public Long getToNodeId() - { - return toNodeId; - } - - public void setToNodeId(Long toNodeId) - { - this.toNodeId = toNodeId; - } - - public boolean isIncludeChildIds() - { - return includeChildIds; - } - - public void setIncludeChildIds(boolean includeChildIds) - { - this.includeChildIds = includeChildIds; - } - - public boolean isIncludeTxnId() - { - return includeTxnId; - } - - public void setIncludeTxnId(boolean includeTxnId) - { - this.includeTxnId = includeTxnId; - } - -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +import java.util.List; +import java.util.OptionalInt; + +/** + * Stores node meta data query parameters for use in SOLR remote api calls + * + * @since 4.0 + */ +public class NodeMetaDataParameters +{ + private List transactionIds; + private Long fromTxnId; + private Long toTxnId; + + // default is 'all' results + private OptionalInt maxResults = OptionalInt.empty(); + + private Long fromNodeId; + private Long toNodeId; + private List nodeIds; + + private boolean includeAclId = true; + private boolean includeAspects = true; + private boolean includeNodeRef = true; + private boolean includeOwner = true; + private boolean includeProperties = true; + private boolean includePaths = true; + private boolean includeType = true; + private boolean includeChildAssociations = true; + private boolean includeParentAssociations = true; + private boolean includeChildIds = true; + private boolean includeTxnId = true; + + public boolean isIncludeAclId() + { + return includeAclId; + } + + public void setIncludeAclId(boolean includeAclId) + { + this.includeAclId = includeAclId; + } + + public boolean isIncludeAspects() + { + return includeAspects; + } + + public void setIncludeAspects(boolean includeAspects) + { + this.includeAspects = includeAspects; + } + + public boolean isIncludeNodeRef() + { + return includeNodeRef; + } + + public void setIncludeNodeRef(boolean includeNodeRef) + { + this.includeNodeRef = includeNodeRef; + } + + public boolean isIncludeOwner() + { + return includeOwner; + } + + public void setIncludeOwner(boolean includeOwner) + { + this.includeOwner = includeOwner; + } + + public boolean isIncludeProperties() + { + return includeProperties; + } + + public void setIncludeProperties(boolean includeProperties) + { + this.includeProperties = includeProperties; + } + + public boolean isIncludePaths() + { + return includePaths; + } + + public void setIncludePaths(boolean includePaths) + { + this.includePaths = includePaths; + } + + public boolean isIncludeType() + { + return includeType; + } + + public void setIncludeType(boolean includeType) + { + this.includeType = includeType; + } + + public boolean isIncludeChildAssociations() + { + return includeChildAssociations; + } + + public void setIncludeChildAssociations(boolean includeChildAssociations) + { + this.includeChildAssociations = includeChildAssociations; + } + + public boolean isIncludeParentAssociations() + { + return includeParentAssociations; + } + + public void setIncludeParentAssociations(boolean includeParentAssociations) + { + this.includeParentAssociations = includeParentAssociations; + } + + public OptionalInt getMaxResults() + { + return maxResults; + } + + public void setMaxResults(int maxResults) + { + this.maxResults = OptionalInt.of(maxResults); + } + + public List getNodeIds() + { + return nodeIds; + } + + public void setNodeIds(List nodeIds) + { + this.nodeIds = nodeIds; + } + + public void setTransactionIds(List txnIds) + { + this.transactionIds = txnIds; + } + + public List getTransactionIds() + { + return transactionIds; + } + + public Long getFromTxnId() + { + return fromTxnId; + } + + public void setFromTxnId(Long fromTxnId) + { + this.fromTxnId = fromTxnId; + } + + public Long getToTxnId() + { + return toTxnId; + } + + public void setToTxnId(Long toTxnId) + { + this.toTxnId = toTxnId; + } + + public Long getFromNodeId() + { + return fromNodeId; + } + + public void setFromNodeId(Long fromNodeId) + { + this.fromNodeId = fromNodeId; + } + + public Long getToNodeId() + { + return toNodeId; + } + + public void setToNodeId(Long toNodeId) + { + this.toNodeId = toNodeId; + } + + public boolean isIncludeChildIds() + { + return includeChildIds; + } + + public void setIncludeChildIds(boolean includeChildIds) + { + this.includeChildIds = includeChildIds; + } + + public boolean isIncludeTxnId() + { + return includeTxnId; + } + + public void setIncludeTxnId(boolean includeTxnId) + { + this.includeTxnId = includeTxnId; + } + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/PropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/PropertyValue.java index 5edb306c8..b0d788a5a 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/PropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/PropertyValue.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,14 +24,14 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * Base class for property values returned to SOLR for indexing. - * - * @since 4.0 - */ -public abstract class PropertyValue -{ - -} +package org.alfresco.solr.client; + +/** + * Base class for property values returned to SOLR for indexing. + * + * @since 4.0 + */ +public abstract class PropertyValue +{ + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClient.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClient.java index ac4f83801..233905369 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClient.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClient.java @@ -1,1637 +1,1637 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.httpclient.AlfrescoHttpClient; -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.httpclient.GetRequest; -import org.alfresco.httpclient.PostRequest; -import org.alfresco.httpclient.Request; -import org.alfresco.httpclient.Response; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.repo.index.shard.ShardState; -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.AssociationRef; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.MLText; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.Path; -import org.alfresco.service.cmr.repository.Path.AttributeElement; -import org.alfresco.service.cmr.repository.Path.ChildAssocElement; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; -import org.alfresco.service.cmr.repository.datatype.TypeConversionException; -import org.alfresco.service.cmr.repository.datatype.TypeConverter; -import org.alfresco.service.cmr.repository.datatype.TypeConverter.Converter; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.ISO8601DateFormat; -import org.alfresco.util.Pair; -import org.apache.commons.codec.EncoderException; -import org.apache.commons.codec.net.URLCodec; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.util.DateUtil; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.extensions.surf.util.URLEncoder; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import static java.util.Optional.ofNullable; - -// TODO error handling, including dealing with a repository that is not responsive (ConnectException in sendRemoteRequest) -// TODO get text content transform status handling -/** - * Http client to handle SOLR-Alfresco remote calls. - * - * @since 4.0 - */ -public class SOLRAPIClient -{ - protected final static Logger LOGGER = LoggerFactory.getLogger(SOLRAPIClient.class); - private static final String GET_ACL_CHANGESETS_URL = "api/solr/aclchangesets"; - private static final String GET_ACLS = "api/solr/acls"; - private static final String GET_ACLS_READERS = "api/solr/aclsReaders"; - private static final String GET_TRANSACTIONS_URL = "api/solr/transactions"; - private static final String GET_METADATA_URL = "api/solr/metadata"; - private static final String GET_NODES_URL = "api/solr/nodes"; - private static final String GET_CONTENT = "api/solr/textContent"; - private static final String GET_MODEL = "api/solr/model"; - private static final String GET_MODELS_DIFF = "api/solr/modelsdiff"; - private static final String GET_NEXT_TX_COMMIT_TIME = "api/solr/nextTransaction"; - private static final String GET_TX_INTERVAL_COMMIT_TIME = "api/solr/transactionInterval"; - - private static final String CHECKSUM_HEADER = "XAlfresco-modelChecksum"; - - private AlfrescoHttpClient repositoryHttpClient; - private SOLRDeserializer deserializer; - private DictionaryService dictionaryService; - private JsonFactory jsonFactory; - private NamespaceDAO namespaceDAO; - - /** - * This option enables ("Accept-Encoding": "gzip") header for compression - * in GET_CONTENT requests. Additional configuration is required in - * Alfresco Repository Tomcat Connector or HTTP Web Proxy to deal - * with compressed requests. - */ - private boolean compression; - - public SOLRAPIClient(AlfrescoHttpClient repositoryHttpClient, - DictionaryService dictionaryService, - NamespaceDAO namespaceDAO) - { - this(repositoryHttpClient, dictionaryService, namespaceDAO, false); - } - - public SOLRAPIClient(AlfrescoHttpClient repositoryHttpClient, - DictionaryService dictionaryService, - NamespaceDAO namespaceDAO, - boolean compression) - { - this.repositoryHttpClient = repositoryHttpClient; - this.dictionaryService = dictionaryService; - this.namespaceDAO = namespaceDAO; - this.deserializer = new SOLRDeserializer(namespaceDAO); - this.jsonFactory = new JsonFactory(); - this.compression = compression; - } - - /** - * Get the ACL ChangeSets - * - * @param fromCommitTime the lowest commit time (optional) - * @param minAclChangeSetId the lowest ChangeSet ID (optional) - * @param maxResults the maximum number of results (a reasonable value only) - * @return the ACL ChangeSets in order of commit time and ID - */ - public AclChangeSets getAclChangeSets(Long fromCommitTime, Long minAclChangeSetId, Long toCommitTime, Long maxAclChangeSetId, int maxResults) - throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_ACL_CHANGESETS_URL); - StringBuilder args = new StringBuilder(); - if (fromCommitTime != null) - { - args.append("?").append("fromTime").append("=").append(fromCommitTime); - } - if (minAclChangeSetId != null) - { - args.append(args.length() == 0 ? "?" : "&").append("fromId").append("=").append(minAclChangeSetId); - } - if (toCommitTime != null) - { - args.append(args.length() == 0 ? "?" : "&").append("toTime").append("=").append(toCommitTime); - } - if (maxAclChangeSetId != null) - { - args.append(args.length() == 0 ? "?" : "&").append("toId").append("=").append(maxAclChangeSetId); - } - if (maxResults != 0 && maxResults != Integer.MAX_VALUE) - { - args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); - } - url.append(args); - - GetRequest req = new GetRequest(url.toString()); - JSONObject json = callRepository(GET_ACL_CHANGESETS_URL, req); - - JSONArray aclChangeSetsJSON = json.getJSONArray("aclChangeSets"); - List aclChangeSets = new ArrayList(aclChangeSetsJSON.length()); - for (int i = 0; i < aclChangeSetsJSON.length(); i++) - { - JSONObject aclChangeSetJSON = aclChangeSetsJSON.getJSONObject(i); - long aclChangeSetId = aclChangeSetJSON.getLong("id"); - long commitTimeMs = aclChangeSetJSON.getLong("commitTimeMs"); - int aclCount = aclChangeSetJSON.getInt("aclCount"); - AclChangeSet aclChangeSet = new AclChangeSet(aclChangeSetId, commitTimeMs, aclCount); - aclChangeSets.add(aclChangeSet); - } - - Long maxChangeSetCommitTime = null; - if(json.has("maxChangeSetCommitTime")) - { - maxChangeSetCommitTime = json.getLong("maxChangeSetCommitTime"); - } - - Long maxChangeSetIdOnServer = null; - if(json.has("maxChangeSetId")) - { - maxChangeSetIdOnServer = json.getLong("maxChangeSetId"); - } - - // Done - return new AclChangeSets(aclChangeSets, maxChangeSetCommitTime, maxChangeSetIdOnServer); - } - - /** - * Get the ACLs associated with a given list of ACL ChangeSets. The ACLs may be truncated for - * the last ACL ChangeSet in the return values - the ACL count from the - * {@link #getAclChangeSets(Long, Long, Long, Long, int) ACL ChangeSets}. - * - * @param aclChangeSets the ACL ChangeSets to include - * @param minAclId the lowest ACL ID (may be null) - * @param maxResults the maximum number of results to retrieve - * @return the ACLs (includes ChangeSet ID) - */ - public List getAcls(List aclChangeSets, Long minAclId, int maxResults) throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_ACLS); - StringBuilder args = new StringBuilder(); - if (minAclId != null) - { - args.append("?").append("fromId").append("=").append(minAclId); - } - - if(maxResults >= 0) - { - args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); - } - - url.append(args); - - JSONObject jsonReq = new JSONObject(); - JSONArray aclChangeSetIdsJSON = new JSONArray(); - List aclChangeSetIds = new ArrayList(); - for (AclChangeSet aclChangeSet : aclChangeSets) - { - Long aclChangeSetId = aclChangeSet.getId(); - aclChangeSetIdsJSON.put(aclChangeSetId); - aclChangeSetIds.add(aclChangeSetId); - } - jsonReq.put("aclChangeSetIds", aclChangeSetIdsJSON); - - PostRequest req = new PostRequest(url.toString(), jsonReq.toString(), "application/json"); - JSONObject json = callRepository(GET_ACL_CHANGESETS_URL, req); - - JSONArray aclsJSON = json.getJSONArray("acls"); - List acls = new ArrayList(aclsJSON.length()); - for (int i = 0; i < aclsJSON.length(); i++) - { - JSONObject aclJSON = aclsJSON.getJSONObject(i); - long aclChangeSetId = aclJSON.getLong("aclChangeSetId"); - long aclId = aclJSON.getLong("id"); - Acl acl = new Acl(aclChangeSetId, aclId); - acls.add(acl); - } - // Done - return acls; - } - - /** - * Get the ACL readers for a given list of ACLs - * - * @param acls the ACLs - * @return the readers for the ACLs - */ - public List getAclReaders(List acls) throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_ACLS_READERS); - - JSONObject jsonReq = new JSONObject(); - JSONArray aclIdsJSON = new JSONArray(); - List aclIds = new ArrayList(); - for (Acl acl : acls) - { - Long aclId = acl.getId(); - aclIdsJSON.put(aclId); - aclIds.add(aclId); - } - jsonReq.put("aclIds", aclIdsJSON); - - PostRequest req = new PostRequest(url.toString(), jsonReq.toString(), "application/json"); - JSONObject json = callRepository(GET_ACLS_READERS, req); - - JSONArray aclsReadersJSON = json.getJSONArray("aclsReaders"); - List aclsReaders = new ArrayList(aclsReadersJSON.length()); - for (int i = 0; i < aclsReadersJSON.length(); i++) - { - JSONObject aclReadersJSON = aclsReadersJSON.getJSONObject(i); - long aclId = aclReadersJSON.getLong("aclId"); - JSONArray readersJSON = aclReadersJSON.getJSONArray("readers"); - List readers = authorityListFromJSON(readersJSON); - JSONArray deniedJSON = aclReadersJSON.getJSONArray("denied"); - List denied = authorityListFromJSON(deniedJSON); - long aclChangeSetId = aclReadersJSON.getLong("aclChangeSetId"); - - String tenantDomain = aclReadersJSON.getString("tenantDomain"); - if (tenantDomain == null) - { - tenantDomain = TenantService.DEFAULT_DOMAIN; - } - - AclReaders aclReaders = new AclReaders(aclId, readers, denied, aclChangeSetId, tenantDomain); - aclsReaders.add(aclReaders); - } - // Done - return aclsReaders; - } - - /** - * Convert a JSON array of authorities to a simple Java List<String> - * - * @param jsonArray JSONArray - * @return List<String> - * @throws JSONException - */ - private List authorityListFromJSON(JSONArray jsonArray) throws JSONException - { - List authorities = new ArrayList(jsonArray.length()); - for (int j = 0; j < jsonArray.length(); j++) - { - String authority = jsonArray.getString(j); - authorities.add(authority); - } - return authorities; - } - - public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, Long maxTxnId, int maxResults) throws AuthenticationException, IOException, JSONException - { - try - { - return getTransactions(fromCommitTime, minTxnId, toCommitTime, maxTxnId, maxResults, null); - } - catch (EncoderException e) - { - // Can not happen .... - throw new IOException(e); - } - } - - public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, - Long maxTxnId, int maxResults, ShardState shardState) - throws AuthenticationException, IOException, JSONException, EncoderException - { - URLCodec encoder = new URLCodec(); - - StringBuilder url = new StringBuilder(GET_TRANSACTIONS_URL); - StringBuilder args = new StringBuilder(); - if (fromCommitTime != null) - { - args.append("?").append("fromCommitTime").append("=").append(fromCommitTime); - } - if (minTxnId != null) - { - args.append(args.length() == 0 ? "?" : "&").append("minTxnId").append("=").append(minTxnId); - } - if (toCommitTime != null) - { - args.append(args.length() == 0 ? "?" : "&").append("toCommitTime").append("=").append(toCommitTime); - } - if (maxTxnId != null) - { - args.append(args.length() == 0 ? "?" : "&").append("maxTxnId").append("=").append(maxTxnId); - } - if (maxResults != 0 && maxResults != Integer.MAX_VALUE) - { - args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); - } - if(shardState != null) - { - LOGGER.debug("### Shard state exists ###"); - args.append(args.length() == 0 ? "?" : "&"); - args.append(encoder.encode("baseUrl")).append("=").append(encoder.encode(shardState.getShardInstance().getBaseUrl())); - args.append("&").append(encoder.encode("hostName")).append("=").append(encoder.encode(shardState.getShardInstance().getHostName())); - args.append("&").append(encoder.encode("template")).append("=").append(encoder.encode(shardState.getShardInstance().getShard().getFloc().getTemplate())); - - for(String key : shardState.getShardInstance().getShard().getFloc().getPropertyBag().keySet()) - { - String value = shardState.getShardInstance().getShard().getFloc().getPropertyBag().get(key); - if(value != null) - { - args.append("&").append(encoder.encode("floc.property."+key)).append("=").append(encoder.encode(value)); - } - } - - for(String key : shardState.getPropertyBag().keySet()) - { - String value = shardState.getPropertyBag().get(key); - if(value != null) - { - args.append("&").append(encoder.encode("state.property."+key)).append("=").append(encoder.encode(value)); - } - } - - args.append("&").append(encoder.encode("instance")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getInstance())); - args.append("&").append(encoder.encode("numberOfShards")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getFloc().getNumberOfShards())); - args.append("&").append(encoder.encode("port")).append("=").append(encoder.encode("" + shardState.getShardInstance().getPort())); - args.append("&").append(encoder.encode("stores")).append("="); - for(StoreRef store : shardState.getShardInstance().getShard().getFloc().getStoreRefs()) - { - if(args.charAt(args.length()-1) != '=') - { - args.append(encoder.encode(",")); - } - args.append(encoder.encode(store.toString())); - } - args.append("&").append(encoder.encode("isMaster")).append("=").append(encoder.encode("" + shardState.isMaster())); - args.append("&").append(encoder.encode("hasContent")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getFloc().hasContent())); - args.append("&").append(encoder.encode("shardMethod")).append("=").append(encoder.encode(shardState.getShardInstance().getShard().getFloc().getShardMethod().toString())); - - args.append("&").append(encoder.encode("lastUpdated")).append("=").append(encoder.encode("" + shardState.getLastUpdated())); - args.append("&").append(encoder.encode("lastIndexedChangeSetCommitTime")).append("=").append(encoder.encode("" + shardState.getLastIndexedChangeSetCommitTime())); - args.append("&").append(encoder.encode("lastIndexedChangeSetId")).append("=").append(encoder.encode("" + shardState.getLastIndexedChangeSetId())); - args.append("&").append(encoder.encode("lastIndexedTxCommitTime")).append("=").append(encoder.encode("" + shardState.getLastIndexedTxCommitTime())); - args.append("&").append(encoder.encode("lastIndexedTxId")).append("=").append(encoder.encode("" + shardState.getLastIndexedTxId())); - - } - - url.append(args); - LOGGER.debug("### GetRequest: " + url.toString()); - GetRequest req = new GetRequest(url.toString()); - Response response = null; - List transactions = new ArrayList(); - Long maxTxnCommitTime = null; - Long maxTxnIdOnServer = null; - - LookAheadBufferedReader reader = null; - try - { - response = repositoryHttpClient.sendRequest(req); - if(response.getStatus() != HttpStatus.SC_OK) - { - throw new AlfrescoRuntimeException("GetTransactions return status is " + response.getStatus()); - } - - reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); - JsonParser parser = jsonFactory.createParser(reader); - - JsonToken token = parser.nextValue(); - while (token != null) - { - if ("transactions".equals(parser.getCurrentName())) - { - token = parser.nextToken(); //START_ARRAY - while (token == JsonToken.START_OBJECT) - { - token = parser.nextValue(); - long id = parser.getLongValue(); - - token = parser.nextValue(); - long commitTime = parser.getLongValue(); - - token = parser.nextValue(); - long updates = parser.getLongValue(); - - token = parser.nextValue(); - long deletes = parser.getLongValue(); - - Transaction txn = new Transaction(); - txn.setCommitTimeMs(commitTime); - txn.setDeletes(deletes); - txn.setId(id); - txn.setUpdates(updates); - - transactions.add(txn); - - token = parser.nextToken(); //END_OBJECT - token = parser.nextToken(); // START_OBJECT or END_ARRAY; - } - } - else if ("maxTxnCommitTime".equals(parser.getCurrentName())) - { - maxTxnCommitTime = parser.getLongValue(); - } - else if ("maxTxnId".equals(parser.getCurrentName())) - { - maxTxnIdOnServer = parser.getLongValue(); - } - token = parser.nextValue(); - } - parser.close(); - } - catch (JSONException exception) - { - String message = "Received a malformed JSON payload. Request was \"" + - req.getFullUri() + - "Data: " - + ofNullable(reader) - .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) - .orElse("Not available"); - LOGGER.error(message); - throw exception; - } - finally - { - ofNullable(response).ifPresent(Response::release); - ofNullable(reader).ifPresent(this::silentlyClose); - } - - LOGGER.debug("### Transactions found maxTxnCommitTime: " + maxTxnCommitTime ); - return new Transactions(transactions, maxTxnCommitTime, maxTxnIdOnServer); - } - - public List getNodes(GetNodesParameters parameters, int maxResults) throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_NODES_URL); - - JSONObject body = new JSONObject(); - - if(parameters.getTransactionIds() != null) - { - JSONArray jsonTxnIds = new JSONArray(); - for(Long txnId : parameters.getTransactionIds()) - { - jsonTxnIds.put(txnId); - } - body.put("txnIds", jsonTxnIds); - } - - if(parameters.getFromNodeId() != null) - { - body.put("fromNodeId", parameters.getFromNodeId()); - } - if(parameters.getToNodeId() != null) - { - body.put("toNodeId", parameters.getToNodeId()); - } - if(parameters.getExcludeAspects() != null) - { - JSONArray jsonExcludeAspects = new JSONArray(); - for(QName excludeAspect : parameters.getExcludeAspects()) - { - jsonExcludeAspects.put(excludeAspect.toString()); - } - body.put("excludeAspects", jsonExcludeAspects); - } - if(parameters.getIncludeAspects() != null) - { - JSONArray jsonIncludeAspects = new JSONArray(); - for(QName includeAspect : parameters.getIncludeAspects()) - { - jsonIncludeAspects.put(includeAspect.toString()); - } - body.put("includeAspects", jsonIncludeAspects); - } - - if(parameters.getStoreProtocol() != null) - { - body.put("storeProtocol", parameters.getStoreProtocol()); - } - - if(parameters.getStoreIdentifier() != null) - { - body.put("storeIdentifier", parameters.getStoreIdentifier()); - } - - body.put("maxResults", maxResults); - - if(parameters.getShardProperty() != null) - { - body.put("shardProperty", parameters.getShardProperty().toString()); - } - - if (parameters.getCoreName() != null){ - body.put("coreName", parameters.getCoreName()); - } - - - PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); - JSONObject json = callRepository(GET_NODES_URL, req); - - JSONArray jsonNodes = json.getJSONArray("nodes"); - List nodes = new ArrayList<>(jsonNodes.length()); - for(int i = 0; i < jsonNodes.length(); i++) - { - JSONObject jsonNodeInfo = jsonNodes.getJSONObject(i); - Node nodeInfo = new Node(); - if(jsonNodeInfo.has("id")) - { - nodeInfo.setId(jsonNodeInfo.getLong("id")); - } - - if(jsonNodeInfo.has("nodeRef")) - { - nodeInfo.setNodeRef(jsonNodeInfo.getString("nodeRef")); - } - - if(jsonNodeInfo.has("txnId")) - { - nodeInfo.setTxnId(jsonNodeInfo.getLong("txnId")); - } - - if(jsonNodeInfo.has("aclId")) - { - nodeInfo.setAclId(jsonNodeInfo.getLong("aclId")); - } - - if(jsonNodeInfo.has("shardPropertyValue")) - { - nodeInfo.setShardPropertyValue(jsonNodeInfo.getString("shardPropertyValue")); - } - - if(jsonNodeInfo.has("explicitShardId")) - { - nodeInfo.setExplicitShardId(jsonNodeInfo.getInt("explicitShardId")); - } - - if(jsonNodeInfo.has("tenant")) - { - nodeInfo.setTenant(jsonNodeInfo.getString("tenant")); - } - - if(jsonNodeInfo.has("status")) - { - Node.SolrApiNodeStatus status; - String statusStr = jsonNodeInfo.getString("status"); - if(statusStr.equals("u")) - { - status = Node.SolrApiNodeStatus.UPDATED; - } - else if(statusStr.equals("d")) - { - status = Node.SolrApiNodeStatus.DELETED; - } - else - { - status = Node.SolrApiNodeStatus.UNKNOWN; - } - nodeInfo.setStatus(status); - } - - nodes.add(nodeInfo); - } - - return nodes; - } - - private PropertyValue getSinglePropertyValue(DataTypeDefinition dataType, Object value) throws JSONException - { - PropertyValue ret = null; - QName dataTypeName = dataType.getName(); - - if(value == null || value == JSONObject.NULL) - { - ret = null; - } - else if(dataTypeName.equals(DataTypeDefinition.MLTEXT)) - { - JSONArray a = (JSONArray)value; - Map mlValues = new HashMap(a.length()); - - for(int k = 0; k < a.length(); k++) - { - JSONObject pair = a.getJSONObject(k); - Locale locale = deserializer.deserializeValue(Locale.class, pair.getString("locale")); - String mlValue = pair.has("value") && !pair.isNull("value") ? pair.getString("value") : null; - mlValues.put(locale, mlValue); - } - - ret = new MLTextPropertyValue(mlValues); - } - else if(dataTypeName.equals(DataTypeDefinition.CONTENT)) - { - JSONObject o = (JSONObject)value; - - String localeStr = o.has("locale") && !o.isNull("locale") ? o.getString("locale") : null; - Locale locale = (o.has("locale") && !o.isNull("locale") ? deserializer.deserializeValue(Locale.class, localeStr) : null); - - long size = o.has("size") && !o.isNull("size") ? o.getLong("size") : 0; - - String encoding = o.has("encoding") && !o.isNull("encoding") ? o.getString("encoding") : null; - String mimetype = o.has("mimetype") && !o.isNull("mimetype") ? o.getString("mimetype") : null; - - Long id = o.has("contentId") && !o.isNull("contentId") ? o.getLong("contentId") : null; - - ret = new ContentPropertyValue(locale, size, encoding, mimetype, id); - } - else - { - ret = new StringPropertyValue((String)value); - } - - return ret; - } - - private PropertyValue getPropertyValue(PropertyDefinition propertyDef, Object value) throws JSONException - { - PropertyValue ret = null; - - if(value == null || value == JSONObject.NULL) - { - ret = null; - } - else if(propertyDef == null) - { - // assume a string - ret = new StringPropertyValue((String)value); - } - else - { - DataTypeDefinition dataType = propertyDef.getDataType(); - - boolean isMulti = propertyDef.isMultiValued(); - if(isMulti) - { - if(!(value instanceof JSONArray)) - { - throw new IllegalArgumentException("Expected json array, got " + value.getClass().getName()); - } - - MultiPropertyValue multi = new MultiPropertyValue(); - JSONArray array = (JSONArray)value; - for(int j = 0; j < array.length(); j++) - { - multi.addValue(getSinglePropertyValue(dataType, array.get(j))); - } - - ret = multi; - } - else - { - ret = getSinglePropertyValue(dataType, value); - } - } - - return ret; - } - - public List getNodesMetaData(NodeMetaDataParameters params) throws AuthenticationException, IOException, JSONException - { - List nodeIds = params.getNodeIds(); - - StringBuilder url = new StringBuilder(GET_METADATA_URL); - - JSONObject body = new JSONObject(); - if(nodeIds != null && nodeIds.size() > 0) - { - JSONArray jsonNodeIds = new JSONArray(); - for(Long nodeId : nodeIds) - { - jsonNodeIds.put(nodeId); - } - body.put("nodeIds", jsonNodeIds); - - } - if(params.getFromNodeId() != null) - { - body.put("fromNodeId", params.getFromNodeId()); - } - if(params.getToNodeId() != null) - { - body.put("toNodeId", params.getToNodeId()); - } - - // only need to set in cases where we don't want them in the response - // because they default to true - if(!params.isIncludeAclId()) - { - body.put("includeAclId", params.isIncludeAclId()); - } - if(!params.isIncludeAspects()) - { - body.put("includeAspects", params.isIncludeAspects()); - } - if(!params.isIncludeProperties()) - { - body.put("includeProperties", params.isIncludeProperties()); - } - if(!params.isIncludeChildAssociations()) - { - body.put("includeChildAssociations", params.isIncludeChildAssociations()); - } - if(!params.isIncludeParentAssociations()) - { - body.put("includeParentAssociations", params.isIncludeParentAssociations()); - } - if(!params.isIncludeChildIds()) - { - body.put("includeChildIds", params.isIncludeChildIds()); - } - if(!params.isIncludePaths()) - { - body.put("includePaths", params.isIncludePaths()); - } - if(!params.isIncludeOwner()) - { - body.put("includeOwner", params.isIncludeOwner()); - } - if(!params.isIncludeNodeRef()) - { - body.put("includeNodeRef", params.isIncludeNodeRef()); - } - if(!params.isIncludeTxnId()) - { - body.put("includeTxnId", params.isIncludeTxnId()); - } - - if (params.getMaxResults().isPresent()) - { - body.put("maxResults", params.getMaxResults().getAsInt()); - } - - PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); - JSONObject json = callRepository(GET_METADATA_URL, req); - - JSONArray jsonNodes = json.getJSONArray("nodes"); - List nodes = new ArrayList<>(jsonNodes.length()); - for(int i = 0; i < jsonNodes.length(); i++) - { - JSONObject jsonNodeInfo = jsonNodes.getJSONObject(i); - NodeMetaData metaData = new NodeMetaData(); - - if(jsonNodeInfo.has("id")) - { - metaData.setId(jsonNodeInfo.getLong("id")); - } - - if(jsonNodeInfo.has("tenantDomain")) - { - metaData.setTenantDomain(jsonNodeInfo.getString("tenantDomain")); - } - - if(jsonNodeInfo.has("txnId")) - { - metaData.setTxnId(jsonNodeInfo.getLong("txnId")); - } - - if(jsonNodeInfo.has("aclId")) - { - metaData.setAclId(jsonNodeInfo.getLong("aclId")); - } - - if(jsonNodeInfo.has("nodeRef")) - { - metaData.setNodeRef(new NodeRef(jsonNodeInfo.getString("nodeRef"))); - } - - if(jsonNodeInfo.has("type")) - { - metaData.setType(deserializer.deserializeValue(QName.class, jsonNodeInfo.getString("type"))); - } - - if(jsonNodeInfo.has("aspects")) - { - JSONArray jsonAspects = jsonNodeInfo.getJSONArray("aspects"); - Set aspects = new HashSet(jsonAspects.length()); - for(int j = 0; j < jsonAspects.length(); j++) - { - String jsonAspect = (String)jsonAspects.get(j); - aspects.add(deserializer.deserializeValue(QName.class, jsonAspect)); - } - metaData.setAspects(aspects); - } - - if(jsonNodeInfo.has("paths")) - { - JSONArray jsonPaths = jsonNodeInfo.getJSONArray("paths"); - List> paths = new ArrayList>(jsonPaths.length()); - List ancestorPaths = new ArrayList(); - for(int j = 0; j < jsonPaths.length(); j++) - { - JSONObject path = jsonPaths.getJSONObject(j); - String pathValue = path.getString("path"); - QName qname = path.has("qname") ? deserializer.deserializeValue(QName.class, path.getString("qname")) : null; - paths.add(new Pair(pathValue, qname)); - if(path.has("apath")) - { - String ancestorPath = path.getString("apath"); - ancestorPaths.add(ancestorPath); - } - } - metaData.setPaths(paths); - metaData.setAncestorPaths(ancestorPaths); - } - - if(jsonNodeInfo.has("namePaths")) - { - JSONArray jsonNamePaths = jsonNodeInfo.getJSONArray("namePaths"); - List> namePaths = new ArrayList>(jsonNamePaths.length()); - for(int j = 0; j < jsonNamePaths.length(); j++) - { - JSONObject jsonNamePath = jsonNamePaths.getJSONObject(j); - JSONArray jsonNameElements = jsonNamePath.getJSONArray("namePath"); - List namePath = new ArrayList(jsonNameElements.length()); - for(int k = 0; k < jsonNameElements.length(); k++) - { - String namePathElement = jsonNameElements.getString(k); - namePath.add(namePathElement); - } - namePaths.add(namePath); - } - metaData.setNamePaths(namePaths); - } - - if(jsonNodeInfo.has("ancestors")) - { - JSONArray jsonAncestors = jsonNodeInfo.getJSONArray("ancestors"); - HashSet ancestors = new HashSet(jsonAncestors.length()); - for(int j = 0; j < jsonAncestors.length(); j++) - { - String ancestorNodeRefString = jsonAncestors.getString(j); - NodeRef ancestorNodeRef = new NodeRef(ancestorNodeRefString); - ancestors.add(ancestorNodeRef); - } - metaData.setAncestors(ancestors); - } - - if(jsonNodeInfo.has("properties")) - { - JSONObject jsonProperties = jsonNodeInfo.getJSONObject("properties"); - Map properties = new HashMap(jsonProperties.length()); - @SuppressWarnings("rawtypes") - Iterator propKeysIterator = jsonProperties.keys(); - while(propKeysIterator.hasNext()) - { - String propName = (String)propKeysIterator.next(); - QName propQName = deserializer.deserializeValue(QName.class, propName); - Object propValueObj = jsonProperties.opt(propName); - - // check the expected property type to determine how to process the value - PropertyDefinition propertyDef = dictionaryService.getProperty(propQName); -// if(propertyDef == null) -// { -// // TODO which exception here? -// throw new IllegalArgumentException("Could not find property definition for property " + propName); -// } - - properties.put(propQName, getPropertyValue(propertyDef, propValueObj)); - } - metaData.setProperties(properties); - } - - if(jsonNodeInfo.has("parentAssocsCrc")) - { - metaData.setParentAssocsCrc(jsonNodeInfo.getLong("parentAssocsCrc")); - } - - if(jsonNodeInfo.has("parentAssocs")) - { - JSONArray jsonParentAssocs = jsonNodeInfo.getJSONArray("parentAssocs"); - List assocs = new ArrayList(jsonParentAssocs.length()); - for(int j = 0; j < jsonParentAssocs.length(); j++) - { - String childAssocRefStr = jsonParentAssocs.getString(j); - ChildAssociationRef childAssociationRef = new ChildAssociationRef(childAssocRefStr); - assocs.add(childAssociationRef); - } - metaData.setParentAssocs(assocs); - } - - if(jsonNodeInfo.has("childAssocs")) - { - JSONArray jsonParentAssocs = jsonNodeInfo.getJSONArray("childAssocs"); - List assocs = new ArrayList(jsonParentAssocs.length()); - for(int j = 0; j < jsonParentAssocs.length(); j++) - { - String childAssocRefStr = jsonParentAssocs.getString(j); - ChildAssociationRef childAssociationRef = new ChildAssociationRef(childAssocRefStr); - assocs.add(childAssociationRef); - } - metaData.setChildAssocs(assocs); - } - - if(jsonNodeInfo.has("childIds")) - { - JSONArray jsonChildIds = jsonNodeInfo.getJSONArray("childIds"); - List childIds = new ArrayList(jsonChildIds.length()); - for(int j = 0; j < jsonChildIds.length(); j++) - { - Long childId = jsonChildIds.getLong(j); - childIds.add(childId); - } - metaData.setChildIds(childIds); - } - - if(jsonNodeInfo.has("owner")) - { - metaData.setOwner(jsonNodeInfo.getString("owner")); - } - - nodes.add(metaData); - } - - return nodes; - } - - public GetTextContentResponse getTextContent(Long nodeId, QName propertyQName, Long modifiedSince) throws AuthenticationException, IOException { - StringBuilder url = new StringBuilder(128); - url.append(GET_CONTENT); - - StringBuilder args = new StringBuilder(128); - if(nodeId != null) - { - args.append("?"); - args.append("nodeId"); - args.append("="); - args.append(nodeId); - } - else - { - throw new NullPointerException("getTextContent(): nodeId cannot be null."); - } - if(propertyQName != null) - { - if(args.length() == 0) - { - args.append("?"); - } - else - { - args.append("&"); - } - args.append("propertyQName"); - args.append("="); - args.append(URLEncoder.encode(propertyQName.toString())); - } - - url.append(args); - - GetRequest req = new GetRequest(url.toString()); - - Map headers = new HashMap<>(); - if(modifiedSince != null) - { - headers.put("If-Modified-Since", String.valueOf(DateUtil.formatDate(new Date(modifiedSince)))); - } - if (compression) - { - headers.put("Accept-Encoding", "gzip"); - } - req.setHeaders(headers); - - Response response = repositoryHttpClient.sendRequest(req); - - if(response.getStatus() != Status.STATUS_NOT_MODIFIED && response.getStatus() != Status.STATUS_NO_CONTENT && response.getStatus() != Status.STATUS_OK) - { - int status = response.getStatus(); - response.release(); - throw new AlfrescoRuntimeException("GetTextContentResponse return status is " + status); - } - - return new GetTextContentResponse(response); - } - - public AlfrescoModel getModel(String coreName, QName modelName) throws AuthenticationException, IOException - { - // If the model is new to the SOLR side the prefix will be unknown so we can not generate prefixes for the request! - // Always use the full QName with explicit URI - StringBuilder url = new StringBuilder(GET_MODEL); - - URLCodec encoder = new URLCodec(); - // must send the long name as we may not have the prefix registered - url.append("?modelQName=").append(encoder.encode(modelName.toString(), "UTF-8")); - - GetRequest req = new GetRequest(url.toString()); - - Response response = null; - try - { - response = repositoryHttpClient.sendRequest(req); - if(response.getStatus() != HttpStatus.SC_OK) - { - throw new AlfrescoRuntimeException(coreName + " GetModel return status is " + response.getStatus()); - } - - return new AlfrescoModel(M2Model.createModel(response.getContentAsStream()), - Long.valueOf(response.getHeader(CHECKSUM_HEADER))); - } - finally - { - if(response != null) - { - response.release(); - } - } - } - - public List getModelsDiff(String coreName, List currentModels) throws AuthenticationException, IOException, JSONException - { - StringBuilder url = new StringBuilder(GET_MODELS_DIFF); - - JSONObject body = new JSONObject(); - JSONArray jsonModels = new JSONArray(); - for(AlfrescoModel model : currentModels) - { - JSONObject jsonModel = new JSONObject(); - QName modelQName = QName.createQName( model.getModel().getName(), namespaceDAO); - jsonModel.put("name", modelQName.toString()); - jsonModel.put("checksum", model.getChecksum()); - jsonModels.put(jsonModel); - } - body.put("models", jsonModels); - - PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); - JSONObject json = callRepository(GET_MODELS_DIFF, req); - - JSONArray jsonDiffs = json.getJSONArray("diffs"); - if(jsonDiffs == null) - { - throw new AlfrescoRuntimeException("GetModelsDiff badly formatted response"); - } - - List diffs = new ArrayList(jsonDiffs.length()); - for(int i = 0; i < jsonDiffs.length(); i++) - { - JSONObject jsonDiff = jsonDiffs.getJSONObject(i); - diffs.add(new AlfrescoModelDiff( - QName.createQName(jsonDiff.getString("name")), - AlfrescoModelDiff.TYPE.valueOf(jsonDiff.getString("type")), - (jsonDiff.isNull("oldChecksum") ? null : jsonDiff.getLong("oldChecksum")), - (jsonDiff.isNull("newChecksum") ? null : jsonDiff.getLong("newChecksum")))); - } - - return diffs; - } - - /** - * Returns the next commit time from a given commit time. - * - * @param coreName alfresco, archive - * @param fromCommitTime initial transaction commit time - * @return Time of the next transaction - * @throws IOException - * @throws AuthenticationException - * @throws NoSuchMethodException - */ - public Long getNextTxCommitTime(String coreName, Long fromCommitTime) throws AuthenticationException, IOException, NoSuchMethodException - { - StringBuilder url = new StringBuilder(GET_NEXT_TX_COMMIT_TIME); - url.append("?").append("fromCommitTime").append("=").append(fromCommitTime); - GetRequest get = new GetRequest(url.toString()); - Response response = null; - JSONObject json = null; - LookAheadBufferedReader reader = null; - try - { - response = repositoryHttpClient.sendRequest(get); - if (response.getStatus() != HttpStatus.SC_OK) - { - throw new NoSuchMethodException(coreName + " - GetNextTxCommitTime return status is " - + response.getStatus() + " when invoking " + url); - } - - reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); - json = new JSONObject(new JSONTokener(reader)); - } - catch (JSONException exception) - { - String message = "Received a malformed JSON payload. Request was \"" + - get.getFullUri() + - "Data: " - + ofNullable(reader) - .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) - .orElse("Not available"); - LOGGER.error(message); - throw exception; - } - finally - { - ofNullable(response).ifPresent(Response::release); - ofNullable(reader).ifPresent(this::silentlyClose); - } - - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug(json.toString()); - } - - return Long.parseLong(json.get("nextTransactionCommitTimeMs").toString()); - } - - /** - * Returns the minimum and the maximum commit time for transactions in a node id range. - * - * @param coreName alfresco, archive - * @param fromNodeId Id of the initial node - * @param toNodeId Id of the final node - * @return Time of the first transaction, time of the last transaction - * @throws IOException - * @throws AuthenticationException - * @throws NoSuchMethodException - */ - public Pair getTxIntervalCommitTime(String coreName, Long fromNodeId, Long toNodeId) - throws AuthenticationException, IOException, NoSuchMethodException - { - StringBuilder url = new StringBuilder(GET_TX_INTERVAL_COMMIT_TIME); - url.append("?").append("fromNodeId").append("=").append(fromNodeId); - url.append("&").append("toNodeId").append("=").append(toNodeId); - GetRequest get = new GetRequest(url.toString()); - Response response = null; - JSONObject json = null; - LookAheadBufferedReader reader = null; - try - { - response = repositoryHttpClient.sendRequest(get); - if (response.getStatus() != HttpStatus.SC_OK) - { - throw new NoSuchMethodException(coreName + " - GetTxIntervalCommitTime return status is " - + response.getStatus() + " when invoking " + url); - } - - reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); - json = new JSONObject(new JSONTokener(reader)); - } - catch(JSONException exception) - { - String message = "Received a malformed JSON payload. Request was \"" + - get.getFullUri() + - "Data: " - + ofNullable(reader) - .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) - .orElse("Not available"); - LOGGER.error(message); - throw exception; - } - finally - { - ofNullable(response).ifPresent(Response::release); - ofNullable(reader).ifPresent(this::silentlyClose); - } - - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug(json.toString()); - } - - return new Pair(Long.parseLong(json.get("minTransactionCommitTimeMs").toString()), - Long.parseLong(json.get("maxTransactionCommitTimeMs").toString())); - } - - /* - * type conversions from serialized JSON values to SOLR-consumable objects - */ - @SuppressWarnings("rawtypes") - private class SOLRTypeConverter - { - /** - * Default Type Converter - */ - private TypeConverter instance = new TypeConverter(); - private NamespaceDAO namespaceDAO; - - @SuppressWarnings("unchecked") - SOLRTypeConverter(NamespaceDAO namespaceDAO) - { - this.namespaceDAO = namespaceDAO; - - // add all default converters to this converter - for (Entry, Map, Converter>> source : DefaultTypeConverter.INSTANCE.getConverters().entrySet()) - { - for (Entry, Converter> dest : source.getValue().entrySet()) - { - instance.addConverter((Class) source.getKey(), (Class) dest.getKey(), dest.getValue()); - } - } - - // dates - instance.addConverter(String.class, Date.class, new TypeConverter.Converter() - { - public Date convert(String source) - { - try - { - return ISO8601DateFormat.parse(source); - } - catch (Exception e) - { - throw new TypeConversionException("Failed to convert date " + source + " to string", e); - } - } - }); - - // node refs - instance.addConverter(String.class, NodeRef.class, new TypeConverter.Converter() - { - public NodeRef convert(String source) - { - return new NodeRef(source); - } - }); - - // paths - instance.addConverter(String.class, AttributeElement.class, new TypeConverter.Converter() - { - public AttributeElement convert(String source) - { - return new Path.AttributeElement(source); - } - }); - - instance.addConverter(String.class, ChildAssocElement.class, new TypeConverter.Converter() - { - public ChildAssocElement convert(String source) - { - return new Path.ChildAssocElement(instance.convert(ChildAssociationRef.class, source)); - } - }); - - instance.addConverter(String.class, Path.DescendentOrSelfElement.class, new TypeConverter.Converter() - { - public Path.DescendentOrSelfElement convert(String source) - { - return new Path.DescendentOrSelfElement(); - } - }); - - instance.addConverter(String.class, Path.ParentElement.class, new TypeConverter.Converter() - { - public Path.ParentElement convert(String source) - { - return new Path.ParentElement(); - } - }); - - instance.addConverter(String.class, Path.SelfElement.class, new TypeConverter.Converter() - { - public Path.SelfElement convert(String source) - { - return new Path.SelfElement(); - } - }); - - // associations - instance.addConverter(String.class, ChildAssociationRef.class, new TypeConverter.Converter() - { - public ChildAssociationRef convert(String source) - { - return new ChildAssociationRef(source); - } - }); - - instance.addConverter(String.class, AssociationRef.class, new TypeConverter.Converter() - { - public AssociationRef convert(String source) - { - return new AssociationRef(source); - } - }); - - // qnames - instance.addConverter(String.class, QName.class, new TypeConverter.Converter() - { - public QName convert(String source) - { - return QName.resolveToQName(SOLRTypeConverter.this.namespaceDAO, source); - } - }); - - instance.addConverter(String.class, MLText.class, new TypeConverter.Converter() - { - public MLText convert(String source) - { - return new MLText(source); - } - }); - } - - public final T convert(Class c, Object value) - { - return instance.convert(c, value); - } - } - - /* - * Deserializes JSON values from the remote API into objects consumable by SOLR - */ - private class SOLRDeserializer - { - private SOLRTypeConverter typeConverter; - - public SOLRDeserializer(NamespaceDAO namespaceDAO) - { - typeConverter = new SOLRTypeConverter(namespaceDAO); - } - - public T deserializeValue(Class targetClass, Object value) throws JSONException - { - return typeConverter.convert(targetClass, value); - } - } - - private static class SOLRResponse - { - protected Response response; - - public SOLRResponse(Response response) - { - super(); - this.response = response; - } - - public Response getResponse() - { - return response; - } - } - - public static class GetTransactionsResponse extends SOLRResponse - { - private List txns; - - public GetTransactionsResponse(Response response, List txns) - { - super(response); - this.txns = txns; - } - - public List getTransaction() - { - return txns; - } - } - - public static class GetNodesResponse extends SOLRResponse - { - private List nodes; - - public GetNodesResponse(Response response, List nodes) - { - super(response); - this.nodes = nodes; - } - - public List getNodes() - { - return nodes; - } - } - - public static class GetNodesMetaDataResponse extends SOLRResponse - { - private List nodes; - - public GetNodesMetaDataResponse(Response response, List nodes) - { - super(response); - this.nodes = nodes; - } - - public List getNodes() - { - return nodes; - } - } - - public static enum SolrApiContentStatus - { - NOT_MODIFIED, OK, NO_TRANSFORM, NO_CONTENT, UNKNOWN, TRANSFORM_FAILED, GENERAL_FAILURE; - - public static SolrApiContentStatus getStatus(String statusStr) - { - if(statusStr.equals("ok")) - { - return OK; - } - else if(statusStr.equals("transformFailed")) - { - return TRANSFORM_FAILED; - } - else if(statusStr.equals("noTransform")) - { - return NO_TRANSFORM; - } - else if(statusStr.equals("noContent")) - { - return NO_CONTENT; - } - else - { - return UNKNOWN; - } - } - } - - // TODO register a stream close listener that release the response when the response has been read - public static class GetTextContentResponse extends SOLRResponse implements AutoCloseable - { - private InputStream content; - private SolrApiContentStatus status; - private String transformException; - private String transformStatusStr; - private Long transformDuration; - private String contentEncoding; - - public GetTextContentResponse(Response response) throws IOException - { - super(response); - - this.content = response.getContentAsStream(); - this.transformStatusStr = response.getHeader("X-Alfresco-transformStatus"); - this.transformException = response.getHeader("X-Alfresco-transformException"); - String tmp = response.getHeader("X-Alfresco-transformDuration"); - this.transformDuration = (tmp != null ? Long.valueOf(tmp) : null); - this.contentEncoding = response.getHeader("Content-Encoding"); - setStatus(); - } - - public InputStream getContent() - { - return content; - } - - public SolrApiContentStatus getStatus() - { - return status; - } - - private void setStatus() - { - int status = response.getStatus(); - if(status == HttpStatus.SC_NOT_MODIFIED) - { - this.status = SolrApiContentStatus.NOT_MODIFIED; - } - else if(status == HttpStatus.SC_INTERNAL_SERVER_ERROR) - { - this.status = SolrApiContentStatus.GENERAL_FAILURE; - } - else if(status == HttpStatus.SC_OK) - { - this.status = SolrApiContentStatus.OK; - } - else if(status == HttpStatus.SC_NO_CONTENT) - { - if(transformStatusStr == null) - { - this.status = SolrApiContentStatus.UNKNOWN; - } - else - { - if(transformStatusStr.equals("noTransform")) - { - this.status = SolrApiContentStatus.NO_TRANSFORM; - } - else if(transformStatusStr.equals("transformFailed")) - { - this.status = SolrApiContentStatus.TRANSFORM_FAILED; - } - else - { - this.status = SolrApiContentStatus.UNKNOWN; - } - } - } - } - - public String getTransformException() - { - return transformException; - } - - public void release() - { - response.release(); - } - - public Long getTransformDuration() - { - return transformDuration; - } - - public String getContentEncoding() - { - return contentEncoding; - } - - @Override - public void close() { - response.release(); - } - } - - public void close() - { - repositoryHttpClient.close(); - } - - private JSONObject callRepository(String msgId, Request req) throws IOException, AuthenticationException - { - Response response = null; - LookAheadBufferedReader reader = null; - JSONObject json; - try - { - response = repositoryHttpClient.sendRequest(req); - if (response.getStatus() != HttpStatus.SC_OK) - { - throw new AlfrescoRuntimeException(msgId + " return status:" + response.getStatus()); - } - - reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); - json = new JSONObject(new JSONTokener(reader)); - - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug(json.toString(3)); - } - return json; - } - catch (JSONException exception) - { - String message = "Received a malformed JSON payload. Request was \"" + - req.getFullUri() + - "Data: " - + ofNullable(reader) - .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) - .orElse("Not available"); - LOGGER.error(message); - throw exception; - } - finally - { - ofNullable(response).ifPresent(Response::release); - ofNullable(reader).ifPresent(this::silentlyClose); - } - } - - private void silentlyClose(Closeable closeable) - { - try - { - closeable.close(); - } - catch (Exception ignore) - { - // Nothing to be done here - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.AlfrescoHttpClient; +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.httpclient.GetRequest; +import org.alfresco.httpclient.PostRequest; +import org.alfresco.httpclient.Request; +import org.alfresco.httpclient.Response; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.index.shard.ShardState; +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.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.MLText; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.Path.AttributeElement; +import org.alfresco.service.cmr.repository.Path.ChildAssocElement; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConversionException; +import org.alfresco.service.cmr.repository.datatype.TypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConverter.Converter; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO8601DateFormat; +import org.alfresco.util.Pair; +import org.apache.commons.codec.EncoderException; +import org.apache.commons.codec.net.URLCodec; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.util.DateUtil; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.extensions.surf.util.URLEncoder; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import static java.util.Optional.ofNullable; + +// TODO error handling, including dealing with a repository that is not responsive (ConnectException in sendRemoteRequest) +// TODO get text content transform status handling +/** + * Http client to handle SOLR-Alfresco remote calls. + * + * @since 4.0 + */ +public class SOLRAPIClient +{ + protected final static Logger LOGGER = LoggerFactory.getLogger(SOLRAPIClient.class); + private static final String GET_ACL_CHANGESETS_URL = "api/solr/aclchangesets"; + private static final String GET_ACLS = "api/solr/acls"; + private static final String GET_ACLS_READERS = "api/solr/aclsReaders"; + private static final String GET_TRANSACTIONS_URL = "api/solr/transactions"; + private static final String GET_METADATA_URL = "api/solr/metadata"; + private static final String GET_NODES_URL = "api/solr/nodes"; + private static final String GET_CONTENT = "api/solr/textContent"; + private static final String GET_MODEL = "api/solr/model"; + private static final String GET_MODELS_DIFF = "api/solr/modelsdiff"; + private static final String GET_NEXT_TX_COMMIT_TIME = "api/solr/nextTransaction"; + private static final String GET_TX_INTERVAL_COMMIT_TIME = "api/solr/transactionInterval"; + + private static final String CHECKSUM_HEADER = "XAlfresco-modelChecksum"; + + private AlfrescoHttpClient repositoryHttpClient; + private SOLRDeserializer deserializer; + private DictionaryService dictionaryService; + private JsonFactory jsonFactory; + private NamespaceDAO namespaceDAO; + + /** + * This option enables ("Accept-Encoding": "gzip") header for compression + * in GET_CONTENT requests. Additional configuration is required in + * Alfresco Repository Tomcat Connector or HTTP Web Proxy to deal + * with compressed requests. + */ + private boolean compression; + + public SOLRAPIClient(AlfrescoHttpClient repositoryHttpClient, + DictionaryService dictionaryService, + NamespaceDAO namespaceDAO) + { + this(repositoryHttpClient, dictionaryService, namespaceDAO, false); + } + + public SOLRAPIClient(AlfrescoHttpClient repositoryHttpClient, + DictionaryService dictionaryService, + NamespaceDAO namespaceDAO, + boolean compression) + { + this.repositoryHttpClient = repositoryHttpClient; + this.dictionaryService = dictionaryService; + this.namespaceDAO = namespaceDAO; + this.deserializer = new SOLRDeserializer(namespaceDAO); + this.jsonFactory = new JsonFactory(); + this.compression = compression; + } + + /** + * Get the ACL ChangeSets + * + * @param fromCommitTime the lowest commit time (optional) + * @param minAclChangeSetId the lowest ChangeSet ID (optional) + * @param maxResults the maximum number of results (a reasonable value only) + * @return the ACL ChangeSets in order of commit time and ID + */ + public AclChangeSets getAclChangeSets(Long fromCommitTime, Long minAclChangeSetId, Long toCommitTime, Long maxAclChangeSetId, int maxResults) + throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_ACL_CHANGESETS_URL); + StringBuilder args = new StringBuilder(); + if (fromCommitTime != null) + { + args.append("?").append("fromTime").append("=").append(fromCommitTime); + } + if (minAclChangeSetId != null) + { + args.append(args.length() == 0 ? "?" : "&").append("fromId").append("=").append(minAclChangeSetId); + } + if (toCommitTime != null) + { + args.append(args.length() == 0 ? "?" : "&").append("toTime").append("=").append(toCommitTime); + } + if (maxAclChangeSetId != null) + { + args.append(args.length() == 0 ? "?" : "&").append("toId").append("=").append(maxAclChangeSetId); + } + if (maxResults != 0 && maxResults != Integer.MAX_VALUE) + { + args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); + } + url.append(args); + + GetRequest req = new GetRequest(url.toString()); + JSONObject json = callRepository(GET_ACL_CHANGESETS_URL, req); + + JSONArray aclChangeSetsJSON = json.getJSONArray("aclChangeSets"); + List aclChangeSets = new ArrayList(aclChangeSetsJSON.length()); + for (int i = 0; i < aclChangeSetsJSON.length(); i++) + { + JSONObject aclChangeSetJSON = aclChangeSetsJSON.getJSONObject(i); + long aclChangeSetId = aclChangeSetJSON.getLong("id"); + long commitTimeMs = aclChangeSetJSON.getLong("commitTimeMs"); + int aclCount = aclChangeSetJSON.getInt("aclCount"); + AclChangeSet aclChangeSet = new AclChangeSet(aclChangeSetId, commitTimeMs, aclCount); + aclChangeSets.add(aclChangeSet); + } + + Long maxChangeSetCommitTime = null; + if(json.has("maxChangeSetCommitTime")) + { + maxChangeSetCommitTime = json.getLong("maxChangeSetCommitTime"); + } + + Long maxChangeSetIdOnServer = null; + if(json.has("maxChangeSetId")) + { + maxChangeSetIdOnServer = json.getLong("maxChangeSetId"); + } + + // Done + return new AclChangeSets(aclChangeSets, maxChangeSetCommitTime, maxChangeSetIdOnServer); + } + + /** + * Get the ACLs associated with a given list of ACL ChangeSets. The ACLs may be truncated for + * the last ACL ChangeSet in the return values - the ACL count from the + * {@link #getAclChangeSets(Long, Long, Long, Long, int) ACL ChangeSets}. + * + * @param aclChangeSets the ACL ChangeSets to include + * @param minAclId the lowest ACL ID (may be null) + * @param maxResults the maximum number of results to retrieve + * @return the ACLs (includes ChangeSet ID) + */ + public List getAcls(List aclChangeSets, Long minAclId, int maxResults) throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_ACLS); + StringBuilder args = new StringBuilder(); + if (minAclId != null) + { + args.append("?").append("fromId").append("=").append(minAclId); + } + + if(maxResults >= 0) + { + args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); + } + + url.append(args); + + JSONObject jsonReq = new JSONObject(); + JSONArray aclChangeSetIdsJSON = new JSONArray(); + List aclChangeSetIds = new ArrayList(); + for (AclChangeSet aclChangeSet : aclChangeSets) + { + Long aclChangeSetId = aclChangeSet.getId(); + aclChangeSetIdsJSON.put(aclChangeSetId); + aclChangeSetIds.add(aclChangeSetId); + } + jsonReq.put("aclChangeSetIds", aclChangeSetIdsJSON); + + PostRequest req = new PostRequest(url.toString(), jsonReq.toString(), "application/json"); + JSONObject json = callRepository(GET_ACL_CHANGESETS_URL, req); + + JSONArray aclsJSON = json.getJSONArray("acls"); + List acls = new ArrayList(aclsJSON.length()); + for (int i = 0; i < aclsJSON.length(); i++) + { + JSONObject aclJSON = aclsJSON.getJSONObject(i); + long aclChangeSetId = aclJSON.getLong("aclChangeSetId"); + long aclId = aclJSON.getLong("id"); + Acl acl = new Acl(aclChangeSetId, aclId); + acls.add(acl); + } + // Done + return acls; + } + + /** + * Get the ACL readers for a given list of ACLs + * + * @param acls the ACLs + * @return the readers for the ACLs + */ + public List getAclReaders(List acls) throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_ACLS_READERS); + + JSONObject jsonReq = new JSONObject(); + JSONArray aclIdsJSON = new JSONArray(); + List aclIds = new ArrayList(); + for (Acl acl : acls) + { + Long aclId = acl.getId(); + aclIdsJSON.put(aclId); + aclIds.add(aclId); + } + jsonReq.put("aclIds", aclIdsJSON); + + PostRequest req = new PostRequest(url.toString(), jsonReq.toString(), "application/json"); + JSONObject json = callRepository(GET_ACLS_READERS, req); + + JSONArray aclsReadersJSON = json.getJSONArray("aclsReaders"); + List aclsReaders = new ArrayList(aclsReadersJSON.length()); + for (int i = 0; i < aclsReadersJSON.length(); i++) + { + JSONObject aclReadersJSON = aclsReadersJSON.getJSONObject(i); + long aclId = aclReadersJSON.getLong("aclId"); + JSONArray readersJSON = aclReadersJSON.getJSONArray("readers"); + List readers = authorityListFromJSON(readersJSON); + JSONArray deniedJSON = aclReadersJSON.getJSONArray("denied"); + List denied = authorityListFromJSON(deniedJSON); + long aclChangeSetId = aclReadersJSON.getLong("aclChangeSetId"); + + String tenantDomain = aclReadersJSON.getString("tenantDomain"); + if (tenantDomain == null) + { + tenantDomain = TenantService.DEFAULT_DOMAIN; + } + + AclReaders aclReaders = new AclReaders(aclId, readers, denied, aclChangeSetId, tenantDomain); + aclsReaders.add(aclReaders); + } + // Done + return aclsReaders; + } + + /** + * Convert a JSON array of authorities to a simple Java List<String> + * + * @param jsonArray JSONArray + * @return List<String> + * @throws JSONException + */ + private List authorityListFromJSON(JSONArray jsonArray) throws JSONException + { + List authorities = new ArrayList(jsonArray.length()); + for (int j = 0; j < jsonArray.length(); j++) + { + String authority = jsonArray.getString(j); + authorities.add(authority); + } + return authorities; + } + + public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, Long maxTxnId, int maxResults) throws AuthenticationException, IOException, JSONException + { + try + { + return getTransactions(fromCommitTime, minTxnId, toCommitTime, maxTxnId, maxResults, null); + } + catch (EncoderException e) + { + // Can not happen .... + throw new IOException(e); + } + } + + public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, + Long maxTxnId, int maxResults, ShardState shardState) + throws AuthenticationException, IOException, JSONException, EncoderException + { + URLCodec encoder = new URLCodec(); + + StringBuilder url = new StringBuilder(GET_TRANSACTIONS_URL); + StringBuilder args = new StringBuilder(); + if (fromCommitTime != null) + { + args.append("?").append("fromCommitTime").append("=").append(fromCommitTime); + } + if (minTxnId != null) + { + args.append(args.length() == 0 ? "?" : "&").append("minTxnId").append("=").append(minTxnId); + } + if (toCommitTime != null) + { + args.append(args.length() == 0 ? "?" : "&").append("toCommitTime").append("=").append(toCommitTime); + } + if (maxTxnId != null) + { + args.append(args.length() == 0 ? "?" : "&").append("maxTxnId").append("=").append(maxTxnId); + } + if (maxResults != 0 && maxResults != Integer.MAX_VALUE) + { + args.append(args.length() == 0 ? "?" : "&").append("maxResults").append("=").append(maxResults); + } + if(shardState != null) + { + LOGGER.debug("### Shard state exists ###"); + args.append(args.length() == 0 ? "?" : "&"); + args.append(encoder.encode("baseUrl")).append("=").append(encoder.encode(shardState.getShardInstance().getBaseUrl())); + args.append("&").append(encoder.encode("hostName")).append("=").append(encoder.encode(shardState.getShardInstance().getHostName())); + args.append("&").append(encoder.encode("template")).append("=").append(encoder.encode(shardState.getShardInstance().getShard().getFloc().getTemplate())); + + for(String key : shardState.getShardInstance().getShard().getFloc().getPropertyBag().keySet()) + { + String value = shardState.getShardInstance().getShard().getFloc().getPropertyBag().get(key); + if(value != null) + { + args.append("&").append(encoder.encode("floc.property."+key)).append("=").append(encoder.encode(value)); + } + } + + for(String key : shardState.getPropertyBag().keySet()) + { + String value = shardState.getPropertyBag().get(key); + if(value != null) + { + args.append("&").append(encoder.encode("state.property."+key)).append("=").append(encoder.encode(value)); + } + } + + args.append("&").append(encoder.encode("instance")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getInstance())); + args.append("&").append(encoder.encode("numberOfShards")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getFloc().getNumberOfShards())); + args.append("&").append(encoder.encode("port")).append("=").append(encoder.encode("" + shardState.getShardInstance().getPort())); + args.append("&").append(encoder.encode("stores")).append("="); + for(StoreRef store : shardState.getShardInstance().getShard().getFloc().getStoreRefs()) + { + if(args.charAt(args.length()-1) != '=') + { + args.append(encoder.encode(",")); + } + args.append(encoder.encode(store.toString())); + } + args.append("&").append(encoder.encode("isMaster")).append("=").append(encoder.encode("" + shardState.isMaster())); + args.append("&").append(encoder.encode("hasContent")).append("=").append(encoder.encode("" + shardState.getShardInstance().getShard().getFloc().hasContent())); + args.append("&").append(encoder.encode("shardMethod")).append("=").append(encoder.encode(shardState.getShardInstance().getShard().getFloc().getShardMethod().toString())); + + args.append("&").append(encoder.encode("lastUpdated")).append("=").append(encoder.encode("" + shardState.getLastUpdated())); + args.append("&").append(encoder.encode("lastIndexedChangeSetCommitTime")).append("=").append(encoder.encode("" + shardState.getLastIndexedChangeSetCommitTime())); + args.append("&").append(encoder.encode("lastIndexedChangeSetId")).append("=").append(encoder.encode("" + shardState.getLastIndexedChangeSetId())); + args.append("&").append(encoder.encode("lastIndexedTxCommitTime")).append("=").append(encoder.encode("" + shardState.getLastIndexedTxCommitTime())); + args.append("&").append(encoder.encode("lastIndexedTxId")).append("=").append(encoder.encode("" + shardState.getLastIndexedTxId())); + + } + + url.append(args); + LOGGER.debug("### GetRequest: " + url.toString()); + GetRequest req = new GetRequest(url.toString()); + Response response = null; + List transactions = new ArrayList(); + Long maxTxnCommitTime = null; + Long maxTxnIdOnServer = null; + + LookAheadBufferedReader reader = null; + try + { + response = repositoryHttpClient.sendRequest(req); + if(response.getStatus() != HttpStatus.SC_OK) + { + throw new AlfrescoRuntimeException("GetTransactions return status is " + response.getStatus()); + } + + reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); + JsonParser parser = jsonFactory.createParser(reader); + + JsonToken token = parser.nextValue(); + while (token != null) + { + if ("transactions".equals(parser.getCurrentName())) + { + token = parser.nextToken(); //START_ARRAY + while (token == JsonToken.START_OBJECT) + { + token = parser.nextValue(); + long id = parser.getLongValue(); + + token = parser.nextValue(); + long commitTime = parser.getLongValue(); + + token = parser.nextValue(); + long updates = parser.getLongValue(); + + token = parser.nextValue(); + long deletes = parser.getLongValue(); + + Transaction txn = new Transaction(); + txn.setCommitTimeMs(commitTime); + txn.setDeletes(deletes); + txn.setId(id); + txn.setUpdates(updates); + + transactions.add(txn); + + token = parser.nextToken(); //END_OBJECT + token = parser.nextToken(); // START_OBJECT or END_ARRAY; + } + } + else if ("maxTxnCommitTime".equals(parser.getCurrentName())) + { + maxTxnCommitTime = parser.getLongValue(); + } + else if ("maxTxnId".equals(parser.getCurrentName())) + { + maxTxnIdOnServer = parser.getLongValue(); + } + token = parser.nextValue(); + } + parser.close(); + } + catch (JSONException exception) + { + String message = "Received a malformed JSON payload. Request was \"" + + req.getFullUri() + + "Data: " + + ofNullable(reader) + .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) + .orElse("Not available"); + LOGGER.error(message); + throw exception; + } + finally + { + ofNullable(response).ifPresent(Response::release); + ofNullable(reader).ifPresent(this::silentlyClose); + } + + LOGGER.debug("### Transactions found maxTxnCommitTime: " + maxTxnCommitTime ); + return new Transactions(transactions, maxTxnCommitTime, maxTxnIdOnServer); + } + + public List getNodes(GetNodesParameters parameters, int maxResults) throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_NODES_URL); + + JSONObject body = new JSONObject(); + + if(parameters.getTransactionIds() != null) + { + JSONArray jsonTxnIds = new JSONArray(); + for(Long txnId : parameters.getTransactionIds()) + { + jsonTxnIds.put(txnId); + } + body.put("txnIds", jsonTxnIds); + } + + if(parameters.getFromNodeId() != null) + { + body.put("fromNodeId", parameters.getFromNodeId()); + } + if(parameters.getToNodeId() != null) + { + body.put("toNodeId", parameters.getToNodeId()); + } + if(parameters.getExcludeAspects() != null) + { + JSONArray jsonExcludeAspects = new JSONArray(); + for(QName excludeAspect : parameters.getExcludeAspects()) + { + jsonExcludeAspects.put(excludeAspect.toString()); + } + body.put("excludeAspects", jsonExcludeAspects); + } + if(parameters.getIncludeAspects() != null) + { + JSONArray jsonIncludeAspects = new JSONArray(); + for(QName includeAspect : parameters.getIncludeAspects()) + { + jsonIncludeAspects.put(includeAspect.toString()); + } + body.put("includeAspects", jsonIncludeAspects); + } + + if(parameters.getStoreProtocol() != null) + { + body.put("storeProtocol", parameters.getStoreProtocol()); + } + + if(parameters.getStoreIdentifier() != null) + { + body.put("storeIdentifier", parameters.getStoreIdentifier()); + } + + body.put("maxResults", maxResults); + + if(parameters.getShardProperty() != null) + { + body.put("shardProperty", parameters.getShardProperty().toString()); + } + + if (parameters.getCoreName() != null){ + body.put("coreName", parameters.getCoreName()); + } + + + PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); + JSONObject json = callRepository(GET_NODES_URL, req); + + JSONArray jsonNodes = json.getJSONArray("nodes"); + List nodes = new ArrayList<>(jsonNodes.length()); + for(int i = 0; i < jsonNodes.length(); i++) + { + JSONObject jsonNodeInfo = jsonNodes.getJSONObject(i); + Node nodeInfo = new Node(); + if(jsonNodeInfo.has("id")) + { + nodeInfo.setId(jsonNodeInfo.getLong("id")); + } + + if(jsonNodeInfo.has("nodeRef")) + { + nodeInfo.setNodeRef(jsonNodeInfo.getString("nodeRef")); + } + + if(jsonNodeInfo.has("txnId")) + { + nodeInfo.setTxnId(jsonNodeInfo.getLong("txnId")); + } + + if(jsonNodeInfo.has("aclId")) + { + nodeInfo.setAclId(jsonNodeInfo.getLong("aclId")); + } + + if(jsonNodeInfo.has("shardPropertyValue")) + { + nodeInfo.setShardPropertyValue(jsonNodeInfo.getString("shardPropertyValue")); + } + + if(jsonNodeInfo.has("explicitShardId")) + { + nodeInfo.setExplicitShardId(jsonNodeInfo.getInt("explicitShardId")); + } + + if(jsonNodeInfo.has("tenant")) + { + nodeInfo.setTenant(jsonNodeInfo.getString("tenant")); + } + + if(jsonNodeInfo.has("status")) + { + Node.SolrApiNodeStatus status; + String statusStr = jsonNodeInfo.getString("status"); + if(statusStr.equals("u")) + { + status = Node.SolrApiNodeStatus.UPDATED; + } + else if(statusStr.equals("d")) + { + status = Node.SolrApiNodeStatus.DELETED; + } + else + { + status = Node.SolrApiNodeStatus.UNKNOWN; + } + nodeInfo.setStatus(status); + } + + nodes.add(nodeInfo); + } + + return nodes; + } + + private PropertyValue getSinglePropertyValue(DataTypeDefinition dataType, Object value) throws JSONException + { + PropertyValue ret = null; + QName dataTypeName = dataType.getName(); + + if(value == null || value == JSONObject.NULL) + { + ret = null; + } + else if(dataTypeName.equals(DataTypeDefinition.MLTEXT)) + { + JSONArray a = (JSONArray)value; + Map mlValues = new HashMap(a.length()); + + for(int k = 0; k < a.length(); k++) + { + JSONObject pair = a.getJSONObject(k); + Locale locale = deserializer.deserializeValue(Locale.class, pair.getString("locale")); + String mlValue = pair.has("value") && !pair.isNull("value") ? pair.getString("value") : null; + mlValues.put(locale, mlValue); + } + + ret = new MLTextPropertyValue(mlValues); + } + else if(dataTypeName.equals(DataTypeDefinition.CONTENT)) + { + JSONObject o = (JSONObject)value; + + String localeStr = o.has("locale") && !o.isNull("locale") ? o.getString("locale") : null; + Locale locale = (o.has("locale") && !o.isNull("locale") ? deserializer.deserializeValue(Locale.class, localeStr) : null); + + long size = o.has("size") && !o.isNull("size") ? o.getLong("size") : 0; + + String encoding = o.has("encoding") && !o.isNull("encoding") ? o.getString("encoding") : null; + String mimetype = o.has("mimetype") && !o.isNull("mimetype") ? o.getString("mimetype") : null; + + Long id = o.has("contentId") && !o.isNull("contentId") ? o.getLong("contentId") : null; + + ret = new ContentPropertyValue(locale, size, encoding, mimetype, id); + } + else + { + ret = new StringPropertyValue((String)value); + } + + return ret; + } + + private PropertyValue getPropertyValue(PropertyDefinition propertyDef, Object value) throws JSONException + { + PropertyValue ret = null; + + if(value == null || value == JSONObject.NULL) + { + ret = null; + } + else if(propertyDef == null) + { + // assume a string + ret = new StringPropertyValue((String)value); + } + else + { + DataTypeDefinition dataType = propertyDef.getDataType(); + + boolean isMulti = propertyDef.isMultiValued(); + if(isMulti) + { + if(!(value instanceof JSONArray)) + { + throw new IllegalArgumentException("Expected json array, got " + value.getClass().getName()); + } + + MultiPropertyValue multi = new MultiPropertyValue(); + JSONArray array = (JSONArray)value; + for(int j = 0; j < array.length(); j++) + { + multi.addValue(getSinglePropertyValue(dataType, array.get(j))); + } + + ret = multi; + } + else + { + ret = getSinglePropertyValue(dataType, value); + } + } + + return ret; + } + + public List getNodesMetaData(NodeMetaDataParameters params) throws AuthenticationException, IOException, JSONException + { + List nodeIds = params.getNodeIds(); + + StringBuilder url = new StringBuilder(GET_METADATA_URL); + + JSONObject body = new JSONObject(); + if(nodeIds != null && nodeIds.size() > 0) + { + JSONArray jsonNodeIds = new JSONArray(); + for(Long nodeId : nodeIds) + { + jsonNodeIds.put(nodeId); + } + body.put("nodeIds", jsonNodeIds); + + } + if(params.getFromNodeId() != null) + { + body.put("fromNodeId", params.getFromNodeId()); + } + if(params.getToNodeId() != null) + { + body.put("toNodeId", params.getToNodeId()); + } + + // only need to set in cases where we don't want them in the response + // because they default to true + if(!params.isIncludeAclId()) + { + body.put("includeAclId", params.isIncludeAclId()); + } + if(!params.isIncludeAspects()) + { + body.put("includeAspects", params.isIncludeAspects()); + } + if(!params.isIncludeProperties()) + { + body.put("includeProperties", params.isIncludeProperties()); + } + if(!params.isIncludeChildAssociations()) + { + body.put("includeChildAssociations", params.isIncludeChildAssociations()); + } + if(!params.isIncludeParentAssociations()) + { + body.put("includeParentAssociations", params.isIncludeParentAssociations()); + } + if(!params.isIncludeChildIds()) + { + body.put("includeChildIds", params.isIncludeChildIds()); + } + if(!params.isIncludePaths()) + { + body.put("includePaths", params.isIncludePaths()); + } + if(!params.isIncludeOwner()) + { + body.put("includeOwner", params.isIncludeOwner()); + } + if(!params.isIncludeNodeRef()) + { + body.put("includeNodeRef", params.isIncludeNodeRef()); + } + if(!params.isIncludeTxnId()) + { + body.put("includeTxnId", params.isIncludeTxnId()); + } + + if (params.getMaxResults().isPresent()) + { + body.put("maxResults", params.getMaxResults().getAsInt()); + } + + PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); + JSONObject json = callRepository(GET_METADATA_URL, req); + + JSONArray jsonNodes = json.getJSONArray("nodes"); + List nodes = new ArrayList<>(jsonNodes.length()); + for(int i = 0; i < jsonNodes.length(); i++) + { + JSONObject jsonNodeInfo = jsonNodes.getJSONObject(i); + NodeMetaData metaData = new NodeMetaData(); + + if(jsonNodeInfo.has("id")) + { + metaData.setId(jsonNodeInfo.getLong("id")); + } + + if(jsonNodeInfo.has("tenantDomain")) + { + metaData.setTenantDomain(jsonNodeInfo.getString("tenantDomain")); + } + + if(jsonNodeInfo.has("txnId")) + { + metaData.setTxnId(jsonNodeInfo.getLong("txnId")); + } + + if(jsonNodeInfo.has("aclId")) + { + metaData.setAclId(jsonNodeInfo.getLong("aclId")); + } + + if(jsonNodeInfo.has("nodeRef")) + { + metaData.setNodeRef(new NodeRef(jsonNodeInfo.getString("nodeRef"))); + } + + if(jsonNodeInfo.has("type")) + { + metaData.setType(deserializer.deserializeValue(QName.class, jsonNodeInfo.getString("type"))); + } + + if(jsonNodeInfo.has("aspects")) + { + JSONArray jsonAspects = jsonNodeInfo.getJSONArray("aspects"); + Set aspects = new HashSet(jsonAspects.length()); + for(int j = 0; j < jsonAspects.length(); j++) + { + String jsonAspect = (String)jsonAspects.get(j); + aspects.add(deserializer.deserializeValue(QName.class, jsonAspect)); + } + metaData.setAspects(aspects); + } + + if(jsonNodeInfo.has("paths")) + { + JSONArray jsonPaths = jsonNodeInfo.getJSONArray("paths"); + List> paths = new ArrayList>(jsonPaths.length()); + List ancestorPaths = new ArrayList(); + for(int j = 0; j < jsonPaths.length(); j++) + { + JSONObject path = jsonPaths.getJSONObject(j); + String pathValue = path.getString("path"); + QName qname = path.has("qname") ? deserializer.deserializeValue(QName.class, path.getString("qname")) : null; + paths.add(new Pair(pathValue, qname)); + if(path.has("apath")) + { + String ancestorPath = path.getString("apath"); + ancestorPaths.add(ancestorPath); + } + } + metaData.setPaths(paths); + metaData.setAncestorPaths(ancestorPaths); + } + + if(jsonNodeInfo.has("namePaths")) + { + JSONArray jsonNamePaths = jsonNodeInfo.getJSONArray("namePaths"); + List> namePaths = new ArrayList>(jsonNamePaths.length()); + for(int j = 0; j < jsonNamePaths.length(); j++) + { + JSONObject jsonNamePath = jsonNamePaths.getJSONObject(j); + JSONArray jsonNameElements = jsonNamePath.getJSONArray("namePath"); + List namePath = new ArrayList(jsonNameElements.length()); + for(int k = 0; k < jsonNameElements.length(); k++) + { + String namePathElement = jsonNameElements.getString(k); + namePath.add(namePathElement); + } + namePaths.add(namePath); + } + metaData.setNamePaths(namePaths); + } + + if(jsonNodeInfo.has("ancestors")) + { + JSONArray jsonAncestors = jsonNodeInfo.getJSONArray("ancestors"); + HashSet ancestors = new HashSet(jsonAncestors.length()); + for(int j = 0; j < jsonAncestors.length(); j++) + { + String ancestorNodeRefString = jsonAncestors.getString(j); + NodeRef ancestorNodeRef = new NodeRef(ancestorNodeRefString); + ancestors.add(ancestorNodeRef); + } + metaData.setAncestors(ancestors); + } + + if(jsonNodeInfo.has("properties")) + { + JSONObject jsonProperties = jsonNodeInfo.getJSONObject("properties"); + Map properties = new HashMap(jsonProperties.length()); + @SuppressWarnings("rawtypes") + Iterator propKeysIterator = jsonProperties.keys(); + while(propKeysIterator.hasNext()) + { + String propName = (String)propKeysIterator.next(); + QName propQName = deserializer.deserializeValue(QName.class, propName); + Object propValueObj = jsonProperties.opt(propName); + + // check the expected property type to determine how to process the value + PropertyDefinition propertyDef = dictionaryService.getProperty(propQName); +// if(propertyDef == null) +// { +// // TODO which exception here? +// throw new IllegalArgumentException("Could not find property definition for property " + propName); +// } + + properties.put(propQName, getPropertyValue(propertyDef, propValueObj)); + } + metaData.setProperties(properties); + } + + if(jsonNodeInfo.has("parentAssocsCrc")) + { + metaData.setParentAssocsCrc(jsonNodeInfo.getLong("parentAssocsCrc")); + } + + if(jsonNodeInfo.has("parentAssocs")) + { + JSONArray jsonParentAssocs = jsonNodeInfo.getJSONArray("parentAssocs"); + List assocs = new ArrayList(jsonParentAssocs.length()); + for(int j = 0; j < jsonParentAssocs.length(); j++) + { + String childAssocRefStr = jsonParentAssocs.getString(j); + ChildAssociationRef childAssociationRef = new ChildAssociationRef(childAssocRefStr); + assocs.add(childAssociationRef); + } + metaData.setParentAssocs(assocs); + } + + if(jsonNodeInfo.has("childAssocs")) + { + JSONArray jsonParentAssocs = jsonNodeInfo.getJSONArray("childAssocs"); + List assocs = new ArrayList(jsonParentAssocs.length()); + for(int j = 0; j < jsonParentAssocs.length(); j++) + { + String childAssocRefStr = jsonParentAssocs.getString(j); + ChildAssociationRef childAssociationRef = new ChildAssociationRef(childAssocRefStr); + assocs.add(childAssociationRef); + } + metaData.setChildAssocs(assocs); + } + + if(jsonNodeInfo.has("childIds")) + { + JSONArray jsonChildIds = jsonNodeInfo.getJSONArray("childIds"); + List childIds = new ArrayList(jsonChildIds.length()); + for(int j = 0; j < jsonChildIds.length(); j++) + { + Long childId = jsonChildIds.getLong(j); + childIds.add(childId); + } + metaData.setChildIds(childIds); + } + + if(jsonNodeInfo.has("owner")) + { + metaData.setOwner(jsonNodeInfo.getString("owner")); + } + + nodes.add(metaData); + } + + return nodes; + } + + public GetTextContentResponse getTextContent(Long nodeId, QName propertyQName, Long modifiedSince) throws AuthenticationException, IOException { + StringBuilder url = new StringBuilder(128); + url.append(GET_CONTENT); + + StringBuilder args = new StringBuilder(128); + if(nodeId != null) + { + args.append("?"); + args.append("nodeId"); + args.append("="); + args.append(nodeId); + } + else + { + throw new NullPointerException("getTextContent(): nodeId cannot be null."); + } + if(propertyQName != null) + { + if(args.length() == 0) + { + args.append("?"); + } + else + { + args.append("&"); + } + args.append("propertyQName"); + args.append("="); + args.append(URLEncoder.encode(propertyQName.toString())); + } + + url.append(args); + + GetRequest req = new GetRequest(url.toString()); + + Map headers = new HashMap<>(); + if(modifiedSince != null) + { + headers.put("If-Modified-Since", String.valueOf(DateUtil.formatDate(new Date(modifiedSince)))); + } + if (compression) + { + headers.put("Accept-Encoding", "gzip"); + } + req.setHeaders(headers); + + Response response = repositoryHttpClient.sendRequest(req); + + if(response.getStatus() != Status.STATUS_NOT_MODIFIED && response.getStatus() != Status.STATUS_NO_CONTENT && response.getStatus() != Status.STATUS_OK) + { + int status = response.getStatus(); + response.release(); + throw new AlfrescoRuntimeException("GetTextContentResponse return status is " + status); + } + + return new GetTextContentResponse(response); + } + + public AlfrescoModel getModel(String coreName, QName modelName) throws AuthenticationException, IOException + { + // If the model is new to the SOLR side the prefix will be unknown so we can not generate prefixes for the request! + // Always use the full QName with explicit URI + StringBuilder url = new StringBuilder(GET_MODEL); + + URLCodec encoder = new URLCodec(); + // must send the long name as we may not have the prefix registered + url.append("?modelQName=").append(encoder.encode(modelName.toString(), "UTF-8")); + + GetRequest req = new GetRequest(url.toString()); + + Response response = null; + try + { + response = repositoryHttpClient.sendRequest(req); + if(response.getStatus() != HttpStatus.SC_OK) + { + throw new AlfrescoRuntimeException(coreName + " GetModel return status is " + response.getStatus()); + } + + return new AlfrescoModel(M2Model.createModel(response.getContentAsStream()), + Long.valueOf(response.getHeader(CHECKSUM_HEADER))); + } + finally + { + if(response != null) + { + response.release(); + } + } + } + + public List getModelsDiff(String coreName, List currentModels) throws AuthenticationException, IOException, JSONException + { + StringBuilder url = new StringBuilder(GET_MODELS_DIFF); + + JSONObject body = new JSONObject(); + JSONArray jsonModels = new JSONArray(); + for(AlfrescoModel model : currentModels) + { + JSONObject jsonModel = new JSONObject(); + QName modelQName = QName.createQName( model.getModel().getName(), namespaceDAO); + jsonModel.put("name", modelQName.toString()); + jsonModel.put("checksum", model.getChecksum()); + jsonModels.put(jsonModel); + } + body.put("models", jsonModels); + + PostRequest req = new PostRequest(url.toString(), body.toString(), "application/json"); + JSONObject json = callRepository(GET_MODELS_DIFF, req); + + JSONArray jsonDiffs = json.getJSONArray("diffs"); + if(jsonDiffs == null) + { + throw new AlfrescoRuntimeException("GetModelsDiff badly formatted response"); + } + + List diffs = new ArrayList(jsonDiffs.length()); + for(int i = 0; i < jsonDiffs.length(); i++) + { + JSONObject jsonDiff = jsonDiffs.getJSONObject(i); + diffs.add(new AlfrescoModelDiff( + QName.createQName(jsonDiff.getString("name")), + AlfrescoModelDiff.TYPE.valueOf(jsonDiff.getString("type")), + (jsonDiff.isNull("oldChecksum") ? null : jsonDiff.getLong("oldChecksum")), + (jsonDiff.isNull("newChecksum") ? null : jsonDiff.getLong("newChecksum")))); + } + + return diffs; + } + + /** + * Returns the next commit time from a given commit time. + * + * @param coreName alfresco, archive + * @param fromCommitTime initial transaction commit time + * @return Time of the next transaction + * @throws IOException + * @throws AuthenticationException + * @throws NoSuchMethodException + */ + public Long getNextTxCommitTime(String coreName, Long fromCommitTime) throws AuthenticationException, IOException, NoSuchMethodException + { + StringBuilder url = new StringBuilder(GET_NEXT_TX_COMMIT_TIME); + url.append("?").append("fromCommitTime").append("=").append(fromCommitTime); + GetRequest get = new GetRequest(url.toString()); + Response response = null; + JSONObject json = null; + LookAheadBufferedReader reader = null; + try + { + response = repositoryHttpClient.sendRequest(get); + if (response.getStatus() != HttpStatus.SC_OK) + { + throw new NoSuchMethodException(coreName + " - GetNextTxCommitTime return status is " + + response.getStatus() + " when invoking " + url); + } + + reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); + json = new JSONObject(new JSONTokener(reader)); + } + catch (JSONException exception) + { + String message = "Received a malformed JSON payload. Request was \"" + + get.getFullUri() + + "Data: " + + ofNullable(reader) + .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) + .orElse("Not available"); + LOGGER.error(message); + throw exception; + } + finally + { + ofNullable(response).ifPresent(Response::release); + ofNullable(reader).ifPresent(this::silentlyClose); + } + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug(json.toString()); + } + + return Long.parseLong(json.get("nextTransactionCommitTimeMs").toString()); + } + + /** + * Returns the minimum and the maximum commit time for transactions in a node id range. + * + * @param coreName alfresco, archive + * @param fromNodeId Id of the initial node + * @param toNodeId Id of the final node + * @return Time of the first transaction, time of the last transaction + * @throws IOException + * @throws AuthenticationException + * @throws NoSuchMethodException + */ + public Pair getTxIntervalCommitTime(String coreName, Long fromNodeId, Long toNodeId) + throws AuthenticationException, IOException, NoSuchMethodException + { + StringBuilder url = new StringBuilder(GET_TX_INTERVAL_COMMIT_TIME); + url.append("?").append("fromNodeId").append("=").append(fromNodeId); + url.append("&").append("toNodeId").append("=").append(toNodeId); + GetRequest get = new GetRequest(url.toString()); + Response response = null; + JSONObject json = null; + LookAheadBufferedReader reader = null; + try + { + response = repositoryHttpClient.sendRequest(get); + if (response.getStatus() != HttpStatus.SC_OK) + { + throw new NoSuchMethodException(coreName + " - GetTxIntervalCommitTime return status is " + + response.getStatus() + " when invoking " + url); + } + + reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); + json = new JSONObject(new JSONTokener(reader)); + } + catch(JSONException exception) + { + String message = "Received a malformed JSON payload. Request was \"" + + get.getFullUri() + + "Data: " + + ofNullable(reader) + .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) + .orElse("Not available"); + LOGGER.error(message); + throw exception; + } + finally + { + ofNullable(response).ifPresent(Response::release); + ofNullable(reader).ifPresent(this::silentlyClose); + } + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug(json.toString()); + } + + return new Pair(Long.parseLong(json.get("minTransactionCommitTimeMs").toString()), + Long.parseLong(json.get("maxTransactionCommitTimeMs").toString())); + } + + /* + * type conversions from serialized JSON values to SOLR-consumable objects + */ + @SuppressWarnings("rawtypes") + private class SOLRTypeConverter + { + /** + * Default Type Converter + */ + private TypeConverter instance = new TypeConverter(); + private NamespaceDAO namespaceDAO; + + @SuppressWarnings("unchecked") + SOLRTypeConverter(NamespaceDAO namespaceDAO) + { + this.namespaceDAO = namespaceDAO; + + // add all default converters to this converter + for (Entry, Map, Converter>> source : DefaultTypeConverter.INSTANCE.getConverters().entrySet()) + { + for (Entry, Converter> dest : source.getValue().entrySet()) + { + instance.addConverter((Class) source.getKey(), (Class) dest.getKey(), dest.getValue()); + } + } + + // dates + instance.addConverter(String.class, Date.class, new TypeConverter.Converter() + { + public Date convert(String source) + { + try + { + return ISO8601DateFormat.parse(source); + } + catch (Exception e) + { + throw new TypeConversionException("Failed to convert date " + source + " to string", e); + } + } + }); + + // node refs + instance.addConverter(String.class, NodeRef.class, new TypeConverter.Converter() + { + public NodeRef convert(String source) + { + return new NodeRef(source); + } + }); + + // paths + instance.addConverter(String.class, AttributeElement.class, new TypeConverter.Converter() + { + public AttributeElement convert(String source) + { + return new Path.AttributeElement(source); + } + }); + + instance.addConverter(String.class, ChildAssocElement.class, new TypeConverter.Converter() + { + public ChildAssocElement convert(String source) + { + return new Path.ChildAssocElement(instance.convert(ChildAssociationRef.class, source)); + } + }); + + instance.addConverter(String.class, Path.DescendentOrSelfElement.class, new TypeConverter.Converter() + { + public Path.DescendentOrSelfElement convert(String source) + { + return new Path.DescendentOrSelfElement(); + } + }); + + instance.addConverter(String.class, Path.ParentElement.class, new TypeConverter.Converter() + { + public Path.ParentElement convert(String source) + { + return new Path.ParentElement(); + } + }); + + instance.addConverter(String.class, Path.SelfElement.class, new TypeConverter.Converter() + { + public Path.SelfElement convert(String source) + { + return new Path.SelfElement(); + } + }); + + // associations + instance.addConverter(String.class, ChildAssociationRef.class, new TypeConverter.Converter() + { + public ChildAssociationRef convert(String source) + { + return new ChildAssociationRef(source); + } + }); + + instance.addConverter(String.class, AssociationRef.class, new TypeConverter.Converter() + { + public AssociationRef convert(String source) + { + return new AssociationRef(source); + } + }); + + // qnames + instance.addConverter(String.class, QName.class, new TypeConverter.Converter() + { + public QName convert(String source) + { + return QName.resolveToQName(SOLRTypeConverter.this.namespaceDAO, source); + } + }); + + instance.addConverter(String.class, MLText.class, new TypeConverter.Converter() + { + public MLText convert(String source) + { + return new MLText(source); + } + }); + } + + public final T convert(Class c, Object value) + { + return instance.convert(c, value); + } + } + + /* + * Deserializes JSON values from the remote API into objects consumable by SOLR + */ + private class SOLRDeserializer + { + private SOLRTypeConverter typeConverter; + + public SOLRDeserializer(NamespaceDAO namespaceDAO) + { + typeConverter = new SOLRTypeConverter(namespaceDAO); + } + + public T deserializeValue(Class targetClass, Object value) throws JSONException + { + return typeConverter.convert(targetClass, value); + } + } + + private static class SOLRResponse + { + protected Response response; + + public SOLRResponse(Response response) + { + super(); + this.response = response; + } + + public Response getResponse() + { + return response; + } + } + + public static class GetTransactionsResponse extends SOLRResponse + { + private List txns; + + public GetTransactionsResponse(Response response, List txns) + { + super(response); + this.txns = txns; + } + + public List getTransaction() + { + return txns; + } + } + + public static class GetNodesResponse extends SOLRResponse + { + private List nodes; + + public GetNodesResponse(Response response, List nodes) + { + super(response); + this.nodes = nodes; + } + + public List getNodes() + { + return nodes; + } + } + + public static class GetNodesMetaDataResponse extends SOLRResponse + { + private List nodes; + + public GetNodesMetaDataResponse(Response response, List nodes) + { + super(response); + this.nodes = nodes; + } + + public List getNodes() + { + return nodes; + } + } + + public static enum SolrApiContentStatus + { + NOT_MODIFIED, OK, NO_TRANSFORM, NO_CONTENT, UNKNOWN, TRANSFORM_FAILED, GENERAL_FAILURE; + + public static SolrApiContentStatus getStatus(String statusStr) + { + if(statusStr.equals("ok")) + { + return OK; + } + else if(statusStr.equals("transformFailed")) + { + return TRANSFORM_FAILED; + } + else if(statusStr.equals("noTransform")) + { + return NO_TRANSFORM; + } + else if(statusStr.equals("noContent")) + { + return NO_CONTENT; + } + else + { + return UNKNOWN; + } + } + } + + // TODO register a stream close listener that release the response when the response has been read + public static class GetTextContentResponse extends SOLRResponse implements AutoCloseable + { + private InputStream content; + private SolrApiContentStatus status; + private String transformException; + private String transformStatusStr; + private Long transformDuration; + private String contentEncoding; + + public GetTextContentResponse(Response response) throws IOException + { + super(response); + + this.content = response.getContentAsStream(); + this.transformStatusStr = response.getHeader("X-Alfresco-transformStatus"); + this.transformException = response.getHeader("X-Alfresco-transformException"); + String tmp = response.getHeader("X-Alfresco-transformDuration"); + this.transformDuration = (tmp != null ? Long.valueOf(tmp) : null); + this.contentEncoding = response.getHeader("Content-Encoding"); + setStatus(); + } + + public InputStream getContent() + { + return content; + } + + public SolrApiContentStatus getStatus() + { + return status; + } + + private void setStatus() + { + int status = response.getStatus(); + if(status == HttpStatus.SC_NOT_MODIFIED) + { + this.status = SolrApiContentStatus.NOT_MODIFIED; + } + else if(status == HttpStatus.SC_INTERNAL_SERVER_ERROR) + { + this.status = SolrApiContentStatus.GENERAL_FAILURE; + } + else if(status == HttpStatus.SC_OK) + { + this.status = SolrApiContentStatus.OK; + } + else if(status == HttpStatus.SC_NO_CONTENT) + { + if(transformStatusStr == null) + { + this.status = SolrApiContentStatus.UNKNOWN; + } + else + { + if(transformStatusStr.equals("noTransform")) + { + this.status = SolrApiContentStatus.NO_TRANSFORM; + } + else if(transformStatusStr.equals("transformFailed")) + { + this.status = SolrApiContentStatus.TRANSFORM_FAILED; + } + else + { + this.status = SolrApiContentStatus.UNKNOWN; + } + } + } + } + + public String getTransformException() + { + return transformException; + } + + public void release() + { + response.release(); + } + + public Long getTransformDuration() + { + return transformDuration; + } + + public String getContentEncoding() + { + return contentEncoding; + } + + @Override + public void close() { + response.release(); + } + } + + public void close() + { + repositoryHttpClient.close(); + } + + private JSONObject callRepository(String msgId, Request req) throws IOException, AuthenticationException + { + Response response = null; + LookAheadBufferedReader reader = null; + JSONObject json; + try + { + response = repositoryHttpClient.sendRequest(req); + if (response.getStatus() != HttpStatus.SC_OK) + { + throw new AlfrescoRuntimeException(msgId + " return status:" + response.getStatus()); + } + + reader = new LookAheadBufferedReader(new InputStreamReader(response.getContentAsStream(), StandardCharsets.UTF_8), LOGGER); + json = new JSONObject(new JSONTokener(reader)); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug(json.toString(3)); + } + return json; + } + catch (JSONException exception) + { + String message = "Received a malformed JSON payload. Request was \"" + + req.getFullUri() + + "Data: " + + ofNullable(reader) + .map(LookAheadBufferedReader::lookAheadAndGetBufferedContent) + .orElse("Not available"); + LOGGER.error(message); + throw exception; + } + finally + { + ofNullable(response).ifPresent(Response::release); + ofNullable(reader).ifPresent(this::silentlyClose); + } + } + + private void silentlyClose(Closeable closeable) + { + try + { + closeable.close(); + } + catch (Exception ignore) + { + // Nothing to be done here + } + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClientFactory.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClientFactory.java index 571a7a77d..98afbac4e 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClientFactory.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIClientFactory.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIQueueClient.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIQueueClient.java index afa992d92..2e68fe80c 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIQueueClient.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/SOLRAPIQueueClient.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Status.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Status.java index ab392a6e4..2c21b5541 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Status.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Status.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,193 +24,193 @@ * #L% */ -package org.alfresco.solr.client; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.extensions.surf.util.I18NUtil; - -/** - * Status code constants for SOLR. - * - * @since 4.0 - */ -public class Status -{ - /* Status code constants */ - public static final int STATUS_CONTINUE = HttpServletResponse.SC_CONTINUE; - public static final int STATUS_SWITCHING_PROTOCOLS = HttpServletResponse.SC_SWITCHING_PROTOCOLS; - public static final int STATUS_OK = HttpServletResponse.SC_OK; - public static final int STATUS_CREATED = HttpServletResponse.SC_CREATED; - public static final int STATUS_ACCEPTED = HttpServletResponse.SC_ACCEPTED; - public static final int STATUS_NON_AUTHORITATIVE_INFORMATION = HttpServletResponse.SC_NON_AUTHORITATIVE_INFORMATION; - public static final int STATUS_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT; - public static final int STATUS_RESET_CONTENT = HttpServletResponse.SC_RESET_CONTENT; - public static final int STATUS_PARTIAL_CONTENT = HttpServletResponse.SC_PARTIAL_CONTENT; - public static final int STATUS_MULTIPLE_CHOICES = HttpServletResponse.SC_MULTIPLE_CHOICES; - public static final int STATUS_MOVED_PERMANENTLY = HttpServletResponse.SC_MOVED_PERMANENTLY; - public static final int STATUS_MOVED_TEMPORARILY = HttpServletResponse.SC_MOVED_TEMPORARILY; - public static final int STATUS_FOUND = HttpServletResponse.SC_FOUND; - public static final int STATUS_SEE_OTHER = HttpServletResponse.SC_SEE_OTHER; - public static final int STATUS_NOT_MODIFIED = HttpServletResponse.SC_NOT_MODIFIED; - public static final int STATUS_USE_PROXY = HttpServletResponse.SC_USE_PROXY; - public static final int STATUS_TEMPORARY_REDIRECT = HttpServletResponse.SC_TEMPORARY_REDIRECT; - public static final int STATUS_BAD_REQUEST = HttpServletResponse.SC_BAD_REQUEST; - public static final int STATUS_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED; - public static final int STATUS_PAYMENT_REQUIRED = HttpServletResponse.SC_PAYMENT_REQUIRED; - public static final int STATUS_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN; - public static final int STATUS_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND; - public static final int STATUS_METHOD_NOT_ALLOWED = HttpServletResponse.SC_METHOD_NOT_ALLOWED; - public static final int STATUS_NOT_ACCEPTABLE = HttpServletResponse.SC_NOT_ACCEPTABLE; - public static final int STATUS_PROXY_AUTHENTICATION_REQUIRED = HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED; - public static final int STATUS_REQUEST_TIMEOUT = HttpServletResponse.SC_REQUEST_TIMEOUT; - public static final int STATUS_CONFLICT = HttpServletResponse.SC_CONFLICT; - public static final int STATUS_GONE = HttpServletResponse.SC_GONE; - public static final int STATUS_LENGTH_REQUIRED = HttpServletResponse.SC_LENGTH_REQUIRED; - public static final int STATUS_PRECONDITION_FAILED = HttpServletResponse.SC_PRECONDITION_FAILED; - public static final int STATUS_REQUEST_ENTITY_TOO_LARGE = HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE; - public static final int STATUS_REQUEST_URI_TOO_LONG = HttpServletResponse.SC_REQUEST_URI_TOO_LONG; - public static final int STATUS_UNSUPPORTED_MEDIA_TYPE = HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; - public static final int STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE; - public static final int STATUS_EXPECTATION_FAILED = HttpServletResponse.SC_EXPECTATION_FAILED; - public static final int STATUS_INTERNAL_SERVER_ERROR = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - public static final int STATUS_NOT_IMPLEMENTED = HttpServletResponse.SC_NOT_IMPLEMENTED; - public static final int STATUS_BAD_GATEWAY = HttpServletResponse.SC_BAD_GATEWAY; - public static final int STATUS_SERVICE_UNAVAILABLE = HttpServletResponse.SC_SERVICE_UNAVAILABLE; - public static final int STATUS_GATEWAY_TIMEOUT = HttpServletResponse.SC_GATEWAY_TIMEOUT; - public static final int STATUS_HTTP_VERSION_NOT_SUPPORTED = HttpServletResponse.SC_HTTP_VERSION_NOT_SUPPORTED; - - - private Throwable exception = null; - private String location = ""; - private int code = HttpServletResponse.SC_OK; - private String message = ""; - private boolean redirect = false; - - /** - * Helper method to set the code and message. - *

- * Redirect is set to true. - * - * @param code code - * @param message message - */ - public void setCode(int code, String message) - { - this.code = code; - this.message = message; - this.redirect = true; - } - - /** - * @param exception Throwable - */ - public void setException(Throwable exception) - { - this.exception = exception; - } - - /** - * @return exception - */ - public Throwable getException() - { - return exception; - } - - /** - * @param message String - */ - public void setMessage(String message) - { - this.message = message; - } - - /** - * @return message - */ - public String getMessage() - { - return message; - } - - /** - * @param redirect redirect to status code response - */ - public void setRedirect(boolean redirect) - { - this.redirect = redirect; - } - - /** - * @return redirect to status code response - */ - public boolean getRedirect() - { - return redirect; - } - - /** - * @see javax.servlet.http.HTTPServletResponse - * - * @param code status code - */ - public void setCode(int code) - { - this.code = code; - } - - /** - * @return status code - */ - public int getCode() - { - return code; - } - - /** - * Gets the short name of the status code - * - * @return status code name - */ - public String getCodeName() - { - String codeName = I18NUtil.getMessage("webscript.code." + code + ".name"); - return codeName == null ? "" : codeName; - } - - /** - * @see javax.servlet.http.HTTPServletResponse - * - * @param location location response-header - */ - public void setLocation(String location) - { - this.location = location; - } - - /** - * @return location - */ - public String getLocation() - { - return location; - } - - /** - * Gets the description of the status code - * - * @return status code description - */ - public String getCodeDescription() - { - String codeDescription = I18NUtil.getMessage("webscript.code." + code + ".description"); - return codeDescription == null ? "" : codeDescription; - } - - @Override - public String toString() - { - return Integer.toString(code); - } -} +package org.alfresco.solr.client; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Status code constants for SOLR. + * + * @since 4.0 + */ +public class Status +{ + /* Status code constants */ + public static final int STATUS_CONTINUE = HttpServletResponse.SC_CONTINUE; + public static final int STATUS_SWITCHING_PROTOCOLS = HttpServletResponse.SC_SWITCHING_PROTOCOLS; + public static final int STATUS_OK = HttpServletResponse.SC_OK; + public static final int STATUS_CREATED = HttpServletResponse.SC_CREATED; + public static final int STATUS_ACCEPTED = HttpServletResponse.SC_ACCEPTED; + public static final int STATUS_NON_AUTHORITATIVE_INFORMATION = HttpServletResponse.SC_NON_AUTHORITATIVE_INFORMATION; + public static final int STATUS_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT; + public static final int STATUS_RESET_CONTENT = HttpServletResponse.SC_RESET_CONTENT; + public static final int STATUS_PARTIAL_CONTENT = HttpServletResponse.SC_PARTIAL_CONTENT; + public static final int STATUS_MULTIPLE_CHOICES = HttpServletResponse.SC_MULTIPLE_CHOICES; + public static final int STATUS_MOVED_PERMANENTLY = HttpServletResponse.SC_MOVED_PERMANENTLY; + public static final int STATUS_MOVED_TEMPORARILY = HttpServletResponse.SC_MOVED_TEMPORARILY; + public static final int STATUS_FOUND = HttpServletResponse.SC_FOUND; + public static final int STATUS_SEE_OTHER = HttpServletResponse.SC_SEE_OTHER; + public static final int STATUS_NOT_MODIFIED = HttpServletResponse.SC_NOT_MODIFIED; + public static final int STATUS_USE_PROXY = HttpServletResponse.SC_USE_PROXY; + public static final int STATUS_TEMPORARY_REDIRECT = HttpServletResponse.SC_TEMPORARY_REDIRECT; + public static final int STATUS_BAD_REQUEST = HttpServletResponse.SC_BAD_REQUEST; + public static final int STATUS_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED; + public static final int STATUS_PAYMENT_REQUIRED = HttpServletResponse.SC_PAYMENT_REQUIRED; + public static final int STATUS_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN; + public static final int STATUS_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND; + public static final int STATUS_METHOD_NOT_ALLOWED = HttpServletResponse.SC_METHOD_NOT_ALLOWED; + public static final int STATUS_NOT_ACCEPTABLE = HttpServletResponse.SC_NOT_ACCEPTABLE; + public static final int STATUS_PROXY_AUTHENTICATION_REQUIRED = HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED; + public static final int STATUS_REQUEST_TIMEOUT = HttpServletResponse.SC_REQUEST_TIMEOUT; + public static final int STATUS_CONFLICT = HttpServletResponse.SC_CONFLICT; + public static final int STATUS_GONE = HttpServletResponse.SC_GONE; + public static final int STATUS_LENGTH_REQUIRED = HttpServletResponse.SC_LENGTH_REQUIRED; + public static final int STATUS_PRECONDITION_FAILED = HttpServletResponse.SC_PRECONDITION_FAILED; + public static final int STATUS_REQUEST_ENTITY_TOO_LARGE = HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE; + public static final int STATUS_REQUEST_URI_TOO_LONG = HttpServletResponse.SC_REQUEST_URI_TOO_LONG; + public static final int STATUS_UNSUPPORTED_MEDIA_TYPE = HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; + public static final int STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE; + public static final int STATUS_EXPECTATION_FAILED = HttpServletResponse.SC_EXPECTATION_FAILED; + public static final int STATUS_INTERNAL_SERVER_ERROR = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; + public static final int STATUS_NOT_IMPLEMENTED = HttpServletResponse.SC_NOT_IMPLEMENTED; + public static final int STATUS_BAD_GATEWAY = HttpServletResponse.SC_BAD_GATEWAY; + public static final int STATUS_SERVICE_UNAVAILABLE = HttpServletResponse.SC_SERVICE_UNAVAILABLE; + public static final int STATUS_GATEWAY_TIMEOUT = HttpServletResponse.SC_GATEWAY_TIMEOUT; + public static final int STATUS_HTTP_VERSION_NOT_SUPPORTED = HttpServletResponse.SC_HTTP_VERSION_NOT_SUPPORTED; + + + private Throwable exception = null; + private String location = ""; + private int code = HttpServletResponse.SC_OK; + private String message = ""; + private boolean redirect = false; + + /** + * Helper method to set the code and message. + *

+ * Redirect is set to true. + * + * @param code code + * @param message message + */ + public void setCode(int code, String message) + { + this.code = code; + this.message = message; + this.redirect = true; + } + + /** + * @param exception Throwable + */ + public void setException(Throwable exception) + { + this.exception = exception; + } + + /** + * @return exception + */ + public Throwable getException() + { + return exception; + } + + /** + * @param message String + */ + public void setMessage(String message) + { + this.message = message; + } + + /** + * @return message + */ + public String getMessage() + { + return message; + } + + /** + * @param redirect redirect to status code response + */ + public void setRedirect(boolean redirect) + { + this.redirect = redirect; + } + + /** + * @return redirect to status code response + */ + public boolean getRedirect() + { + return redirect; + } + + /** + * @see javax.servlet.http.HTTPServletResponse + * + * @param code status code + */ + public void setCode(int code) + { + this.code = code; + } + + /** + * @return status code + */ + public int getCode() + { + return code; + } + + /** + * Gets the short name of the status code + * + * @return status code name + */ + public String getCodeName() + { + String codeName = I18NUtil.getMessage("webscript.code." + code + ".name"); + return codeName == null ? "" : codeName; + } + + /** + * @see javax.servlet.http.HTTPServletResponse + * + * @param location location response-header + */ + public void setLocation(String location) + { + this.location = location; + } + + /** + * @return location + */ + public String getLocation() + { + return location; + } + + /** + * Gets the description of the status code + * + * @return status code description + */ + public String getCodeDescription() + { + String codeDescription = I18NUtil.getMessage("webscript.code." + code + ".description"); + return codeDescription == null ? "" : codeDescription; + } + + @Override + public String toString() + { + return Integer.toString(code); + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/StringPropertyValue.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/StringPropertyValue.java index 212da7461..ae6b6d236 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/StringPropertyValue.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/StringPropertyValue.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,31 +24,31 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * Represents a property value as a string - * - * @since 4.0 - */ -public class StringPropertyValue extends PropertyValue -{ - private String value; - - public StringPropertyValue(String value) - { - super(); - this.value = value; - } - - public String getValue() - { - return value; - } - - @Override - public String toString() - { - return "StringPropertyValue [value=" + value + "]"; - } -} +package org.alfresco.solr.client; + +/** + * Represents a property value as a string + * + * @since 4.0 + */ +public class StringPropertyValue extends PropertyValue +{ + private String value; + + public StringPropertyValue(String value) + { + super(); + this.value = value; + } + + public String getValue() + { + return value; + } + + @Override + public String toString() + { + return "StringPropertyValue [value=" + value + "]"; + } +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transaction.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transaction.java index 19ed9adfe..7e253de8e 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transaction.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transaction.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,89 +24,89 @@ * #L% */ -package org.alfresco.solr.client; - -/** - * SOLR-side representation of transaction information. - * - * @since 4.0 - */ -public class Transaction -{ - private long id; - private long commitTimeMs; - private long updates; - private long deletes; - - public long getId() - { - return id; - } - public void setId(long id) - { - this.id = id; - } - public long getCommitTimeMs() - { - return commitTimeMs; - } - public void setCommitTimeMs(long commitTimeMs) - { - this.commitTimeMs = commitTimeMs; - } - public long getUpdates() - { - return updates; - } - public void setUpdates(long updates) - { - this.updates = updates; - } - public long getDeletes() - { - return deletes; - } - public void setDeletes(long deletes) - { - this.deletes = deletes; - } - @Override - public String toString() - { - return "Transaction [id=" + id + ", commitTimeMs=" + commitTimeMs + ", updates=" + updates + ", deletes=" - + deletes + "]"; - } - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + (int) (commitTimeMs ^ (commitTimeMs >>> 32)); - result = prime * result + (int) (deletes ^ (deletes >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (int) (updates ^ (updates >>> 32)); - return result; - } - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Transaction other = (Transaction) obj; - if (commitTimeMs != other.commitTimeMs) - return false; - if (deletes != other.deletes) - return false; - if (id != other.id) - return false; - if (updates != other.updates) - return false; - return true; - } - - -} +package org.alfresco.solr.client; + +/** + * SOLR-side representation of transaction information. + * + * @since 4.0 + */ +public class Transaction +{ + private long id; + private long commitTimeMs; + private long updates; + private long deletes; + + public long getId() + { + return id; + } + public void setId(long id) + { + this.id = id; + } + public long getCommitTimeMs() + { + return commitTimeMs; + } + public void setCommitTimeMs(long commitTimeMs) + { + this.commitTimeMs = commitTimeMs; + } + public long getUpdates() + { + return updates; + } + public void setUpdates(long updates) + { + this.updates = updates; + } + public long getDeletes() + { + return deletes; + } + public void setDeletes(long deletes) + { + this.deletes = deletes; + } + @Override + public String toString() + { + return "Transaction [id=" + id + ", commitTimeMs=" + commitTimeMs + ", updates=" + updates + ", deletes=" + + deletes + "]"; + } + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (int) (commitTimeMs ^ (commitTimeMs >>> 32)); + result = prime * result + (int) (deletes ^ (deletes >>> 32)); + result = prime * result + (int) (id ^ (id >>> 32)); + result = prime * result + (int) (updates ^ (updates >>> 32)); + return result; + } + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Transaction other = (Transaction) obj; + if (commitTimeMs != other.commitTimeMs) + return false; + if (deletes != other.deletes) + return false; + if (id != other.id) + return false; + if (updates != other.updates) + return false; + return true; + } + + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transactions.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transactions.java index be6cdf25e..73c6da2ca 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transactions.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/Transactions.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,28 +24,28 @@ * #L% */ -package org.alfresco.solr.client; - +package org.alfresco.solr.client; + import java.util.Comparator; -import java.util.List; - -/** - * @author Andy - * - */ -public class Transactions -{ - private List transactions; - - private Long maxTxnCommitTime; - - private Long maxTxnId; - - public Transactions(List transactions, Long maxTxnCommitTime, Long maxTxnId) - { - this.transactions = transactions; - this.maxTxnCommitTime = maxTxnCommitTime; - this.maxTxnId = maxTxnId; +import java.util.List; + +/** + * @author Andy + * + */ +public class Transactions +{ + private List transactions; + + private Long maxTxnCommitTime; + + private Long maxTxnId; + + public Transactions(List transactions, Long maxTxnCommitTime, Long maxTxnId) + { + this.transactions = transactions; + this.maxTxnCommitTime = maxTxnCommitTime; + this.maxTxnId = maxTxnId; } public Transactions(List transactions) @@ -59,20 +59,20 @@ public class Transactions .max(Comparator.comparing(Transaction::getId)).get().getId(); } } - - public List getTransactions() - { - return transactions; - } - - public Long getMaxTxnCommitTime() - { - return maxTxnCommitTime; - } - - public Long getMaxTxnId() - { - return maxTxnId; - } - -} + + public List getTransactions() + { + return transactions; + } + + public Long getMaxTxnCommitTime() + { + return maxTxnCommitTime; + } + + public Long getMaxTxnId() + { + return maxTxnId; + } + +} diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/IndexHealthReport.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/IndexHealthReport.java index 14539177b..2edf71054 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/IndexHealthReport.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/IndexHealthReport.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,412 +24,412 @@ * #L% */ -package org.alfresco.solr.tracker; - -import org.alfresco.solr.InformationServerCollectionProvider; -import org.alfresco.solr.adapters.IOpenBitSet; - - -public class IndexHealthReport -{ - long dbTransactionCount; - - IOpenBitSet missingTxFromIndex; - - IOpenBitSet duplicatedTxInIndex; - - IOpenBitSet txInIndexButNotInDb; - - IOpenBitSet duplicatedLeafInIndex; - - IOpenBitSet duplicatedAuxInIndex; - - long transactionDocsInIndex; - - long uniqueTransactionDocsInIndex; - - long uniqueAclTransactionDocsInIndex; - - long aclTransactionDocsInIndex; - - long leafDocCountInIndex; - - long auxDocCountInIndex; - - long lastIndexedCommitTime; - - long lastIndexedIdBeforeHoles; - - long dbAclTransactionCount; - - IOpenBitSet missingAclTxFromIndex; - - IOpenBitSet duplicatedAclTxInIndex; - - IOpenBitSet aclTxInIndexButNotInDb; - - IOpenBitSet duplicatedErrorInIndex; - - IOpenBitSet duplicatedUnindexedInIndex; - - long errorDocCountInIndex; - - long unindexedDocCountInIndex; - - public IndexHealthReport(InformationServerCollectionProvider srv) - { - this.missingTxFromIndex = srv.getOpenBitSetInstance(); - this.duplicatedTxInIndex = srv.getOpenBitSetInstance(); - this.txInIndexButNotInDb = srv.getOpenBitSetInstance(); - this.duplicatedLeafInIndex = srv.getOpenBitSetInstance(); - this.duplicatedAuxInIndex = srv.getOpenBitSetInstance(); - - this.missingAclTxFromIndex = srv.getOpenBitSetInstance(); - this.duplicatedAclTxInIndex = srv.getOpenBitSetInstance(); - this.aclTxInIndexButNotInDb = srv.getOpenBitSetInstance(); - this.duplicatedErrorInIndex = srv.getOpenBitSetInstance(); - this.duplicatedUnindexedInIndex = srv.getOpenBitSetInstance(); - } - - /** - * @return the transactionDocsInIndex - */ - public long getTransactionDocsInIndex() - { - return transactionDocsInIndex; - } - - public long getAclTransactionDocsInIndex() - { - return aclTransactionDocsInIndex; - } - - /** - * @param leafDocCountInIndex long - */ - public void setLeafDocCountInIndex(long leafDocCountInIndex) - { - this.leafDocCountInIndex = leafDocCountInIndex; - } - - /** - * @return the leafDocCountInIndex - */ - public long getLeafDocCountInIndex() - { - return leafDocCountInIndex; - } - - /** - * @param auxDocCountInIndex long - */ - public void setAuxDocCountInIndex(long auxDocCountInIndex) - { - this.auxDocCountInIndex = auxDocCountInIndex; - } - - /** - * @return the leafDocCountInIndex - */ - public long getAuxDocCountInIndex() - { - return auxDocCountInIndex; - } - - /** - * @param txid long - */ - public void setDuplicatedLeafInIndex(long txid) - { - duplicatedLeafInIndex.set(txid); - - } - - /** - * @return the duplicatedLeafInIndex - */ - public IOpenBitSet getDuplicatedLeafInIndex() - { - return duplicatedLeafInIndex; - } - - /** - * @param txid long - */ - public void setDuplicatedAuxInIndex(long txid) - { - duplicatedAuxInIndex.set(txid); - - } - - /** - * @return the duplicatedLeafInIndex - */ - public IOpenBitSet getDuplicatedAuxInIndex() - { - return duplicatedAuxInIndex; - } - - /** - * @param transactionDocsInIndex - * the transactionDocsInIndex to set - */ - public void setTransactionDocsInIndex(long transactionDocsInIndex) - { - this.transactionDocsInIndex = transactionDocsInIndex; - } - - /** - * @param aclTransactionDocsInIndex - * the transactionDocsInIndex to set - */ - public void setAclTransactionDocsInIndex(long aclTransactionDocsInIndex) - { - this.aclTransactionDocsInIndex = aclTransactionDocsInIndex; - } - - /** - * @return the missingFromIndex - */ - public IOpenBitSet getMissingTxFromIndex() - { - return missingTxFromIndex; - } - - /** - * @return the missingFromIndex - */ - public IOpenBitSet getMissingAclTxFromIndex() - { - return missingAclTxFromIndex; - } - - /** - * @return the duplicatedInIndex - */ - public IOpenBitSet getDuplicatedTxInIndex() - { - return duplicatedTxInIndex; - } - - /** - * @return the duplicatedInIndex - */ - public IOpenBitSet getDuplicatedAclTxInIndex() - { - return duplicatedAclTxInIndex; - } - - /** - * @return the inIndexButNotInDb - */ - public IOpenBitSet getTxInIndexButNotInDb() - { - return txInIndexButNotInDb; - } - - /** - * @return the inIndexButNotInDb - */ - public IOpenBitSet getAclTxInIndexButNotInDb() - { - return aclTxInIndexButNotInDb; - } - - /** - * @return the dbTransactionCount - */ - public long getDbTransactionCount() - { - return dbTransactionCount; - } - - /** - * @param dbTransactionCount - * the dbTransactionCount to set - */ - public void setDbTransactionCount(long dbTransactionCount) - { - this.dbTransactionCount = dbTransactionCount; - } - - public void setMissingTxFromIndex(long txid) - { - missingTxFromIndex.set(txid); - } - - public void setMissingAclTxFromIndex(long txid) - { - missingAclTxFromIndex.set(txid); - } - - public void setDuplicatedTxInIndex(long txid) - { - duplicatedTxInIndex.set(txid); - } - - public void setDuplicatedAclTxInIndex(long txid) - { - duplicatedAclTxInIndex.set(txid); - } - - public void setTxInIndexButNotInDb(long txid) - { - txInIndexButNotInDb.set(txid); - } - - public void setAclTxInIndexButNotInDb(long txid) - { - aclTxInIndexButNotInDb.set(txid); - } - - /** - * @param lastIndexedCommitTime - * the lastIndexCommitTime to set - */ - public void setLastIndexedCommitTime(long lastIndexedCommitTime) - { - this.lastIndexedCommitTime = lastIndexedCommitTime; - } - - /** - * @return the lastIndexedIdBeforeHoles - */ - public long getLastIndexedIdBeforeHoles() - { - return lastIndexedIdBeforeHoles; - } - - /** - * @param lastIndexedIdBeforeHoles - * the lastIndexedIdBeforeHoles to set - */ - public void setLastIndexedIdBeforeHoles(long lastIndexedIdBeforeHoles) - { - this.lastIndexedIdBeforeHoles = lastIndexedIdBeforeHoles; - } - - /** - * @param dbAclTransactionCount long - */ - public void setDbAclTransactionCount(long dbAclTransactionCount) - { - this.dbAclTransactionCount = dbAclTransactionCount; - } - - /** - * @return the dbAclTransactionCount - */ - public long getDbAclTransactionCount() - { - return dbAclTransactionCount; - } - - /** - * @return the uniqueTransactionDocsInIndex - */ - public long getUniqueTransactionDocsInIndex() - { - return uniqueTransactionDocsInIndex; - } - - /** - * @param uniqueTransactionDocsInIndex the uniqueTransactionDocsInIndex to set - */ - public void setUniqueTransactionDocsInIndex(long uniqueTransactionDocsInIndex) - { - this.uniqueTransactionDocsInIndex = uniqueTransactionDocsInIndex; - } - - /** - * @return the uniqueAclTransactionDocsInIndex - */ - public long getUniqueAclTransactionDocsInIndex() - { - return uniqueAclTransactionDocsInIndex; - } - - /** - * @param uniqueAclTransactionDocsInIndex the uniqueAclTransactionDocsInIndex to set - */ - public void setUniqueAclTransactionDocsInIndex(long uniqueAclTransactionDocsInIndex) - { - this.uniqueAclTransactionDocsInIndex = uniqueAclTransactionDocsInIndex; - } - - /** - * @return the lastIndexedCommitTime - */ - public long getLastIndexedCommitTime() - { - return lastIndexedCommitTime; - } - - /** - * @param unindexedDocCountInIndex long - */ - public void setUnindexedDocCountInIndex(long unindexedDocCountInIndex) - { - this.unindexedDocCountInIndex = unindexedDocCountInIndex; - } - - /** - * @param dbId long - */ - public void setDuplicatedUnindexedInIndex(long dbId) - { - duplicatedUnindexedInIndex.set(dbId); - } - - /** - * @param errorDocCountInIndex long - */ - public void setErrorDocCountInIndex(long errorDocCountInIndex) - { - this.errorDocCountInIndex = errorDocCountInIndex; - } - - /** - * @param dbId long - */ - public void setDuplicatedErrorInIndex(long dbId) - { - duplicatedErrorInIndex.set(dbId); - - } - - /** - * @return the duplicatedErrorInIndex - */ - public IOpenBitSet getDuplicatedErrorInIndex() - { - return duplicatedErrorInIndex; - } - - /** - * @return the duplicatedUnindexedInIndex - */ - public IOpenBitSet getDuplicatedUnindexedInIndex() - { - return duplicatedUnindexedInIndex; - } - - /** - * @return the errorDocCountInIndex - */ - public long getErrorDocCountInIndex() - { - return errorDocCountInIndex; - } - - /** - * @return the unindexedDocCountInIndex - */ - public long getUnindexedDocCountInIndex() - { - return unindexedDocCountInIndex; - } - - - -} +package org.alfresco.solr.tracker; + +import org.alfresco.solr.InformationServerCollectionProvider; +import org.alfresco.solr.adapters.IOpenBitSet; + + +public class IndexHealthReport +{ + long dbTransactionCount; + + IOpenBitSet missingTxFromIndex; + + IOpenBitSet duplicatedTxInIndex; + + IOpenBitSet txInIndexButNotInDb; + + IOpenBitSet duplicatedLeafInIndex; + + IOpenBitSet duplicatedAuxInIndex; + + long transactionDocsInIndex; + + long uniqueTransactionDocsInIndex; + + long uniqueAclTransactionDocsInIndex; + + long aclTransactionDocsInIndex; + + long leafDocCountInIndex; + + long auxDocCountInIndex; + + long lastIndexedCommitTime; + + long lastIndexedIdBeforeHoles; + + long dbAclTransactionCount; + + IOpenBitSet missingAclTxFromIndex; + + IOpenBitSet duplicatedAclTxInIndex; + + IOpenBitSet aclTxInIndexButNotInDb; + + IOpenBitSet duplicatedErrorInIndex; + + IOpenBitSet duplicatedUnindexedInIndex; + + long errorDocCountInIndex; + + long unindexedDocCountInIndex; + + public IndexHealthReport(InformationServerCollectionProvider srv) + { + this.missingTxFromIndex = srv.getOpenBitSetInstance(); + this.duplicatedTxInIndex = srv.getOpenBitSetInstance(); + this.txInIndexButNotInDb = srv.getOpenBitSetInstance(); + this.duplicatedLeafInIndex = srv.getOpenBitSetInstance(); + this.duplicatedAuxInIndex = srv.getOpenBitSetInstance(); + + this.missingAclTxFromIndex = srv.getOpenBitSetInstance(); + this.duplicatedAclTxInIndex = srv.getOpenBitSetInstance(); + this.aclTxInIndexButNotInDb = srv.getOpenBitSetInstance(); + this.duplicatedErrorInIndex = srv.getOpenBitSetInstance(); + this.duplicatedUnindexedInIndex = srv.getOpenBitSetInstance(); + } + + /** + * @return the transactionDocsInIndex + */ + public long getTransactionDocsInIndex() + { + return transactionDocsInIndex; + } + + public long getAclTransactionDocsInIndex() + { + return aclTransactionDocsInIndex; + } + + /** + * @param leafDocCountInIndex long + */ + public void setLeafDocCountInIndex(long leafDocCountInIndex) + { + this.leafDocCountInIndex = leafDocCountInIndex; + } + + /** + * @return the leafDocCountInIndex + */ + public long getLeafDocCountInIndex() + { + return leafDocCountInIndex; + } + + /** + * @param auxDocCountInIndex long + */ + public void setAuxDocCountInIndex(long auxDocCountInIndex) + { + this.auxDocCountInIndex = auxDocCountInIndex; + } + + /** + * @return the leafDocCountInIndex + */ + public long getAuxDocCountInIndex() + { + return auxDocCountInIndex; + } + + /** + * @param txid long + */ + public void setDuplicatedLeafInIndex(long txid) + { + duplicatedLeafInIndex.set(txid); + + } + + /** + * @return the duplicatedLeafInIndex + */ + public IOpenBitSet getDuplicatedLeafInIndex() + { + return duplicatedLeafInIndex; + } + + /** + * @param txid long + */ + public void setDuplicatedAuxInIndex(long txid) + { + duplicatedAuxInIndex.set(txid); + + } + + /** + * @return the duplicatedLeafInIndex + */ + public IOpenBitSet getDuplicatedAuxInIndex() + { + return duplicatedAuxInIndex; + } + + /** + * @param transactionDocsInIndex + * the transactionDocsInIndex to set + */ + public void setTransactionDocsInIndex(long transactionDocsInIndex) + { + this.transactionDocsInIndex = transactionDocsInIndex; + } + + /** + * @param aclTransactionDocsInIndex + * the transactionDocsInIndex to set + */ + public void setAclTransactionDocsInIndex(long aclTransactionDocsInIndex) + { + this.aclTransactionDocsInIndex = aclTransactionDocsInIndex; + } + + /** + * @return the missingFromIndex + */ + public IOpenBitSet getMissingTxFromIndex() + { + return missingTxFromIndex; + } + + /** + * @return the missingFromIndex + */ + public IOpenBitSet getMissingAclTxFromIndex() + { + return missingAclTxFromIndex; + } + + /** + * @return the duplicatedInIndex + */ + public IOpenBitSet getDuplicatedTxInIndex() + { + return duplicatedTxInIndex; + } + + /** + * @return the duplicatedInIndex + */ + public IOpenBitSet getDuplicatedAclTxInIndex() + { + return duplicatedAclTxInIndex; + } + + /** + * @return the inIndexButNotInDb + */ + public IOpenBitSet getTxInIndexButNotInDb() + { + return txInIndexButNotInDb; + } + + /** + * @return the inIndexButNotInDb + */ + public IOpenBitSet getAclTxInIndexButNotInDb() + { + return aclTxInIndexButNotInDb; + } + + /** + * @return the dbTransactionCount + */ + public long getDbTransactionCount() + { + return dbTransactionCount; + } + + /** + * @param dbTransactionCount + * the dbTransactionCount to set + */ + public void setDbTransactionCount(long dbTransactionCount) + { + this.dbTransactionCount = dbTransactionCount; + } + + public void setMissingTxFromIndex(long txid) + { + missingTxFromIndex.set(txid); + } + + public void setMissingAclTxFromIndex(long txid) + { + missingAclTxFromIndex.set(txid); + } + + public void setDuplicatedTxInIndex(long txid) + { + duplicatedTxInIndex.set(txid); + } + + public void setDuplicatedAclTxInIndex(long txid) + { + duplicatedAclTxInIndex.set(txid); + } + + public void setTxInIndexButNotInDb(long txid) + { + txInIndexButNotInDb.set(txid); + } + + public void setAclTxInIndexButNotInDb(long txid) + { + aclTxInIndexButNotInDb.set(txid); + } + + /** + * @param lastIndexedCommitTime + * the lastIndexCommitTime to set + */ + public void setLastIndexedCommitTime(long lastIndexedCommitTime) + { + this.lastIndexedCommitTime = lastIndexedCommitTime; + } + + /** + * @return the lastIndexedIdBeforeHoles + */ + public long getLastIndexedIdBeforeHoles() + { + return lastIndexedIdBeforeHoles; + } + + /** + * @param lastIndexedIdBeforeHoles + * the lastIndexedIdBeforeHoles to set + */ + public void setLastIndexedIdBeforeHoles(long lastIndexedIdBeforeHoles) + { + this.lastIndexedIdBeforeHoles = lastIndexedIdBeforeHoles; + } + + /** + * @param dbAclTransactionCount long + */ + public void setDbAclTransactionCount(long dbAclTransactionCount) + { + this.dbAclTransactionCount = dbAclTransactionCount; + } + + /** + * @return the dbAclTransactionCount + */ + public long getDbAclTransactionCount() + { + return dbAclTransactionCount; + } + + /** + * @return the uniqueTransactionDocsInIndex + */ + public long getUniqueTransactionDocsInIndex() + { + return uniqueTransactionDocsInIndex; + } + + /** + * @param uniqueTransactionDocsInIndex the uniqueTransactionDocsInIndex to set + */ + public void setUniqueTransactionDocsInIndex(long uniqueTransactionDocsInIndex) + { + this.uniqueTransactionDocsInIndex = uniqueTransactionDocsInIndex; + } + + /** + * @return the uniqueAclTransactionDocsInIndex + */ + public long getUniqueAclTransactionDocsInIndex() + { + return uniqueAclTransactionDocsInIndex; + } + + /** + * @param uniqueAclTransactionDocsInIndex the uniqueAclTransactionDocsInIndex to set + */ + public void setUniqueAclTransactionDocsInIndex(long uniqueAclTransactionDocsInIndex) + { + this.uniqueAclTransactionDocsInIndex = uniqueAclTransactionDocsInIndex; + } + + /** + * @return the lastIndexedCommitTime + */ + public long getLastIndexedCommitTime() + { + return lastIndexedCommitTime; + } + + /** + * @param unindexedDocCountInIndex long + */ + public void setUnindexedDocCountInIndex(long unindexedDocCountInIndex) + { + this.unindexedDocCountInIndex = unindexedDocCountInIndex; + } + + /** + * @param dbId long + */ + public void setDuplicatedUnindexedInIndex(long dbId) + { + duplicatedUnindexedInIndex.set(dbId); + } + + /** + * @param errorDocCountInIndex long + */ + public void setErrorDocCountInIndex(long errorDocCountInIndex) + { + this.errorDocCountInIndex = errorDocCountInIndex; + } + + /** + * @param dbId long + */ + public void setDuplicatedErrorInIndex(long dbId) + { + duplicatedErrorInIndex.set(dbId); + + } + + /** + * @return the duplicatedErrorInIndex + */ + public IOpenBitSet getDuplicatedErrorInIndex() + { + return duplicatedErrorInIndex; + } + + /** + * @return the duplicatedUnindexedInIndex + */ + public IOpenBitSet getDuplicatedUnindexedInIndex() + { + return duplicatedUnindexedInIndex; + } + + /** + * @return the errorDocCountInIndex + */ + public long getErrorDocCountInIndex() + { + return errorDocCountInIndex; + } + + /** + * @return the unindexedDocCountInIndex + */ + public long getUnindexedDocCountInIndex() + { + return unindexedDocCountInIndex; + } + + + +} \ No newline at end of file diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/TrackerStats.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/TrackerStats.java index 278e1353a..a13c92011 100644 --- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/TrackerStats.java +++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/tracker/TrackerStats.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,967 +24,967 @@ * #L% */ -package org.alfresco.solr.tracker; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; +package org.alfresco.solr.tracker; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.concurrent.NotThreadSafe; -import org.alfresco.solr.InformationServerCollectionProvider; -import org.alfresco.solr.adapters.ISimpleOrderedMap; -import org.alfresco.util.Pair; - -/** - * @author Andy - */ -public class TrackerStats -{ - public static final int TIME_SCALE = 1000000; - - ConcurrentHashMap modelTimes = new ConcurrentHashMap(); - - ConcurrentHashMap aclTimes = new ConcurrentHashMap(); - - ConcurrentHashMap changeSetAcls = new ConcurrentHashMap(); - - ConcurrentHashMap txDocs = new ConcurrentHashMap(); - - ConcurrentHashMap docTransformationTimes = new ConcurrentHashMap(); - - ConcurrentHashMap nodeTimes = new ConcurrentHashMap(); - - ConcurrentHashMap elapsedNodeTimes = new ConcurrentHashMap(); - - ConcurrentHashMap elapsedAclTimes = new ConcurrentHashMap(); - - ConcurrentHashMap elapsedContentTimes = new ConcurrentHashMap(); - - private InformationServerCollectionProvider infoSrv; - - public TrackerStats(InformationServerCollectionProvider server) - { - infoSrv = server; - } - - /** - * @return the modelTimes - */ - public SimpleStats getModelTimes() - { - return aggregateResults(modelTimes); - } - - /** - * @param all ConcurrentHashMap - * @return SimpleStats - */ - private SimpleStats aggregateResults(ConcurrentHashMap all) - { - SimpleStats answer = null; - - for (String key : all.keySet()) - { - IncrementalStats next = all.get(key); - IncrementalStats stats = next.copy(); - if (answer == null) - { - answer = new SimpleStats(stats.scale, this.infoSrv); - answer .start = stats.start; - answer.moments[0] = stats.moments[0]; - answer.moments[1] = stats.moments[1]; - answer.moments[2] = stats.moments[2]; - answer.max = stats.max; - answer.min = stats.min; - answer.copies.put(key, stats); - } - else - { - SimpleStats newAnswer = new SimpleStats(answer.scale, this.infoSrv); - - newAnswer.moments[0] = answer.moments[0] + stats.moments[0]; - - newAnswer.moments[1] = answer.moments[1] * answer.moments[0] + stats.moments[1] * stats.moments[0]; - newAnswer.moments[1] /= answer.moments[0] + stats.moments[0]; - - newAnswer.moments[2] = answer.moments[2] * answer.moments[0]; - newAnswer.moments[2] += (answer.moments[1] - newAnswer.moments[1]) * (answer.moments[1] - newAnswer.moments[1]) * answer.moments[0]; - newAnswer.moments[2] += stats.moments[2] * stats.moments[0]; - newAnswer.moments[2] += (stats.moments[1] - newAnswer.moments[1]) * (stats.moments[1] - newAnswer.moments[1]) * stats.moments[0]; - newAnswer.moments[2] /= answer.moments[0] + stats.moments[0]; - - newAnswer.min = (stats.min < answer.min) ? stats.min : answer.min; - newAnswer.max = (stats.max > answer.max) ? stats.max : answer.max; - - newAnswer.start = (stats.start.compareTo(answer.start) < 1) ? stats.start : answer.start; - - newAnswer.copies.putAll(answer.copies); - newAnswer.copies.put(key, stats); - - answer = newAnswer; - } - - } - - if (answer == null) - { - answer = new SimpleStats(1, this.infoSrv); - } - - return answer; - } - - /** - * @return the aclTxTimes - */ - public SimpleStats getAclTimes() - { - return aggregateResults(aclTimes); - } - - public SimpleStats getChangeSetAcls() - { - return aggregateResults(changeSetAcls); - } - - public SimpleStats getNodeTimes() - { - return aggregateResults(nodeTimes); - } - - /** - * @return the txDocs - */ - public SimpleStats getTxDocs() - { - return aggregateResults(txDocs); - } - - /** - * @return the docTransformationTimes - */ - public SimpleStats getDocTransformationTimes() - { - return aggregateResults(docTransformationTimes); - } - - public double getMeanModelSyncTime() - { - return aggregateResults(modelTimes).getMean(); - } - - public double getMeanNodeIndexTime() - { - return aggregateResults(nodeTimes).getMean(); - } - - public double getMeanNodeElapsedIndexTime() - { - return aggregateResults(elapsedNodeTimes).getMean(); - } - - public double getMeanAclElapsedIndexTime() - { - return aggregateResults(elapsedAclTimes).getMean(); - } - - public double getMeanContentElapsedIndexTime() - { - return aggregateResults(elapsedContentTimes).getMean(); - } - - public double getNodeIndexingThreadCount() - { - return nodeTimes.size(); - } - - public double getMeanAclIndexTime() - { - return aggregateResults(nodeTimes).getMean(); - } - - public double getMeanDocsPerTx() - { - return aggregateResults(txDocs).getMean(); - } - - public double getMeanAclsPerChangeSet() - { - return aggregateResults(changeSetAcls).getMean(); - } - - public static class SimpleStats - { - HashMap copies = new HashMap(); - private InformationServerCollectionProvider server; - int scale; - - SimpleStats(int scale, InformationServerCollectionProvider server) - { - this.scale = scale; - this.server = server; - } - - double[] moments = new double[3]; - - double min = 0D; - - double max = 0D; - - Date start = null; - - synchronized long getN() - { - return (long) moments[0]; - } - - synchronized double getMin() - { - return min; - } - - synchronized double getMax() - { - return max; - } - - synchronized double getMean() - { - return moments[1]; - } - - synchronized double getVarience() - { - if (moments[0] > 1) - { - return moments[2] * moments[0] / (moments[0] - 1); - } - else - { - return Double.NaN; - } - } - - synchronized double getStandardDeviation() - { - return Math.sqrt(getVarience()); - } - - public synchronized ISimpleOrderedMap getNamedList(boolean incdludeDetail, boolean includeHist, boolean includeValues) - { - ISimpleOrderedMap map = this.server.getSimpleOrderedMapInstance(); - map.add("Start", start); - map.add("N", getN()); - map.add("Min", getMin()); - map.add("Max", getMax()); - map.add("Mean", getMean()); - map.add("Varience", getVarience()); - map.add("StdDev", getStandardDeviation()); - if (incdludeDetail) - { +import org.alfresco.solr.InformationServerCollectionProvider; +import org.alfresco.solr.adapters.ISimpleOrderedMap; +import org.alfresco.util.Pair; + +/** + * @author Andy + */ +public class TrackerStats +{ + public static final int TIME_SCALE = 1000000; + + ConcurrentHashMap modelTimes = new ConcurrentHashMap(); + + ConcurrentHashMap aclTimes = new ConcurrentHashMap(); + + ConcurrentHashMap changeSetAcls = new ConcurrentHashMap(); + + ConcurrentHashMap txDocs = new ConcurrentHashMap(); + + ConcurrentHashMap docTransformationTimes = new ConcurrentHashMap(); + + ConcurrentHashMap nodeTimes = new ConcurrentHashMap(); + + ConcurrentHashMap elapsedNodeTimes = new ConcurrentHashMap(); + + ConcurrentHashMap elapsedAclTimes = new ConcurrentHashMap(); + + ConcurrentHashMap elapsedContentTimes = new ConcurrentHashMap(); + + private InformationServerCollectionProvider infoSrv; + + public TrackerStats(InformationServerCollectionProvider server) + { + infoSrv = server; + } + + /** + * @return the modelTimes + */ + public SimpleStats getModelTimes() + { + return aggregateResults(modelTimes); + } + + /** + * @param all ConcurrentHashMap + * @return SimpleStats + */ + private SimpleStats aggregateResults(ConcurrentHashMap all) + { + SimpleStats answer = null; + + for (String key : all.keySet()) + { + IncrementalStats next = all.get(key); + IncrementalStats stats = next.copy(); + if (answer == null) + { + answer = new SimpleStats(stats.scale, this.infoSrv); + answer .start = stats.start; + answer.moments[0] = stats.moments[0]; + answer.moments[1] = stats.moments[1]; + answer.moments[2] = stats.moments[2]; + answer.max = stats.max; + answer.min = stats.min; + answer.copies.put(key, stats); + } + else + { + SimpleStats newAnswer = new SimpleStats(answer.scale, this.infoSrv); + + newAnswer.moments[0] = answer.moments[0] + stats.moments[0]; + + newAnswer.moments[1] = answer.moments[1] * answer.moments[0] + stats.moments[1] * stats.moments[0]; + newAnswer.moments[1] /= answer.moments[0] + stats.moments[0]; + + newAnswer.moments[2] = answer.moments[2] * answer.moments[0]; + newAnswer.moments[2] += (answer.moments[1] - newAnswer.moments[1]) * (answer.moments[1] - newAnswer.moments[1]) * answer.moments[0]; + newAnswer.moments[2] += stats.moments[2] * stats.moments[0]; + newAnswer.moments[2] += (stats.moments[1] - newAnswer.moments[1]) * (stats.moments[1] - newAnswer.moments[1]) * stats.moments[0]; + newAnswer.moments[2] /= answer.moments[0] + stats.moments[0]; + + newAnswer.min = (stats.min < answer.min) ? stats.min : answer.min; + newAnswer.max = (stats.max > answer.max) ? stats.max : answer.max; + + newAnswer.start = (stats.start.compareTo(answer.start) < 1) ? stats.start : answer.start; + + newAnswer.copies.putAll(answer.copies); + newAnswer.copies.put(key, stats); + + answer = newAnswer; + } + + } + + if (answer == null) + { + answer = new SimpleStats(1, this.infoSrv); + } + + return answer; + } + + /** + * @return the aclTxTimes + */ + public SimpleStats getAclTimes() + { + return aggregateResults(aclTimes); + } + + public SimpleStats getChangeSetAcls() + { + return aggregateResults(changeSetAcls); + } + + public SimpleStats getNodeTimes() + { + return aggregateResults(nodeTimes); + } + + /** + * @return the txDocs + */ + public SimpleStats getTxDocs() + { + return aggregateResults(txDocs); + } + + /** + * @return the docTransformationTimes + */ + public SimpleStats getDocTransformationTimes() + { + return aggregateResults(docTransformationTimes); + } + + public double getMeanModelSyncTime() + { + return aggregateResults(modelTimes).getMean(); + } + + public double getMeanNodeIndexTime() + { + return aggregateResults(nodeTimes).getMean(); + } + + public double getMeanNodeElapsedIndexTime() + { + return aggregateResults(elapsedNodeTimes).getMean(); + } + + public double getMeanAclElapsedIndexTime() + { + return aggregateResults(elapsedAclTimes).getMean(); + } + + public double getMeanContentElapsedIndexTime() + { + return aggregateResults(elapsedContentTimes).getMean(); + } + + public double getNodeIndexingThreadCount() + { + return nodeTimes.size(); + } + + public double getMeanAclIndexTime() + { + return aggregateResults(nodeTimes).getMean(); + } + + public double getMeanDocsPerTx() + { + return aggregateResults(txDocs).getMean(); + } + + public double getMeanAclsPerChangeSet() + { + return aggregateResults(changeSetAcls).getMean(); + } + + public static class SimpleStats + { + HashMap copies = new HashMap(); + private InformationServerCollectionProvider server; + int scale; + + SimpleStats(int scale, InformationServerCollectionProvider server) + { + this.scale = scale; + this.server = server; + } + + double[] moments = new double[3]; + + double min = 0D; + + double max = 0D; + + Date start = null; + + synchronized long getN() + { + return (long) moments[0]; + } + + synchronized double getMin() + { + return min; + } + + synchronized double getMax() + { + return max; + } + + synchronized double getMean() + { + return moments[1]; + } + + synchronized double getVarience() + { + if (moments[0] > 1) + { + return moments[2] * moments[0] / (moments[0] - 1); + } + else + { + return Double.NaN; + } + } + + synchronized double getStandardDeviation() + { + return Math.sqrt(getVarience()); + } + + public synchronized ISimpleOrderedMap getNamedList(boolean incdludeDetail, boolean includeHist, boolean includeValues) + { + ISimpleOrderedMap map = this.server.getSimpleOrderedMapInstance(); + map.add("Start", start); + map.add("N", getN()); + map.add("Min", getMin()); + map.add("Max", getMax()); + map.add("Mean", getMean()); + map.add("Varience", getVarience()); + map.add("StdDev", getStandardDeviation()); + if (incdludeDetail) + { for (Entry copy : copies.entrySet()) { map.add(copy.getKey(), copy.getValue().getNamedList(includeHist, includeValues)); } - } - - return map; - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - return "SimpleStats [" - + "\n getN()=" + getN() + ",\n getMin()=" + getMin() + ",\n getMax()=" + getMax() + ",\n getMean()=" - + getMean() + ",\n getVarience()=" + getVarience() + ",\n getStandardDeviation()=" + getStandardDeviation() + ",\n copies=" - + copies + ",\n]"; - } - - } - - public static class Bucket - { - IncrementalStats incrementalStats; - - public double leftBoundary; - - public double rightBoundary; - - public double countLeft; - - public double countRight; - - Bucket(IncrementalStats incrementalStats, double leftBoundary, double rightBoundary) - { - this(incrementalStats, leftBoundary, rightBoundary, 0D, 0D); - } - - Bucket(IncrementalStats incrementalStats, double leftBoundary, double rightBoundary, double countLeft, double countRight) - { - this.incrementalStats = incrementalStats; - this.leftBoundary = leftBoundary; - this.rightBoundary = rightBoundary; - this.countLeft = countLeft; - this.countRight = countRight; - } - - public void add(double x) - { - if ((x - leftBoundary) < (rightBoundary - x)) - { - countLeft++; - } - else - { - countRight++; - } - } - - public double mergeError(Bucket o) - { - double f_m = (this.countLeft + this.countRight + o.countLeft + o.countRight) / 4.0d; - double t1 = this.countLeft - f_m; - double t2 = this.countRight - f_m; - double o1 = o.countLeft - f_m; - double o2 = o.countRight - f_m; - - return (t1 * t1) + (t2 * t2) + (o1 * o1) + (o2 * o2); - } - - public double error() - { - double f_m = (this.countLeft + this.countRight) / 2.0d; - double t1 = this.countLeft - f_m; - double t2 = this.countRight - f_m; - return (t1 * t1) + (t2 * t2); - - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - double mark = (leftBoundary + rightBoundary) / 2.0D; - double width = rightBoundary - leftBoundary; - return "Bucket [(" - + leftBoundary + " TO " + mark + " = " + countLeft / incrementalStats.getN() / width + ") : (" + mark + " TO " + rightBoundary + " = " + countRight - / incrementalStats.getN() / width + "]"; - } - - } - + } + + return map; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "SimpleStats [" + + "\n getN()=" + getN() + ",\n getMin()=" + getMin() + ",\n getMax()=" + getMax() + ",\n getMean()=" + + getMean() + ",\n getVarience()=" + getVarience() + ",\n getStandardDeviation()=" + getStandardDeviation() + ",\n copies=" + + copies + ",\n]"; + } + + } + + public static class Bucket + { + IncrementalStats incrementalStats; + + public double leftBoundary; + + public double rightBoundary; + + public double countLeft; + + public double countRight; + + Bucket(IncrementalStats incrementalStats, double leftBoundary, double rightBoundary) + { + this(incrementalStats, leftBoundary, rightBoundary, 0D, 0D); + } + + Bucket(IncrementalStats incrementalStats, double leftBoundary, double rightBoundary, double countLeft, double countRight) + { + this.incrementalStats = incrementalStats; + this.leftBoundary = leftBoundary; + this.rightBoundary = rightBoundary; + this.countLeft = countLeft; + this.countRight = countRight; + } + + public void add(double x) + { + if ((x - leftBoundary) < (rightBoundary - x)) + { + countLeft++; + } + else + { + countRight++; + } + } + + public double mergeError(Bucket o) + { + double f_m = (this.countLeft + this.countRight + o.countLeft + o.countRight) / 4.0d; + double t1 = this.countLeft - f_m; + double t2 = this.countRight - f_m; + double o1 = o.countLeft - f_m; + double o2 = o.countRight - f_m; + + return (t1 * t1) + (t2 * t2) + (o1 * o1) + (o2 * o2); + } + + public double error() + { + double f_m = (this.countLeft + this.countRight) / 2.0d; + double t1 = this.countLeft - f_m; + double t2 = this.countRight - f_m; + return (t1 * t1) + (t2 * t2); + + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + double mark = (leftBoundary + rightBoundary) / 2.0D; + double width = rightBoundary - leftBoundary; + return "Bucket [(" + + leftBoundary + " TO " + mark + " = " + countLeft / incrementalStats.getN() / width + ") : (" + mark + " TO " + rightBoundary + " = " + countRight + / incrementalStats.getN() / width + "]"; + } + + } + @NotThreadSafe - public static class IncrementalStats - { - Date start = new Date(); - - int scale; - - int buckets; - - double[] moments = new double[5]; - - double min = 0D; - - double max = 0D; - - List values; - - List hist; - - InformationServerCollectionProvider server; - - public IncrementalStats(int scale, int buckets, InformationServerCollectionProvider infoSrv) - { - this.scale = scale; - this.buckets = buckets; - values = new ArrayList(buckets); - hist = new ArrayList(buckets + 1); - this.server = infoSrv; - } - - /** - * @return ISimpleOrderedMap - */ - public ISimpleOrderedMap getNamedList(boolean includeHist, boolean includeValues) - { - ISimpleOrderedMap map = this.server.getSimpleOrderedMapInstance(); - map.add("Start", start); - map.add("N", getN()); - map.add("Min", getMin()); - map.add("Max", getMax()); - map.add("Mean", getMean()); - map.add("Varience", getVarience()); - map.add("StdDev", getStandardDeviation()); - map.add("Skew", getSkew()); - map.add("Kurtosis", getKurtosis()); - - if (includeHist) - { - int i = 0; - ISimpleOrderedMap buckets = this.server.getSimpleOrderedMapInstance(); - for (Bucket b : hist) - { - double mark = (b.leftBoundary + b.rightBoundary) / 2.0D; - double width = b.rightBoundary - b.leftBoundary; - - // SimpleOrderedMap bucket = new SimpleOrderedMap(); - // bucket.add("Lower", b.leftBoundary); - // bucket.add("Upper", mark); - // bucket.add("ProbabilityDensity", b.countLeft/b.incrementalStats.getN()/width); - buckets.add("" + i++, (b.leftBoundary + mark) / 2.0D + "," + b.countLeft / b.incrementalStats.getN() / width); - - // bucket = new SimpleOrderedMap(); - // bucket.add("Lower", mark); - // bucket.add("Upper", b.rightBoundary); - // bucket.add("ProbabilityDensity", b.countRight/b.incrementalStats.getN()/width); - buckets.add("" + i++, (mark + b.rightBoundary) / 2.0D + "," + b.countRight / b.incrementalStats.getN() / width); - } - map.add("Buckets", buckets); - } - - if (includeValues) - { - int i = 0; - ISimpleOrderedMap valuesMap = this.server.getSimpleOrderedMapInstance(); - for (Double value : values) - { - valuesMap.add("" + i++, value); - } - map.add("Values", valuesMap); - } - - return map; - } - - public synchronized void reset() - { - moments = new double[5]; - - min = 0D; - - max = 0D; - - values = new ArrayList(buckets); - - hist = new ArrayList(buckets + 1); - - start = new Date(); - } - - public synchronized void add(double xUnscaled) - { - double x = xUnscaled / scale; - if ((moments[0] == 0) || (x > max)) - { - max = x; - } - if ((moments[0] == 0L) || (x < min)) - { - min = x; - } - double n = moments[0]; - double nPlus1 = n + 1; - double n2 = n * n; - double d = (moments[1] - x) / nPlus1; - double d2 = d * d; - double d3 = d2 * d; - double n_nPlus1 = n / nPlus1; - - moments[4] += 4 * d * moments[3] + 6 * d2 * moments[2] + (1 + n * n2) * d2 * d2; - moments[4] *= n_nPlus1; - - moments[3] += 3 * d * moments[2] + (1 - n2) * d3; - moments[3] *= n_nPlus1; - - moments[2] += (1 + n) * d2; - moments[2] *= n_nPlus1; - - moments[1] -= d; - - moments[0] = nPlus1; - - if (buckets > 1) - { - if (moments[0] < buckets) - { - values.add(Double.valueOf(x)); - } - else if (moments[0] == buckets) - { - values.add(Double.valueOf(x)); - buildInitialBuckets(); - } - else - { - values.set((int) moments[0] % buckets, x); - if (x < hist.get(0).leftBoundary) - { - double delta = (hist.get(0).leftBoundary - x) / 3.0; - Bucket b = new Bucket(this, x - delta, hist.get(0).leftBoundary); - hist.add(0, b); - b.add(x); - Pair bestToMerge = findBestToMerge(); - if (hist.size() > buckets) - { - merge(bestToMerge.getFirst()); - } - } - else if (x >= hist.get(hist.size() - 1).rightBoundary) - { - double delta = (x - hist.get(hist.size() - 1).rightBoundary) / 3.0; - Bucket b = new Bucket(this, hist.get(hist.size() - 1).rightBoundary, x + delta); - hist.add(b); - b.add(x); - Pair bestToMerge = findBestToMerge(); - if (hist.size() > buckets) - { - merge(bestToMerge.getFirst()); - } - } - else - { - // find existing - for (Bucket b : hist) - { - if ((b.leftBoundary <= x) && (x < b.rightBoundary)) - { - b.add(x); - break; - } - - } - Pair bestToMerge = findBestToMerge(); - Pair bestToSplit = findBestToSplit(); - - if (bestToMerge.getSecond() - bestToSplit.getSecond() < 0) - { - if(hist.size() < buckets) - { - split(bestToSplit.getFirst()); - } - else - { - merge(bestToMerge.getFirst()); - split(bestToMerge.getFirst() < bestToSplit.getFirst() ? bestToSplit.getFirst() - 1 : bestToSplit.getFirst()); - } - } - } - } - } - } - - /** - * - */ - private void buildInitialBuckets() - { - // generate initial bucket list - Collections.sort(values); - FOUND: for (int i = 0; i < values.size(); i++) - { - for (Bucket b : hist) - { - if ((b.leftBoundary <= values.get(i)) && (values.get(i) < b.rightBoundary)) - { - b.add(values.get(i)); - continue FOUND; - } - - } - if (i < values.size() - 1) - { - double start = values.get(i); - double end = start + 1.0D; - - END: for (int j = i + 1; j < values.size(); j++) - { - if (values.get(j) > start) - { - end = values.get(j); - break END; - } - } - - Bucket b = new Bucket(this, start, end); - b.add(values.get(i)); - hist.add(b); - } - else - { - double first = values.get(0); - double last = values.get(values.size() - 1); - double width = 1.0D; - if (values.size() > 1) - { - width = (last - first) / (values.size() - 1); - } - Bucket b = new Bucket(this, last, last + width); - b.add(values.get(i)); - hist.add(b); - } - } - } - - void merge(int position) - { - Bucket lower = hist.get(position); - Bucket upper = hist.get(position + 1); - Bucket merged = new Bucket(this, lower.leftBoundary, upper.rightBoundary, lower.countLeft + lower.countRight, upper.countLeft + upper.countRight); - hist.remove(position); - hist.set(position, merged); - - } - - void split(int position) - { - Bucket toSplit = hist.get(position); - double mark = (toSplit.leftBoundary + toSplit.rightBoundary) / 2.0D; - Bucket lower = new Bucket(this, toSplit.leftBoundary, mark, toSplit.countLeft / 2.0D, toSplit.countLeft / 2.0D); - Bucket upper = new Bucket(this, mark, toSplit.rightBoundary, toSplit.countRight / 2.0D, toSplit.countRight / 2.0D); - hist.set(position, upper); - hist.add(position, lower); - } - - Pair findBestToMerge() - { - double minMergeError = Double.MAX_VALUE; - int bucket = 0; - for (int i = 0; i < hist.size() - 1; i++) - { - double mergeError = hist.get(i).mergeError(hist.get(i + 1)); - if (mergeError < minMergeError) - { - minMergeError = mergeError; - bucket = i; - } - } - return new Pair(bucket, minMergeError); - } - - Pair findBestToSplit() - { - double maxError = Double.MIN_VALUE; - int bucket = 0; - for (int i = 0; i < hist.size(); i++) - { - double error = hist.get(i).error(); - if (error > maxError) - { - maxError = error; - bucket = i; - } - } - return new Pair(bucket, maxError); - } - - public synchronized long getN() - { - return (long) moments[0]; - } - - public synchronized double getMin() - { - return min; - } - - public synchronized double getMax() - { - return max; - } - - public synchronized double getMean() - { - return moments[1]; - } - - public synchronized double getVarience() - { - if (moments[0] > 1) - { - return moments[2] * moments[0] / (moments[0] - 1); - } - else - { - return Double.NaN; - } - } - - public synchronized double getStandardDeviation() - { - return Math.sqrt(getVarience()); - } - - public synchronized double getSkew() - { - if (moments[0] > 2) - { - double v = getVarience(); - return moments[3] * moments[0] * moments[0] / (Math.sqrt(v) * v * (moments[0] - 1) * (moments[0] - 2)); - } - else - { - return Double.NaN; - } - } - - public synchronized double getKurtosis() - { - if (moments[0] > 3) - { - double div = (moments[0] - 2) * (moments[0] - 3); - double nMinus1 = moments[0] - 1; - double v = getVarience(); - double z = ((moments[4] * moments[0] * moments[0] * (moments[0] + 1)) / (v * v * nMinus1)); - z -= 3 * nMinus1 * nMinus1; - z /= div; - return z; - } - else - { - return Double.NaN; - } - } - - public synchronized List getHistogram() - { - if(hist.size() == 0) - { - buildInitialBuckets(); - } - return hist; - } - - - - synchronized IncrementalStats copy() - { - IncrementalStats copy = new IncrementalStats(this.scale, this.buckets, this.server); - copy.start = this.start; - copy.max = this.getMax(); - copy.min = this.min; - copy.moments[0] = this.moments[0]; - copy.moments[1] = this.moments[1]; - copy.moments[2] = this.moments[2]; - copy.moments[3] = this.moments[3]; - copy.moments[4] = this.moments[4]; - for (Double x : this.values) - { - copy.values.add(x); - } - for (Bucket b : this.hist) - { - copy.hist.add(new Bucket(copy, b.leftBoundary, b.rightBoundary, b.countLeft, b.countRight)); - } - return copy; - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - return "IncrementalStats [getN()=" - + getN() + ", getMin()=" + getMin() + ", getMax()=" + getMax() + ", getMean()=" + getMean() + ", getVarience()=" + getVarience() + ", getStandardDeviation()=" - + getStandardDeviation() + ", getSkew()=" + getSkew() + ", getKurtosis()=" + getKurtosis() + ", values=" + values + ", hist=" + hist + "]"; - } - - } - - /** - * @param time long - */ - public void addModelTime(long time) - { - IncrementalStats stats = modelTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - modelTimes.put(Thread.currentThread().getName(), stats); - } - stats.add(time); - } - - /** - * @param time long - */ - public void addAclTime(long time) - { - IncrementalStats stats = aclTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - aclTimes.put(Thread.currentThread().getName(), stats); - } - stats.add(time); - } - - /** - * @param time long - */ - public void addNodeTime(long time) - { - IncrementalStats stats = nodeTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - nodeTimes.put(Thread.currentThread().getName(), stats); - } - stats.add(time); - } - - /** - * @param docCount int - * @param time long - */ - public void addElapsedNodeTime(int docCount, long time) - { - if(docCount < 1) - { - return; - } - IncrementalStats stats = elapsedNodeTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - elapsedNodeTimes.put(Thread.currentThread().getName(), stats); - } - long meanTime = time / docCount; - for(int i = 0; i < docCount; i++) - { - stats.add(meanTime); - } - - } - - /** - * @param docCount int - * @param time long - */ - public void addElapsedAclTime(int docCount, long time) - { - if(docCount < 1) - { - return; - } - IncrementalStats stats = elapsedAclTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - elapsedAclTimes.put(Thread.currentThread().getName(), stats); - } - long meanTime = time / docCount; - for(int i = 0; i < docCount; i++) - { - stats.add(meanTime); - } - - } - - /** - * @param docCount int - * @param time long - */ - public void addElapsedContentTime(int docCount, long time) - { - if(docCount < 1) - { - return; - } - IncrementalStats stats = elapsedContentTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - elapsedContentTimes.put(Thread.currentThread().getName(), stats); - } - long meanTime = time / docCount; - for(int i = 0; i < docCount; i++) - { - stats.add(meanTime); - } - - } - - /** - * @param size int - */ - public void addTxDocs(int size) - { - IncrementalStats stats = txDocs.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(1, 50, this.infoSrv); - txDocs.put(Thread.currentThread().getName(), stats); - } - stats.add(size); - } - - /** - * @param size int - */ - public void addChangeSetAcls(int size) - { - IncrementalStats stats = changeSetAcls.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(1, 50, this.infoSrv); - changeSetAcls.put(Thread.currentThread().getName(), stats); - } - stats.add(size); - } - - /** - * @param time long - */ - public void addDocTransformationTime(long time) - { - IncrementalStats stats = docTransformationTimes.get(Thread.currentThread().getName()); - if (stats == null) - { - stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); - docTransformationTimes.put(Thread.currentThread().getName(), stats); - } - stats.add(time); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - return "TrackerStats [modelTimes=" - + modelTimes + ", aclTimes=" + aclTimes + ", changeSetAcls=" + changeSetAcls + ", txDocs=" + txDocs + ", docTransformationTimes=" + docTransformationTimes - + ", nodeTimes=" + nodeTimes + "]"; - } - - /** - * - */ - public void reset() - { - modelTimes.clear(); - aclTimes.clear(); - changeSetAcls.clear(); - txDocs.clear(); - docTransformationTimes.clear(); - nodeTimes.clear(); - } - - -} + public static class IncrementalStats + { + Date start = new Date(); + + int scale; + + int buckets; + + double[] moments = new double[5]; + + double min = 0D; + + double max = 0D; + + List values; + + List hist; + + InformationServerCollectionProvider server; + + public IncrementalStats(int scale, int buckets, InformationServerCollectionProvider infoSrv) + { + this.scale = scale; + this.buckets = buckets; + values = new ArrayList(buckets); + hist = new ArrayList(buckets + 1); + this.server = infoSrv; + } + + /** + * @return ISimpleOrderedMap + */ + public ISimpleOrderedMap getNamedList(boolean includeHist, boolean includeValues) + { + ISimpleOrderedMap map = this.server.getSimpleOrderedMapInstance(); + map.add("Start", start); + map.add("N", getN()); + map.add("Min", getMin()); + map.add("Max", getMax()); + map.add("Mean", getMean()); + map.add("Varience", getVarience()); + map.add("StdDev", getStandardDeviation()); + map.add("Skew", getSkew()); + map.add("Kurtosis", getKurtosis()); + + if (includeHist) + { + int i = 0; + ISimpleOrderedMap buckets = this.server.getSimpleOrderedMapInstance(); + for (Bucket b : hist) + { + double mark = (b.leftBoundary + b.rightBoundary) / 2.0D; + double width = b.rightBoundary - b.leftBoundary; + + // SimpleOrderedMap bucket = new SimpleOrderedMap(); + // bucket.add("Lower", b.leftBoundary); + // bucket.add("Upper", mark); + // bucket.add("ProbabilityDensity", b.countLeft/b.incrementalStats.getN()/width); + buckets.add("" + i++, (b.leftBoundary + mark) / 2.0D + "," + b.countLeft / b.incrementalStats.getN() / width); + + // bucket = new SimpleOrderedMap(); + // bucket.add("Lower", mark); + // bucket.add("Upper", b.rightBoundary); + // bucket.add("ProbabilityDensity", b.countRight/b.incrementalStats.getN()/width); + buckets.add("" + i++, (mark + b.rightBoundary) / 2.0D + "," + b.countRight / b.incrementalStats.getN() / width); + } + map.add("Buckets", buckets); + } + + if (includeValues) + { + int i = 0; + ISimpleOrderedMap valuesMap = this.server.getSimpleOrderedMapInstance(); + for (Double value : values) + { + valuesMap.add("" + i++, value); + } + map.add("Values", valuesMap); + } + + return map; + } + + public synchronized void reset() + { + moments = new double[5]; + + min = 0D; + + max = 0D; + + values = new ArrayList(buckets); + + hist = new ArrayList(buckets + 1); + + start = new Date(); + } + + public synchronized void add(double xUnscaled) + { + double x = xUnscaled / scale; + if ((moments[0] == 0) || (x > max)) + { + max = x; + } + if ((moments[0] == 0L) || (x < min)) + { + min = x; + } + double n = moments[0]; + double nPlus1 = n + 1; + double n2 = n * n; + double d = (moments[1] - x) / nPlus1; + double d2 = d * d; + double d3 = d2 * d; + double n_nPlus1 = n / nPlus1; + + moments[4] += 4 * d * moments[3] + 6 * d2 * moments[2] + (1 + n * n2) * d2 * d2; + moments[4] *= n_nPlus1; + + moments[3] += 3 * d * moments[2] + (1 - n2) * d3; + moments[3] *= n_nPlus1; + + moments[2] += (1 + n) * d2; + moments[2] *= n_nPlus1; + + moments[1] -= d; + + moments[0] = nPlus1; + + if (buckets > 1) + { + if (moments[0] < buckets) + { + values.add(Double.valueOf(x)); + } + else if (moments[0] == buckets) + { + values.add(Double.valueOf(x)); + buildInitialBuckets(); + } + else + { + values.set((int) moments[0] % buckets, x); + if (x < hist.get(0).leftBoundary) + { + double delta = (hist.get(0).leftBoundary - x) / 3.0; + Bucket b = new Bucket(this, x - delta, hist.get(0).leftBoundary); + hist.add(0, b); + b.add(x); + Pair bestToMerge = findBestToMerge(); + if (hist.size() > buckets) + { + merge(bestToMerge.getFirst()); + } + } + else if (x >= hist.get(hist.size() - 1).rightBoundary) + { + double delta = (x - hist.get(hist.size() - 1).rightBoundary) / 3.0; + Bucket b = new Bucket(this, hist.get(hist.size() - 1).rightBoundary, x + delta); + hist.add(b); + b.add(x); + Pair bestToMerge = findBestToMerge(); + if (hist.size() > buckets) + { + merge(bestToMerge.getFirst()); + } + } + else + { + // find existing + for (Bucket b : hist) + { + if ((b.leftBoundary <= x) && (x < b.rightBoundary)) + { + b.add(x); + break; + } + + } + Pair bestToMerge = findBestToMerge(); + Pair bestToSplit = findBestToSplit(); + + if (bestToMerge.getSecond() - bestToSplit.getSecond() < 0) + { + if(hist.size() < buckets) + { + split(bestToSplit.getFirst()); + } + else + { + merge(bestToMerge.getFirst()); + split(bestToMerge.getFirst() < bestToSplit.getFirst() ? bestToSplit.getFirst() - 1 : bestToSplit.getFirst()); + } + } + } + } + } + } + + /** + * + */ + private void buildInitialBuckets() + { + // generate initial bucket list + Collections.sort(values); + FOUND: for (int i = 0; i < values.size(); i++) + { + for (Bucket b : hist) + { + if ((b.leftBoundary <= values.get(i)) && (values.get(i) < b.rightBoundary)) + { + b.add(values.get(i)); + continue FOUND; + } + + } + if (i < values.size() - 1) + { + double start = values.get(i); + double end = start + 1.0D; + + END: for (int j = i + 1; j < values.size(); j++) + { + if (values.get(j) > start) + { + end = values.get(j); + break END; + } + } + + Bucket b = new Bucket(this, start, end); + b.add(values.get(i)); + hist.add(b); + } + else + { + double first = values.get(0); + double last = values.get(values.size() - 1); + double width = 1.0D; + if (values.size() > 1) + { + width = (last - first) / (values.size() - 1); + } + Bucket b = new Bucket(this, last, last + width); + b.add(values.get(i)); + hist.add(b); + } + } + } + + void merge(int position) + { + Bucket lower = hist.get(position); + Bucket upper = hist.get(position + 1); + Bucket merged = new Bucket(this, lower.leftBoundary, upper.rightBoundary, lower.countLeft + lower.countRight, upper.countLeft + upper.countRight); + hist.remove(position); + hist.set(position, merged); + + } + + void split(int position) + { + Bucket toSplit = hist.get(position); + double mark = (toSplit.leftBoundary + toSplit.rightBoundary) / 2.0D; + Bucket lower = new Bucket(this, toSplit.leftBoundary, mark, toSplit.countLeft / 2.0D, toSplit.countLeft / 2.0D); + Bucket upper = new Bucket(this, mark, toSplit.rightBoundary, toSplit.countRight / 2.0D, toSplit.countRight / 2.0D); + hist.set(position, upper); + hist.add(position, lower); + } + + Pair findBestToMerge() + { + double minMergeError = Double.MAX_VALUE; + int bucket = 0; + for (int i = 0; i < hist.size() - 1; i++) + { + double mergeError = hist.get(i).mergeError(hist.get(i + 1)); + if (mergeError < minMergeError) + { + minMergeError = mergeError; + bucket = i; + } + } + return new Pair(bucket, minMergeError); + } + + Pair findBestToSplit() + { + double maxError = Double.MIN_VALUE; + int bucket = 0; + for (int i = 0; i < hist.size(); i++) + { + double error = hist.get(i).error(); + if (error > maxError) + { + maxError = error; + bucket = i; + } + } + return new Pair(bucket, maxError); + } + + public synchronized long getN() + { + return (long) moments[0]; + } + + public synchronized double getMin() + { + return min; + } + + public synchronized double getMax() + { + return max; + } + + public synchronized double getMean() + { + return moments[1]; + } + + public synchronized double getVarience() + { + if (moments[0] > 1) + { + return moments[2] * moments[0] / (moments[0] - 1); + } + else + { + return Double.NaN; + } + } + + public synchronized double getStandardDeviation() + { + return Math.sqrt(getVarience()); + } + + public synchronized double getSkew() + { + if (moments[0] > 2) + { + double v = getVarience(); + return moments[3] * moments[0] * moments[0] / (Math.sqrt(v) * v * (moments[0] - 1) * (moments[0] - 2)); + } + else + { + return Double.NaN; + } + } + + public synchronized double getKurtosis() + { + if (moments[0] > 3) + { + double div = (moments[0] - 2) * (moments[0] - 3); + double nMinus1 = moments[0] - 1; + double v = getVarience(); + double z = ((moments[4] * moments[0] * moments[0] * (moments[0] + 1)) / (v * v * nMinus1)); + z -= 3 * nMinus1 * nMinus1; + z /= div; + return z; + } + else + { + return Double.NaN; + } + } + + public synchronized List getHistogram() + { + if(hist.size() == 0) + { + buildInitialBuckets(); + } + return hist; + } + + + + synchronized IncrementalStats copy() + { + IncrementalStats copy = new IncrementalStats(this.scale, this.buckets, this.server); + copy.start = this.start; + copy.max = this.getMax(); + copy.min = this.min; + copy.moments[0] = this.moments[0]; + copy.moments[1] = this.moments[1]; + copy.moments[2] = this.moments[2]; + copy.moments[3] = this.moments[3]; + copy.moments[4] = this.moments[4]; + for (Double x : this.values) + { + copy.values.add(x); + } + for (Bucket b : this.hist) + { + copy.hist.add(new Bucket(copy, b.leftBoundary, b.rightBoundary, b.countLeft, b.countRight)); + } + return copy; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "IncrementalStats [getN()=" + + getN() + ", getMin()=" + getMin() + ", getMax()=" + getMax() + ", getMean()=" + getMean() + ", getVarience()=" + getVarience() + ", getStandardDeviation()=" + + getStandardDeviation() + ", getSkew()=" + getSkew() + ", getKurtosis()=" + getKurtosis() + ", values=" + values + ", hist=" + hist + "]"; + } + + } + + /** + * @param time long + */ + public void addModelTime(long time) + { + IncrementalStats stats = modelTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + modelTimes.put(Thread.currentThread().getName(), stats); + } + stats.add(time); + } + + /** + * @param time long + */ + public void addAclTime(long time) + { + IncrementalStats stats = aclTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + aclTimes.put(Thread.currentThread().getName(), stats); + } + stats.add(time); + } + + /** + * @param time long + */ + public void addNodeTime(long time) + { + IncrementalStats stats = nodeTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + nodeTimes.put(Thread.currentThread().getName(), stats); + } + stats.add(time); + } + + /** + * @param docCount int + * @param time long + */ + public void addElapsedNodeTime(int docCount, long time) + { + if(docCount < 1) + { + return; + } + IncrementalStats stats = elapsedNodeTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + elapsedNodeTimes.put(Thread.currentThread().getName(), stats); + } + long meanTime = time / docCount; + for(int i = 0; i < docCount; i++) + { + stats.add(meanTime); + } + + } + + /** + * @param docCount int + * @param time long + */ + public void addElapsedAclTime(int docCount, long time) + { + if(docCount < 1) + { + return; + } + IncrementalStats stats = elapsedAclTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + elapsedAclTimes.put(Thread.currentThread().getName(), stats); + } + long meanTime = time / docCount; + for(int i = 0; i < docCount; i++) + { + stats.add(meanTime); + } + + } + + /** + * @param docCount int + * @param time long + */ + public void addElapsedContentTime(int docCount, long time) + { + if(docCount < 1) + { + return; + } + IncrementalStats stats = elapsedContentTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + elapsedContentTimes.put(Thread.currentThread().getName(), stats); + } + long meanTime = time / docCount; + for(int i = 0; i < docCount; i++) + { + stats.add(meanTime); + } + + } + + /** + * @param size int + */ + public void addTxDocs(int size) + { + IncrementalStats stats = txDocs.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(1, 50, this.infoSrv); + txDocs.put(Thread.currentThread().getName(), stats); + } + stats.add(size); + } + + /** + * @param size int + */ + public void addChangeSetAcls(int size) + { + IncrementalStats stats = changeSetAcls.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(1, 50, this.infoSrv); + changeSetAcls.put(Thread.currentThread().getName(), stats); + } + stats.add(size); + } + + /** + * @param time long + */ + public void addDocTransformationTime(long time) + { + IncrementalStats stats = docTransformationTimes.get(Thread.currentThread().getName()); + if (stats == null) + { + stats = new IncrementalStats(TIME_SCALE, 50, this.infoSrv); + docTransformationTimes.put(Thread.currentThread().getName(), stats); + } + stats.add(time); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "TrackerStats [modelTimes=" + + modelTimes + ", aclTimes=" + aclTimes + ", changeSetAcls=" + changeSetAcls + ", txDocs=" + txDocs + ", docTransformationTimes=" + docTransformationTimes + + ", nodeTimes=" + nodeTimes + "]"; + } + + /** + * + */ + public void reset() + { + modelTimes.clear(); + aclTimes.clear(); + changeSetAcls.clear(); + txDocs.clear(); + docTransformationTimes.clear(); + nodeTimes.clear(); + } + + +} diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/BoundedDequeTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/BoundedDequeTest.java index e7ca86c38..95e300c1a 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/BoundedDequeTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/BoundedDequeTest.java @@ -1,57 +1,57 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr; - -import static java.util.Arrays.asList; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.concurrent.LinkedBlockingDeque; - -import org.junit.Test; - -/** Unit tests for the {@link BoundedDeque}. */ -public class BoundedDequeTest -{ - /** Check that earlier entries are removed from the BoundedDeque. */ - @Test - public void testBoundedness() - { - // Create a BoundedDeque with size two. - BoundedDeque boundedDeque = new BoundedDeque<>(2); - - // Add three things. - boundedDeque.add("A"); - boundedDeque.add("B"); - boundedDeque.add("C"); - - // Check that the latest two are still there. - LinkedBlockingDeque actual = boundedDeque.getDeque(); - assertEquals("Unexpected entries in BoundedDeque.", asList("C", "B"), new ArrayList(actual)); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr; + +import static java.util.Arrays.asList; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.concurrent.LinkedBlockingDeque; + +import org.junit.Test; + +/** Unit tests for the {@link BoundedDeque}. */ +public class BoundedDequeTest +{ + /** Check that earlier entries are removed from the BoundedDeque. */ + @Test + public void testBoundedness() + { + // Create a BoundedDeque with size two. + BoundedDeque boundedDeque = new BoundedDeque<>(2); + + // Add three things. + boundedDeque.add("A"); + boundedDeque.add("B"); + boundedDeque.add("C"); + + // Check that the latest two are still there. + LinkedBlockingDeque actual = boundedDeque.getDeque(); + assertEquals("Unexpected entries in BoundedDeque.", asList("C", "B"), new ArrayList(actual)); + } +} diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/AclReadersTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/AclReadersTest.java index dcc8f015e..b14c2ce59 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/AclReadersTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/AclReadersTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -24,63 +24,63 @@ * #L% */ -package org.alfresco.solr.client; - -import static java.util.Arrays.asList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -/** - * Some simple sanity tests for the {@link AclReaders} class. - * - * @author Matt Ward - */ -public class AclReadersTest -{ - @Test - public void testHashCode() - { - // We only care about ID for equals() and hashCode() - - // The same ID - assertEquals(new AclReaders(123, null, null, 0, null).hashCode(), - new AclReaders(123, null, null, 0, null).hashCode()); - - // Different ID - assertNotEquals(new AclReaders(123, null, null, 0, null).hashCode(), - new AclReaders(124, null, null, 0, null).hashCode()); - } - - @Test - public void testEqualsObject() - { - // The very same - final AclReaders aclReaders = new AclReaders(0, null, null, 0, null); - assertTrue(aclReaders.equals(aclReaders)); - - // The same ID - assertEquals(new AclReaders(123, null, null, 0, null), - new AclReaders(123, null, null, 0, null)); - - // Different ID - assertNotEquals(new AclReaders(123, null, null, 0, null), - new AclReaders(124, null, null, 0, null)); - } - - @Test - public void testGetReaders() - { - AclReaders aclReaders = new AclReaders(0, null, asList("d1", "d2"), 0, null); - assertEquals(asList("d1", "d2"), aclReaders.getDenied()); - } - - @Test - public void testGetDenied() - { - AclReaders aclReaders = new AclReaders(0, asList("r1", "r2", "r3"), null, 0, null); - assertEquals(asList("r1", "r2", "r3"), aclReaders.getReaders()); - } -} +package org.alfresco.solr.client; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Some simple sanity tests for the {@link AclReaders} class. + * + * @author Matt Ward + */ +public class AclReadersTest +{ + @Test + public void testHashCode() + { + // We only care about ID for equals() and hashCode() + + // The same ID + assertEquals(new AclReaders(123, null, null, 0, null).hashCode(), + new AclReaders(123, null, null, 0, null).hashCode()); + + // Different ID + assertNotEquals(new AclReaders(123, null, null, 0, null).hashCode(), + new AclReaders(124, null, null, 0, null).hashCode()); + } + + @Test + public void testEqualsObject() + { + // The very same + final AclReaders aclReaders = new AclReaders(0, null, null, 0, null); + assertTrue(aclReaders.equals(aclReaders)); + + // The same ID + assertEquals(new AclReaders(123, null, null, 0, null), + new AclReaders(123, null, null, 0, null)); + + // Different ID + assertNotEquals(new AclReaders(123, null, null, 0, null), + new AclReaders(124, null, null, 0, null)); + } + + @Test + public void testGetReaders() + { + AclReaders aclReaders = new AclReaders(0, null, asList("d1", "d2"), 0, null); + assertEquals(asList("d1", "d2"), aclReaders.getDenied()); + } + + @Test + public void testGetDenied() + { + AclReaders aclReaders = new AclReaders(0, asList("r1", "r2", "r3"), null, 0, null); + assertEquals(asList("r1", "r2", "r3"), aclReaders.getReaders()); + } +} diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/LookAheadBufferedReaderTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/LookAheadBufferedReaderTest.java index 4e72e82f9..93e6bb65e 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/LookAheadBufferedReaderTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/LookAheadBufferedReaderTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -208,4 +208,4 @@ public class LookAheadBufferedReaderTest throw new RuntimeException(exception); } } -} +} \ No newline at end of file diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientFactoryTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientFactoryTest.java index e6d0b3b23..fccc36ed6 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientFactoryTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientFactoryTest.java @@ -1,101 +1,101 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -import static org.mockito.Mockito.*; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; - -import java.util.Properties; - -import org.alfresco.encryption.KeyResourceLoader; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.solr.client.SOLRAPIClient; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class SOLRAPIClientFactoryTest -{ - @Mock - private Properties props; - @Mock - private KeyResourceLoader keyResourceLoader; - @Mock - private DictionaryService dictionaryService; - @Mock - private NamespaceDAO namespaceDAO; - private SOLRAPIClientFactory factory; - - @Before - public void setUp() throws Exception - { - when(props.getProperty(eq("alfresco.host"), anyString())).thenReturn("localhost"); - when(props.getProperty(eq("alfresco.port"), anyString())).thenReturn("8080"); - when(props.getProperty(eq("alfresco.port.ssl"), anyString())).thenReturn("8443"); - when(props.getProperty(eq("alfresco.maxTotalConnections"), anyString())).thenReturn("40"); - when(props.getProperty(eq("alfresco.maxHostConnections"), anyString())).thenReturn("40"); - when(props.getProperty(eq("alfresco.socketTimeout"), anyString())).thenReturn("0"); - when(props.getProperty(eq("alfresco.secureComms"), anyString())).thenReturn("none"); - - this.factory = new SOLRAPIClientFactory(); - } - - @Test - public void getsSameSOLRAPIClientForSameAlfresco() - { - SOLRAPIClient solrapiClient = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, - namespaceDAO); - assertNotNull(solrapiClient); - - SOLRAPIClient solrapiClient2 = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, - namespaceDAO); - assertNotNull(solrapiClient2); - - assertSame(solrapiClient, solrapiClient2); - } - - @Test - public void getsDifferentSOLRAPIClientForDifferentAlfresco() - { - SOLRAPIClient solrapiClient = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, - namespaceDAO); - assertNotNull(solrapiClient); - - when(props.getProperty("alfresco.port.ssl", "8443")).thenReturn("8444"); - SOLRAPIClient solrapiClient2 = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, - namespaceDAO); - assertNotNull(solrapiClient2); - - assertNotSame(solrapiClient, solrapiClient2); - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +import static org.mockito.Mockito.*; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +import java.util.Properties; + +import org.alfresco.encryption.KeyResourceLoader; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.solr.client.SOLRAPIClient; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SOLRAPIClientFactoryTest +{ + @Mock + private Properties props; + @Mock + private KeyResourceLoader keyResourceLoader; + @Mock + private DictionaryService dictionaryService; + @Mock + private NamespaceDAO namespaceDAO; + private SOLRAPIClientFactory factory; + + @Before + public void setUp() throws Exception + { + when(props.getProperty(eq("alfresco.host"), anyString())).thenReturn("localhost"); + when(props.getProperty(eq("alfresco.port"), anyString())).thenReturn("8080"); + when(props.getProperty(eq("alfresco.port.ssl"), anyString())).thenReturn("8443"); + when(props.getProperty(eq("alfresco.maxTotalConnections"), anyString())).thenReturn("40"); + when(props.getProperty(eq("alfresco.maxHostConnections"), anyString())).thenReturn("40"); + when(props.getProperty(eq("alfresco.socketTimeout"), anyString())).thenReturn("0"); + when(props.getProperty(eq("alfresco.secureComms"), anyString())).thenReturn("none"); + + this.factory = new SOLRAPIClientFactory(); + } + + @Test + public void getsSameSOLRAPIClientForSameAlfresco() + { + SOLRAPIClient solrapiClient = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, + namespaceDAO); + assertNotNull(solrapiClient); + + SOLRAPIClient solrapiClient2 = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, + namespaceDAO); + assertNotNull(solrapiClient2); + + assertSame(solrapiClient, solrapiClient2); + } + + @Test + public void getsDifferentSOLRAPIClientForDifferentAlfresco() + { + SOLRAPIClient solrapiClient = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, + namespaceDAO); + assertNotNull(solrapiClient); + + when(props.getProperty("alfresco.port.ssl", "8443")).thenReturn("8444"); + SOLRAPIClient solrapiClient2 = factory.getSOLRAPIClient(props, keyResourceLoader, dictionaryService, + namespaceDAO); + assertNotNull(solrapiClient2); + + assertNotSame(solrapiClient, solrapiClient2); + } +} diff --git a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientTest.java b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientTest.java index 4e885c3d5..0f4aa1e40 100644 --- a/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientTest.java +++ b/search-services/alfresco-solrclient-lib/src/test/java/org/alfresco/solr/client/SOLRAPIClientTest.java @@ -1,684 +1,684 @@ -/* - * #%L - * Alfresco Search Services - * %% - * Copyright (C) 2005 - 2021 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.solr.client; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.security.AlgorithmParameters; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.alfresco.encryption.DefaultEncryptionUtils; -import org.alfresco.encryption.KeyProvider; -import org.alfresco.encryption.KeyResourceLoader; -import org.alfresco.encryption.KeyStoreParameters; -import org.alfresco.encryption.MACUtils.MACInput; -import org.alfresco.encryption.ssl.SSLEncryptionParameters; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.httpclient.AlfrescoHttpClient; -import org.alfresco.httpclient.AuthenticationException; -import org.alfresco.httpclient.HttpClientFactory; -import org.alfresco.httpclient.HttpClientFactory.SecureCommsType; -import org.alfresco.opencmis.dictionary.CMISDictionaryRegistry; -import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; -import org.alfresco.opencmis.mapping.CMISMapping; -import org.alfresco.opencmis.mapping.RuntimePropertyLuceneBuilderMapping; -import org.alfresco.repo.cache.MemoryCache; -import org.alfresco.repo.dictionary.CompiledModelsCache; -import org.alfresco.repo.dictionary.DictionaryComponent; -import org.alfresco.repo.dictionary.DictionaryDAOImpl; -import org.alfresco.repo.dictionary.DictionaryNamespaceComponent; -import org.alfresco.repo.dictionary.M2Model; -import org.alfresco.repo.dictionary.M2Namespace; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.repo.i18n.StaticMessageLookup; -import org.alfresco.repo.tenant.SingleTServiceImpl; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.DynamicallySizedThreadPoolExecutor; -import org.alfresco.util.Pair; -import org.alfresco.util.TraceableThreadFactory; -import org.alfresco.util.cache.DefaultAsynchronouslyRefreshedCacheRegistry; -import org.apache.chemistry.opencmis.commons.enums.CmisVersion; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.json.JSONException; - -import junit.framework.TestCase; - -/** - * Tests {@link SOLRAPIClient} Note: need to make sure that source/solr/instance is on the run classpath. Note: doesn't - * currently work, need to change to use SSL. - * - * @since 4.0 - */ -public class SOLRAPIClientTest extends TestCase -{ - private static Log logger = LogFactory.getLog(SOLRAPIClientTest.class); - - // private static final String TEST_MODEL = "org/alfresco/repo/dictionary/dictionarydaotest_model.xml"; - - private SOLRAPIClient client; - - private SOLRAPIClient invalidKeyClient; - - private SOLRAPIClient tamperWithClient; - - private DictionaryDAOImpl dictionaryDAO; - - private CMISStrictDictionaryService cmisDictionaryService; - - private static final String CORENAME = "collection1"; - - // private M2Model testModel; - - private void loadModel(Map modelMap, HashSet loadedModels, M2Model model) - { - String modelName = model.getName(); - if (loadedModels.contains(modelName) == false) - { - for (M2Namespace importNamespace : model.getImports()) - { - M2Model importedModel = modelMap.get(importNamespace.getUri()); - if (importedModel != null) - { - - // Ensure that the imported model is loaded first - loadModel(modelMap, loadedModels, importedModel); - } - } - - dictionaryDAO.putModelIgnoringConstraints(model); - loadedModels.add(modelName); - } - } - - @Override - public void setUp() throws Exception - { - if(client == null) - { - TenantService tenantService = new SingleTServiceImpl(); - - dictionaryDAO = new DictionaryDAOImpl(); - NamespaceDAO namespaceDAO = dictionaryDAO; - dictionaryDAO.setTenantService(tenantService); - - CompiledModelsCache compiledModelsCache = new CompiledModelsCache(); - compiledModelsCache.setDictionaryDAO(dictionaryDAO); - compiledModelsCache.setTenantService(tenantService); - compiledModelsCache.setRegistry(new DefaultAsynchronouslyRefreshedCacheRegistry()); - TraceableThreadFactory threadFactory = new TraceableThreadFactory(); - threadFactory.setThreadDaemon(true); - threadFactory.setThreadPriority(Thread.NORM_PRIORITY); - - ThreadPoolExecutor threadPoolExecutor = new DynamicallySizedThreadPoolExecutor(20, 20, 90, TimeUnit.SECONDS, new LinkedBlockingQueue(), threadFactory, - new ThreadPoolExecutor.CallerRunsPolicy()); - compiledModelsCache.setThreadPoolExecutor(threadPoolExecutor); - dictionaryDAO.setDictionaryRegistryCache(compiledModelsCache); - dictionaryDAO.setResourceClassLoader(getResourceClassLoader()); - dictionaryDAO.init(); - - DictionaryComponent dictionaryComponent = new DictionaryComponent(); - dictionaryComponent.setDictionaryDAO(dictionaryDAO); - dictionaryComponent.setMessageLookup(new StaticMessageLookup()); - - // cmis dictionary - CMISMapping cmisMapping = new CMISMapping(); - cmisMapping.setCmisVersion(CmisVersion.CMIS_1_0); - DictionaryNamespaceComponent namespaceService = new DictionaryNamespaceComponent(); - namespaceService.setNamespaceDAO(namespaceDAO); - cmisMapping.setNamespaceService(namespaceService); - cmisMapping.setDictionaryService(dictionaryComponent); - cmisMapping.afterPropertiesSet(); - - cmisDictionaryService = new CMISStrictDictionaryService(); - cmisDictionaryService.setCmisMapping(cmisMapping); - cmisDictionaryService.setDictionaryService(dictionaryComponent); - cmisDictionaryService.setDictionaryDAO(dictionaryDAO); - cmisDictionaryService.setSingletonCache(new MemoryCache()); - cmisDictionaryService.setTenantService(tenantService); - cmisDictionaryService.init(); - - RuntimePropertyLuceneBuilderMapping luceneBuilderMapping = new RuntimePropertyLuceneBuilderMapping(); - luceneBuilderMapping.setDictionaryService(dictionaryComponent); - luceneBuilderMapping.setCmisDictionaryService(cmisDictionaryService); - cmisDictionaryService.setPropertyLuceneBuilderMapping(luceneBuilderMapping); - - luceneBuilderMapping.afterPropertiesSet(); - - // Load the key store from the classpath - ClasspathKeyResourceLoader keyResourceLoader = new ClasspathKeyResourceLoader(); - client = new SOLRAPIClient(getRepoClient(keyResourceLoader), dictionaryComponent, dictionaryDAO); - trackModels(); - } - } - - private void trackModels() throws AuthenticationException, IOException, JSONException - { - - List modelDiffs = client.getModelsDiff(CORENAME,Collections. emptyList()); - HashMap modelMap = new HashMap(); - - for (AlfrescoModelDiff modelDiff : modelDiffs) - { - switch (modelDiff.getType()) - { - case CHANGED: - AlfrescoModel changedModel = client.getModel(CORENAME, modelDiff.getModelName()); - for (M2Namespace namespace : changedModel.getModel().getNamespaces()) - { - modelMap.put(namespace.getUri(), changedModel.getModel()); - } - break; - case NEW: - AlfrescoModel newModel = client.getModel(CORENAME, modelDiff.getModelName()); - for (M2Namespace namespace : newModel.getModel().getNamespaces()) - { - modelMap.put(namespace.getUri(), newModel.getModel()); - } - break; - case REMOVED: - // At the moment we do not unload models - I can see no side effects .... - // However search is used to check for references to indexed properties or types - // This will be partially broken anyway due to eventual consistency - // A model should only be unloaded if there are no data dependencies - // Should have been on the de-lucene list. - break; - } - } - - HashSet loadedModels = new HashSet(); - for (M2Model model : modelMap.values()) - { - loadModel(modelMap, loadedModels, model); - } - if (modelDiffs.size() > 0) - { - afterInitModels(); - } - - } - - public void afterInitModels() - { - cmisDictionaryService.afterDictionaryInit(); - } - - - protected AlfrescoHttpClient getRepoClient(ClasspathKeyResourceLoader keyResourceLoader) - { - // TODO i18n - KeyStoreParameters keyStoreParameters = new KeyStoreParameters("ssl-keystore", "SSL Key Store", "JCEKS", null, "ssl-keystore-passwords.properties", "ssl.repo.client.keystore"); - KeyStoreParameters trustStoreParameters = new KeyStoreParameters("ssl-truststore", "SSL Trust Store", "JCEKS", null, "ssl-truststore-passwords.properties", "ssl.repo.client.truststore"); - - SSLEncryptionParameters sslEncryptionParameters = new SSLEncryptionParameters(keyStoreParameters, trustStoreParameters); - - //MD5HttpClientFactory httpClientFactory = new MD5HttpClientFactory(); -// HttpClientFactory httpClientFactory = new HttpClientFactory(SecureComm); -// -// invalidKeyEncryptionService = new EncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); -// AlfrescoHttpClient repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, invalidKeyEncryptionService); -// //SecureHttpClient repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, invalidKeyEncryptionService); -// invalidKeyClient = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); -// -// keyStoreParameters.setLocation("org/alfresco/solr/client/.keystore"); -// tamperWithEncryptionService = new TamperWithEncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); -// repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, tamperWithEncryptionService); -//// repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, tamperWithEncryptionService); -// tamperWithClient = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); -// -// encryptionParameters.setMessageTimeout(30*1000); -// keyStoreParameters.setLocation("workspace-SpacesStore/conf/.keystore"); -// EncryptionService encryptionService = new EncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); -// repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, encryptionService); - //repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, encryptionService); -// client = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); - - HttpClientFactory httpClientFactory = new HttpClientFactory(SecureCommsType.getType("https"), sslEncryptionParameters, keyResourceLoader, null, null, "localhost", 8080, - 8443, 40, 40, 0); - // TODO need to make port configurable depending on secure comms, or just make redirects - // work - return httpClientFactory.getRepoClient("localhost", 8443); - } - - public ClassLoader getResourceClassLoader() - { - - File f = new File("woof", "alfrescoResources"); - if (f.canRead() && f.isDirectory()) - { - - URL[] urls = new URL[1]; - - try - { - URL url = f.toURI().normalize().toURL(); - urls[0] = url; - } - catch (MalformedURLException e) - { - throw new AlfrescoRuntimeException("Failed to add resources to classpath ", e); - } - - return URLClassLoader.newInstance(urls, this.getClass().getClassLoader()); - } - else - { - return this.getClass().getClassLoader(); - } - } - - private class ClasspathKeyResourceLoader implements KeyResourceLoader - { - @Override - public InputStream getKeyStore(String location) throws FileNotFoundException - { - return getClass().getClassLoader().getResourceAsStream(location); - } - - @Override - public Properties loadKeyMetaData(String location) throws IOException - { - Properties p = new Properties(); - p.load(getClass().getClassLoader().getResourceAsStream(location)); - return p; - } - } - - /** - * Full testing of ChangeSets, ACLs and Readers - */ - public void testGetAcls() throws Exception - { - AclChangeSets aclChangeSets = null; - - aclChangeSets = client.getAclChangeSets(null, null, null, null, 50); - assertTrue("Too many results", aclChangeSets.getAclChangeSets().size() <= 50); - if (aclChangeSets.getAclChangeSets().size() < 2) - { - return; // Not enough data - } - AclChangeSet aclChangeSetCheck = null; - AclChangeSet aclChangeSet0 = aclChangeSets.getAclChangeSets().get(0); - AclChangeSet aclChangeSet1 = aclChangeSets.getAclChangeSets().get(1); - long id0 = aclChangeSet0.getId(); - long commitTimeMs0 = aclChangeSet0.getCommitTimeMs(); - // Now query for the next ID - Long nextId = id0 + 1; - aclChangeSets = client.getAclChangeSets(commitTimeMs0, nextId, null, null, 1); - assertEquals(1, aclChangeSets.getAclChangeSets().size()); - aclChangeSetCheck = aclChangeSets.getAclChangeSets().get(0); - assertEquals(aclChangeSet1, aclChangeSetCheck); - - Map aclChangeSetsById = new HashMap(); - for (AclChangeSet aclChangeSet : aclChangeSets.getAclChangeSets()) - { - aclChangeSetsById.put(aclChangeSet.getId(), aclChangeSet); - } - - Set aclIdUniqueCheck = new HashSet(1000); - // Now do a large walk-through of the ACLs - Long minAclChangeSetId = null; - Long fromCommitTimeMs = null; - for (int i = 0; i < 100; i++) - { - aclChangeSets = client.getAclChangeSets(fromCommitTimeMs, minAclChangeSetId, null, null, 10); - if (aclChangeSets.getAclChangeSets().size() == 0) - { - break; - } - // Now repeat for the ACLs, keeping track of the last ChangeSet - Long nextAclId = null; - while (true) - { - List acls = client.getAcls(aclChangeSets.getAclChangeSets(), nextAclId, 512); - if (acls.size() == 0) - { - break; // Run out of ACLs - } - Set aclIds = new HashSet(1000); - for (Acl acl : acls) - { - long aclId = acl.getId(); - aclIds.add(aclId); - if (!aclIdUniqueCheck.add(aclId)) - { - // ignore duplicates for lazy/eager shared acl creation - //fail("ACL already processed: " + aclId); - } - // Check that we are ascending - if (nextAclId != null) - { - assertTrue("ACL IDs must be ascending: " + aclId, nextAclId.longValue() <= aclId); - } - nextAclId = aclId + 1; - } - // Now get the readers for these ACLs - List aclsReaders = client.getAclReaders(acls); - // Check that the ACL ids are all covered - for (AclReaders aclReaders : aclsReaders) - { - Long aclId = aclReaders.getId(); - aclIds.remove(aclId); - } - assertTrue("Some ACL IDs were not covered: " + aclIds, aclIds.size() == 0); - } - // March in time - AclChangeSet lastAclChangeSet = aclChangeSets.getAclChangeSets().get(aclChangeSets.getAclChangeSets().size() - 1); - fromCommitTimeMs = lastAclChangeSet.getCommitTimeMs(); - minAclChangeSetId = lastAclChangeSet.getId() + 1; - } - } - - public void testGetTransactions() throws Exception - { - // get transactions starting from txn id 1298288417234l - Transactions transactions = client.getTransactions(1298288417234l, null, null, null, 5); - - // get transactions starting from transaction 426 - transactions = client.getTransactions(null, Long.valueOf(1), null, null, 5); - List transactionIds = new ArrayList(transactions.getTransactions().size()); - for (Transaction info : transactions.getTransactions()) - { - logger.debug(info); - transactionIds.add(Long.valueOf(info.getId())); - } - - // get the first 3 nodes in those transactions - GetNodesParameters params = new GetNodesParameters(); - params.setTransactionIds(transactionIds); - List nodes = client.getNodes(params, 5); - for (Node info : nodes) - { - logger.debug(info); - } - - // get the next 3 nodes in those transactions i.e. starting from the last node id (inclusive) - params = new GetNodesParameters(); - params.setTransactionIds(transactionIds); - params.setFromNodeId(nodes.get(nodes.size() - 1).getId()); - nodes = client.getNodes(params, 3); - List nodeIds = new ArrayList(nodes.size()); - for (Node info : nodes) - { - logger.debug(info); - nodeIds.add(info.getId()); - } - - NodeMetaDataParameters metaParams = new NodeMetaDataParameters(); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - List metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - } - - public void testMetaData() throws AuthenticationException, IOException, JSONException - { - NodeMetaDataParameters metaParams = new NodeMetaDataParameters(); - List nodeIds = new ArrayList(1); - nodeIds.add(1L); - metaParams.setMaxResults(3); - metaParams.setNodeIds(nodeIds); - List metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(9L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(19L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - // individual tag/category - // TODO check why the category path has a null QName - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(49437L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - // content with tags - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(49431L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - MultiPropertyValue multi = (MultiPropertyValue) info.getProperties().get(QName.createQName("{http://www.alfresco.org/model/content/1.0}taggable")); - if(multi != null) - { - for (PropertyValue propValue : multi.getValues()) - { - logger.debug("multi property values = " + propValue); - } - } - } - - // content with null property values for author - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(117630L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - - // content with accented characters in title properties - metaParams = new NodeMetaDataParameters(); - nodeIds = new ArrayList(1); - nodeIds.add(117678L); - metaParams.setNodeIds(nodeIds); - metaParams.setMaxResults(3); - metadata = client.getNodesMetaData(metaParams); - for (NodeMetaData info : metadata) - { - logger.debug(info); - } - } - - public void testGetModel() throws AuthenticationException, IOException, JSONException - { - AlfrescoModel alfModel = client.getModel(CORENAME,QName.createQName("http://www.alfresco.org/model/content/1.0", "contentmodel")); - M2Model model = alfModel.getModel(); - assertNotNull(model); - assertEquals("Returned model has incorrect name", "cm:contentmodel", model.getName()); - assertNotNull(alfModel.getChecksum()); - } - - public void testGetModelDiffs() throws AuthenticationException, IOException, JSONException - { - List diffs = client.getModelsDiff(CORENAME,Collections. emptyList()); - assertTrue(diffs.size() > 0); - } - -// public void testMAC() throws IOException, JSONException -// { -// // dodyClient has a secret key that is not the same as the repository's. This -// // should fail with a 401 -// try -// { -// Transactions transactions = invalidKeyClient.getTransactions(1298288417234l, null, null, null, 5); -// } -// catch (AuthenticationException e) -// { -// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); -// } -// -// try -// { -// tamperWithEncryptionService.setOverrideTimestamp(true); -// Transactions transactions = tamperWithClient.getTransactions(1298288417234l, null, null, null, 5); -// } -// catch (AuthenticationException e) -// { -// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); -// } -// finally -// { -// tamperWithEncryptionService.setOverrideTimestamp(false); -// } -// -// try -// { -// tamperWithEncryptionService.setOverrideMAC(true); -// Transactions transactions = tamperWithClient.getTransactions(1298288417234l, null, null, null, 5); -// } -// catch (AuthenticationException e) -// { -// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); -// } -// finally -// { -// tamperWithEncryptionService.setOverrideMAC(false); -// } -// } - - private void outputTextContent(SOLRAPIClient.GetTextContentResponse response) throws IOException - { - InputStream in = response.getContent(); - if (in != null) - { - logger.debug("Text content:"); - - BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); - String line = null; - while ((line = reader.readLine()) != null) - { - logger.debug(line); - } - } - } - - /** - * Overrides request encryption to create dodgy MAC and timestamp on requests - */ - private static class TestEncryptionUtils extends DefaultEncryptionUtils - { - private boolean overrideMAC = false; - - private boolean overrideTimestamp = false; - - public void setOverrideMAC(boolean overrideMAC) - { - this.overrideMAC = overrideMAC; - } - - public void setOverrideTimestamp(boolean overrideTimestamp) - { - this.overrideTimestamp = overrideTimestamp; - } - - @Override - public void setRequestAuthentication(HttpMethod method, byte[] message) throws IOException - { - if (method instanceof PostMethod) - { - // encrypt body - Pair encrypted = encryptor.encrypt(KeyProvider.ALIAS_SOLR, null, message); - setRequestAlgorithmParameters(method, encrypted.getSecond()); - - ((PostMethod) method).setRequestEntity(new ByteArrayRequestEntity(encrypted.getFirst(), "application/octet-stream")); - } - - long requestTimestamp = System.currentTimeMillis(); - - // add MAC header - byte[] mac = macUtils.generateMAC(KeyProvider.ALIAS_SOLR, new MACInput(message, requestTimestamp, getLocalIPAddress())); - - if (logger.isDebugEnabled()) - { - logger.debug("Setting MAC " + mac + " on HTTP request " + method.getPath()); - logger.debug("Setting timestamp " + requestTimestamp + " on HTTP request " + method.getPath()); - } - - if (overrideMAC) - { - mac[0] += (byte) 1; - } - setRequestMac(method, mac); - - if (overrideTimestamp) - { - requestTimestamp += 60000; - } - // prevent replays - setRequestTimestamp(method, requestTimestamp); - } - } -} +/* + * #%L + * Alfresco Search Services + * %% + * Copyright (C) 2005 - 2020 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.solr.client; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.AlgorithmParameters; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.alfresco.encryption.DefaultEncryptionUtils; +import org.alfresco.encryption.KeyProvider; +import org.alfresco.encryption.KeyResourceLoader; +import org.alfresco.encryption.KeyStoreParameters; +import org.alfresco.encryption.MACUtils.MACInput; +import org.alfresco.encryption.ssl.SSLEncryptionParameters; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.AlfrescoHttpClient; +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.httpclient.HttpClientFactory; +import org.alfresco.httpclient.HttpClientFactory.SecureCommsType; +import org.alfresco.opencmis.dictionary.CMISDictionaryRegistry; +import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.opencmis.mapping.RuntimePropertyLuceneBuilderMapping; +import org.alfresco.repo.cache.MemoryCache; +import org.alfresco.repo.dictionary.CompiledModelsCache; +import org.alfresco.repo.dictionary.DictionaryComponent; +import org.alfresco.repo.dictionary.DictionaryDAOImpl; +import org.alfresco.repo.dictionary.DictionaryNamespaceComponent; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.M2Namespace; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.repo.tenant.SingleTServiceImpl; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.DynamicallySizedThreadPoolExecutor; +import org.alfresco.util.Pair; +import org.alfresco.util.TraceableThreadFactory; +import org.alfresco.util.cache.DefaultAsynchronouslyRefreshedCacheRegistry; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; + +import junit.framework.TestCase; + +/** + * Tests {@link SOLRAPIClient} Note: need to make sure that source/solr/instance is on the run classpath. Note: doesn't + * currently work, need to change to use SSL. + * + * @since 4.0 + */ +public class SOLRAPIClientTest extends TestCase +{ + private static Log logger = LogFactory.getLog(SOLRAPIClientTest.class); + + // private static final String TEST_MODEL = "org/alfresco/repo/dictionary/dictionarydaotest_model.xml"; + + private SOLRAPIClient client; + + private SOLRAPIClient invalidKeyClient; + + private SOLRAPIClient tamperWithClient; + + private DictionaryDAOImpl dictionaryDAO; + + private CMISStrictDictionaryService cmisDictionaryService; + + private static final String CORENAME = "collection1"; + + // private M2Model testModel; + + private void loadModel(Map modelMap, HashSet loadedModels, M2Model model) + { + String modelName = model.getName(); + if (loadedModels.contains(modelName) == false) + { + for (M2Namespace importNamespace : model.getImports()) + { + M2Model importedModel = modelMap.get(importNamespace.getUri()); + if (importedModel != null) + { + + // Ensure that the imported model is loaded first + loadModel(modelMap, loadedModels, importedModel); + } + } + + dictionaryDAO.putModelIgnoringConstraints(model); + loadedModels.add(modelName); + } + } + + @Override + public void setUp() throws Exception + { + if(client == null) + { + TenantService tenantService = new SingleTServiceImpl(); + + dictionaryDAO = new DictionaryDAOImpl(); + NamespaceDAO namespaceDAO = dictionaryDAO; + dictionaryDAO.setTenantService(tenantService); + + CompiledModelsCache compiledModelsCache = new CompiledModelsCache(); + compiledModelsCache.setDictionaryDAO(dictionaryDAO); + compiledModelsCache.setTenantService(tenantService); + compiledModelsCache.setRegistry(new DefaultAsynchronouslyRefreshedCacheRegistry()); + TraceableThreadFactory threadFactory = new TraceableThreadFactory(); + threadFactory.setThreadDaemon(true); + threadFactory.setThreadPriority(Thread.NORM_PRIORITY); + + ThreadPoolExecutor threadPoolExecutor = new DynamicallySizedThreadPoolExecutor(20, 20, 90, TimeUnit.SECONDS, new LinkedBlockingQueue(), threadFactory, + new ThreadPoolExecutor.CallerRunsPolicy()); + compiledModelsCache.setThreadPoolExecutor(threadPoolExecutor); + dictionaryDAO.setDictionaryRegistryCache(compiledModelsCache); + dictionaryDAO.setResourceClassLoader(getResourceClassLoader()); + dictionaryDAO.init(); + + DictionaryComponent dictionaryComponent = new DictionaryComponent(); + dictionaryComponent.setDictionaryDAO(dictionaryDAO); + dictionaryComponent.setMessageLookup(new StaticMessageLookup()); + + // cmis dictionary + CMISMapping cmisMapping = new CMISMapping(); + cmisMapping.setCmisVersion(CmisVersion.CMIS_1_0); + DictionaryNamespaceComponent namespaceService = new DictionaryNamespaceComponent(); + namespaceService.setNamespaceDAO(namespaceDAO); + cmisMapping.setNamespaceService(namespaceService); + cmisMapping.setDictionaryService(dictionaryComponent); + cmisMapping.afterPropertiesSet(); + + cmisDictionaryService = new CMISStrictDictionaryService(); + cmisDictionaryService.setCmisMapping(cmisMapping); + cmisDictionaryService.setDictionaryService(dictionaryComponent); + cmisDictionaryService.setDictionaryDAO(dictionaryDAO); + cmisDictionaryService.setSingletonCache(new MemoryCache()); + cmisDictionaryService.setTenantService(tenantService); + cmisDictionaryService.init(); + + RuntimePropertyLuceneBuilderMapping luceneBuilderMapping = new RuntimePropertyLuceneBuilderMapping(); + luceneBuilderMapping.setDictionaryService(dictionaryComponent); + luceneBuilderMapping.setCmisDictionaryService(cmisDictionaryService); + cmisDictionaryService.setPropertyLuceneBuilderMapping(luceneBuilderMapping); + + luceneBuilderMapping.afterPropertiesSet(); + + // Load the key store from the classpath + ClasspathKeyResourceLoader keyResourceLoader = new ClasspathKeyResourceLoader(); + client = new SOLRAPIClient(getRepoClient(keyResourceLoader), dictionaryComponent, dictionaryDAO); + trackModels(); + } + } + + private void trackModels() throws AuthenticationException, IOException, JSONException + { + + List modelDiffs = client.getModelsDiff(CORENAME,Collections. emptyList()); + HashMap modelMap = new HashMap(); + + for (AlfrescoModelDiff modelDiff : modelDiffs) + { + switch (modelDiff.getType()) + { + case CHANGED: + AlfrescoModel changedModel = client.getModel(CORENAME, modelDiff.getModelName()); + for (M2Namespace namespace : changedModel.getModel().getNamespaces()) + { + modelMap.put(namespace.getUri(), changedModel.getModel()); + } + break; + case NEW: + AlfrescoModel newModel = client.getModel(CORENAME, modelDiff.getModelName()); + for (M2Namespace namespace : newModel.getModel().getNamespaces()) + { + modelMap.put(namespace.getUri(), newModel.getModel()); + } + break; + case REMOVED: + // At the moment we do not unload models - I can see no side effects .... + // However search is used to check for references to indexed properties or types + // This will be partially broken anyway due to eventual consistency + // A model should only be unloaded if there are no data dependencies + // Should have been on the de-lucene list. + break; + } + } + + HashSet loadedModels = new HashSet(); + for (M2Model model : modelMap.values()) + { + loadModel(modelMap, loadedModels, model); + } + if (modelDiffs.size() > 0) + { + afterInitModels(); + } + + } + + public void afterInitModels() + { + cmisDictionaryService.afterDictionaryInit(); + } + + + protected AlfrescoHttpClient getRepoClient(ClasspathKeyResourceLoader keyResourceLoader) + { + // TODO i18n + KeyStoreParameters keyStoreParameters = new KeyStoreParameters("ssl-keystore", "SSL Key Store", "JCEKS", null, "ssl-keystore-passwords.properties", "ssl.repo.client.keystore"); + KeyStoreParameters trustStoreParameters = new KeyStoreParameters("ssl-truststore", "SSL Trust Store", "JCEKS", null, "ssl-truststore-passwords.properties", "ssl.repo.client.truststore"); + + SSLEncryptionParameters sslEncryptionParameters = new SSLEncryptionParameters(keyStoreParameters, trustStoreParameters); + + //MD5HttpClientFactory httpClientFactory = new MD5HttpClientFactory(); +// HttpClientFactory httpClientFactory = new HttpClientFactory(SecureComm); +// +// invalidKeyEncryptionService = new EncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); +// AlfrescoHttpClient repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, invalidKeyEncryptionService); +// //SecureHttpClient repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, invalidKeyEncryptionService); +// invalidKeyClient = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); +// +// keyStoreParameters.setLocation("org/alfresco/solr/client/.keystore"); +// tamperWithEncryptionService = new TamperWithEncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); +// repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, tamperWithEncryptionService); +//// repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, tamperWithEncryptionService); +// tamperWithClient = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); +// +// encryptionParameters.setMessageTimeout(30*1000); +// keyStoreParameters.setLocation("workspace-SpacesStore/conf/.keystore"); +// EncryptionService encryptionService = new EncryptionService("127.0.0.1", 8080, keyResourceLoader, keyStoreParameters, encryptionParameters); +// repoClient = httpClientFactory.getAlfrescoHttpClient("127.0.0.1", 8080, encryptionService); + //repoClient = new SecureHttpClient(httpClientFactory, "127.0.0.1", 8080, encryptionService); +// client = new SOLRAPIClient(repoClient, model.getDictionaryService(), namespaceDAO); + + HttpClientFactory httpClientFactory = new HttpClientFactory(SecureCommsType.getType("https"), sslEncryptionParameters, keyResourceLoader, null, null, "localhost", 8080, + 8443, 40, 40, 0); + // TODO need to make port configurable depending on secure comms, or just make redirects + // work + return httpClientFactory.getRepoClient("localhost", 8443); + } + + public ClassLoader getResourceClassLoader() + { + + File f = new File("woof", "alfrescoResources"); + if (f.canRead() && f.isDirectory()) + { + + URL[] urls = new URL[1]; + + try + { + URL url = f.toURI().normalize().toURL(); + urls[0] = url; + } + catch (MalformedURLException e) + { + throw new AlfrescoRuntimeException("Failed to add resources to classpath ", e); + } + + return URLClassLoader.newInstance(urls, this.getClass().getClassLoader()); + } + else + { + return this.getClass().getClassLoader(); + } + } + + private class ClasspathKeyResourceLoader implements KeyResourceLoader + { + @Override + public InputStream getKeyStore(String location) throws FileNotFoundException + { + return getClass().getClassLoader().getResourceAsStream(location); + } + + @Override + public Properties loadKeyMetaData(String location) throws IOException + { + Properties p = new Properties(); + p.load(getClass().getClassLoader().getResourceAsStream(location)); + return p; + } + } + + /** + * Full testing of ChangeSets, ACLs and Readers + */ + public void testGetAcls() throws Exception + { + AclChangeSets aclChangeSets = null; + + aclChangeSets = client.getAclChangeSets(null, null, null, null, 50); + assertTrue("Too many results", aclChangeSets.getAclChangeSets().size() <= 50); + if (aclChangeSets.getAclChangeSets().size() < 2) + { + return; // Not enough data + } + AclChangeSet aclChangeSetCheck = null; + AclChangeSet aclChangeSet0 = aclChangeSets.getAclChangeSets().get(0); + AclChangeSet aclChangeSet1 = aclChangeSets.getAclChangeSets().get(1); + long id0 = aclChangeSet0.getId(); + long commitTimeMs0 = aclChangeSet0.getCommitTimeMs(); + // Now query for the next ID + Long nextId = id0 + 1; + aclChangeSets = client.getAclChangeSets(commitTimeMs0, nextId, null, null, 1); + assertEquals(1, aclChangeSets.getAclChangeSets().size()); + aclChangeSetCheck = aclChangeSets.getAclChangeSets().get(0); + assertEquals(aclChangeSet1, aclChangeSetCheck); + + Map aclChangeSetsById = new HashMap(); + for (AclChangeSet aclChangeSet : aclChangeSets.getAclChangeSets()) + { + aclChangeSetsById.put(aclChangeSet.getId(), aclChangeSet); + } + + Set aclIdUniqueCheck = new HashSet(1000); + // Now do a large walk-through of the ACLs + Long minAclChangeSetId = null; + Long fromCommitTimeMs = null; + for (int i = 0; i < 100; i++) + { + aclChangeSets = client.getAclChangeSets(fromCommitTimeMs, minAclChangeSetId, null, null, 10); + if (aclChangeSets.getAclChangeSets().size() == 0) + { + break; + } + // Now repeat for the ACLs, keeping track of the last ChangeSet + Long nextAclId = null; + while (true) + { + List acls = client.getAcls(aclChangeSets.getAclChangeSets(), nextAclId, 512); + if (acls.size() == 0) + { + break; // Run out of ACLs + } + Set aclIds = new HashSet(1000); + for (Acl acl : acls) + { + long aclId = acl.getId(); + aclIds.add(aclId); + if (!aclIdUniqueCheck.add(aclId)) + { + // ignore duplicates for lazy/eager shared acl creation + //fail("ACL already processed: " + aclId); + } + // Check that we are ascending + if (nextAclId != null) + { + assertTrue("ACL IDs must be ascending: " + aclId, nextAclId.longValue() <= aclId); + } + nextAclId = aclId + 1; + } + // Now get the readers for these ACLs + List aclsReaders = client.getAclReaders(acls); + // Check that the ACL ids are all covered + for (AclReaders aclReaders : aclsReaders) + { + Long aclId = aclReaders.getId(); + aclIds.remove(aclId); + } + assertTrue("Some ACL IDs were not covered: " + aclIds, aclIds.size() == 0); + } + // March in time + AclChangeSet lastAclChangeSet = aclChangeSets.getAclChangeSets().get(aclChangeSets.getAclChangeSets().size() - 1); + fromCommitTimeMs = lastAclChangeSet.getCommitTimeMs(); + minAclChangeSetId = lastAclChangeSet.getId() + 1; + } + } + + public void testGetTransactions() throws Exception + { + // get transactions starting from txn id 1298288417234l + Transactions transactions = client.getTransactions(1298288417234l, null, null, null, 5); + + // get transactions starting from transaction 426 + transactions = client.getTransactions(null, Long.valueOf(1), null, null, 5); + List transactionIds = new ArrayList(transactions.getTransactions().size()); + for (Transaction info : transactions.getTransactions()) + { + logger.debug(info); + transactionIds.add(Long.valueOf(info.getId())); + } + + // get the first 3 nodes in those transactions + GetNodesParameters params = new GetNodesParameters(); + params.setTransactionIds(transactionIds); + List nodes = client.getNodes(params, 5); + for (Node info : nodes) + { + logger.debug(info); + } + + // get the next 3 nodes in those transactions i.e. starting from the last node id (inclusive) + params = new GetNodesParameters(); + params.setTransactionIds(transactionIds); + params.setFromNodeId(nodes.get(nodes.size() - 1).getId()); + nodes = client.getNodes(params, 3); + List nodeIds = new ArrayList(nodes.size()); + for (Node info : nodes) + { + logger.debug(info); + nodeIds.add(info.getId()); + } + + NodeMetaDataParameters metaParams = new NodeMetaDataParameters(); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + List metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + } + + public void testMetaData() throws AuthenticationException, IOException, JSONException + { + NodeMetaDataParameters metaParams = new NodeMetaDataParameters(); + List nodeIds = new ArrayList(1); + nodeIds.add(1L); + metaParams.setMaxResults(3); + metaParams.setNodeIds(nodeIds); + List metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(9L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(19L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + // individual tag/category + // TODO check why the category path has a null QName + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(49437L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + // content with tags + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(49431L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + MultiPropertyValue multi = (MultiPropertyValue) info.getProperties().get(QName.createQName("{http://www.alfresco.org/model/content/1.0}taggable")); + if(multi != null) + { + for (PropertyValue propValue : multi.getValues()) + { + logger.debug("multi property values = " + propValue); + } + } + } + + // content with null property values for author + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(117630L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + + // content with accented characters in title properties + metaParams = new NodeMetaDataParameters(); + nodeIds = new ArrayList(1); + nodeIds.add(117678L); + metaParams.setNodeIds(nodeIds); + metaParams.setMaxResults(3); + metadata = client.getNodesMetaData(metaParams); + for (NodeMetaData info : metadata) + { + logger.debug(info); + } + } + + public void testGetModel() throws AuthenticationException, IOException, JSONException + { + AlfrescoModel alfModel = client.getModel(CORENAME,QName.createQName("http://www.alfresco.org/model/content/1.0", "contentmodel")); + M2Model model = alfModel.getModel(); + assertNotNull(model); + assertEquals("Returned model has incorrect name", "cm:contentmodel", model.getName()); + assertNotNull(alfModel.getChecksum()); + } + + public void testGetModelDiffs() throws AuthenticationException, IOException, JSONException + { + List diffs = client.getModelsDiff(CORENAME,Collections. emptyList()); + assertTrue(diffs.size() > 0); + } + +// public void testMAC() throws IOException, JSONException +// { +// // dodyClient has a secret key that is not the same as the repository's. This +// // should fail with a 401 +// try +// { +// Transactions transactions = invalidKeyClient.getTransactions(1298288417234l, null, null, null, 5); +// } +// catch (AuthenticationException e) +// { +// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); +// } +// +// try +// { +// tamperWithEncryptionService.setOverrideTimestamp(true); +// Transactions transactions = tamperWithClient.getTransactions(1298288417234l, null, null, null, 5); +// } +// catch (AuthenticationException e) +// { +// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); +// } +// finally +// { +// tamperWithEncryptionService.setOverrideTimestamp(false); +// } +// +// try +// { +// tamperWithEncryptionService.setOverrideMAC(true); +// Transactions transactions = tamperWithClient.getTransactions(1298288417234l, null, null, null, 5); +// } +// catch (AuthenticationException e) +// { +// assertEquals("Should have caught unathorised request", e.getMethod().getStatusCode(), HttpStatus.SC_UNAUTHORIZED); +// } +// finally +// { +// tamperWithEncryptionService.setOverrideMAC(false); +// } +// } + + private void outputTextContent(SOLRAPIClient.GetTextContentResponse response) throws IOException + { + InputStream in = response.getContent(); + if (in != null) + { + logger.debug("Text content:"); + + BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); + String line = null; + while ((line = reader.readLine()) != null) + { + logger.debug(line); + } + } + } + + /** + * Overrides request encryption to create dodgy MAC and timestamp on requests + */ + private static class TestEncryptionUtils extends DefaultEncryptionUtils + { + private boolean overrideMAC = false; + + private boolean overrideTimestamp = false; + + public void setOverrideMAC(boolean overrideMAC) + { + this.overrideMAC = overrideMAC; + } + + public void setOverrideTimestamp(boolean overrideTimestamp) + { + this.overrideTimestamp = overrideTimestamp; + } + + @Override + public void setRequestAuthentication(HttpMethod method, byte[] message) throws IOException + { + if (method instanceof PostMethod) + { + // encrypt body + Pair encrypted = encryptor.encrypt(KeyProvider.ALIAS_SOLR, null, message); + setRequestAlgorithmParameters(method, encrypted.getSecond()); + + ((PostMethod) method).setRequestEntity(new ByteArrayRequestEntity(encrypted.getFirst(), "application/octet-stream")); + } + + long requestTimestamp = System.currentTimeMillis(); + + // add MAC header + byte[] mac = macUtils.generateMAC(KeyProvider.ALIAS_SOLR, new MACInput(message, requestTimestamp, getLocalIPAddress())); + + if (logger.isDebugEnabled()) + { + logger.debug("Setting MAC " + mac + " on HTTP request " + method.getPath()); + logger.debug("Setting timestamp " + requestTimestamp + " on HTTP request " + method.getPath()); + } + + if (overrideMAC) + { + mac[0] += (byte) 1; + } + setRequestMac(method, mac); + + if (overrideTimestamp) + { + requestTimestamp += 60000; + } + // prevent replays + setRequestTimestamp(method, requestTimestamp); + } + } +} diff --git a/search-services/packaging/src/test/java/org/alfresco/ThirdPartyLicensesIT.java b/search-services/packaging/src/test/java/org/alfresco/ThirdPartyLicensesIT.java index 47b1a06d3..17bb08092 100644 --- a/search-services/packaging/src/test/java/org/alfresco/ThirdPartyLicensesIT.java +++ b/search-services/packaging/src/test/java/org/alfresco/ThirdPartyLicensesIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Search Services * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2020 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of