From acabdc7984fa92367c5a174ab2e413acf16be60a Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2024 01:53:06 +0530 Subject: [PATCH] =?UTF-8?q?Adding=20cache=20eviction=20and=20listener=20fo?= =?UTF-8?q?r=20invalidating=20index=20field=20type=20=E2=80=A6=20(#142)=20?= =?UTF-8?q?(#143)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding cache eviction and listener for invalidating index field type mappings on index deletion/update * Fixing spotless violations * Fixing code to use .index instead of .findMappings * Fixing spotless violations * Addressing review comments * Fixing existing test failures * Fixing existing test failures --------- (cherry picked from commit fb241187ee73cabd40e20a0720da03bc40cdde74) Signed-off-by: Ankit Jain Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] --- .../plugin/insights/QueryInsightsPlugin.java | 3 +- .../categorizer/IndicesFieldTypeCache.java | 86 +++++++++++++++++++ .../categorizer/QueryShapeGenerator.java | 52 ++++++----- .../settings/QueryCategorizationSettings.java | 7 ++ .../insights/QueryInsightsPluginTests.java | 3 +- .../categorizer/QueryShapeGeneratorTests.java | 67 ++++++++------- .../categorizer/QueryShapeVisitorTests.java | 6 +- 7 files changed, 171 insertions(+), 53 deletions(-) create mode 100644 src/main/java/org/opensearch/plugin/insights/core/service/categorizer/IndicesFieldTypeCache.java diff --git a/src/main/java/org/opensearch/plugin/insights/QueryInsightsPlugin.java b/src/main/java/org/opensearch/plugin/insights/QueryInsightsPlugin.java index 862f4c8..26d86e9 100644 --- a/src/main/java/org/opensearch/plugin/insights/QueryInsightsPlugin.java +++ b/src/main/java/org/opensearch/plugin/insights/QueryInsightsPlugin.java @@ -144,7 +144,8 @@ public List> getSettings() { QueryInsightsSettings.TOP_N_QUERIES_MAX_GROUPS_EXCLUDING_N, QueryInsightsSettings.TOP_N_QUERIES_GROUPING_FIELD_NAME, QueryInsightsSettings.TOP_N_QUERIES_GROUPING_FIELD_TYPE, - QueryCategorizationSettings.SEARCH_QUERY_METRICS_ENABLED_SETTING + QueryCategorizationSettings.SEARCH_QUERY_METRICS_ENABLED_SETTING, + QueryCategorizationSettings.SEARCH_QUERY_FIELD_TYPE_CACHE_SIZE_KEY ); } } diff --git a/src/main/java/org/opensearch/plugin/insights/core/service/categorizer/IndicesFieldTypeCache.java b/src/main/java/org/opensearch/plugin/insights/core/service/categorizer/IndicesFieldTypeCache.java new file mode 100644 index 0000000..abf7975 --- /dev/null +++ b/src/main/java/org/opensearch/plugin/insights/core/service/categorizer/IndicesFieldTypeCache.java @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.plugin.insights.core.service.categorizer; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.lucene.util.RamUsageEstimator; +import org.opensearch.common.cache.Cache; +import org.opensearch.common.cache.CacheBuilder; +import org.opensearch.common.metrics.CounterMetric; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.Index; +import org.opensearch.plugin.insights.settings.QueryCategorizationSettings; + +/** + * Cache implementation specifically for maintaining the field name type mappings + * for indices that are part of successful search requests + */ +public class IndicesFieldTypeCache { + + private static final Logger logger = LogManager.getLogger(IndicesFieldTypeCache.class); + private final Cache cache; + + public IndicesFieldTypeCache(Settings settings) { + final long sizeInBytes = QueryCategorizationSettings.SEARCH_QUERY_FIELD_TYPE_CACHE_SIZE_KEY.get(settings).getBytes(); + CacheBuilder cacheBuilder = CacheBuilder.builder(); + if (sizeInBytes > 0) { + cacheBuilder.setMaximumWeight(sizeInBytes).weigher((k, v) -> RamUsageEstimator.sizeOfObject(k) + v.weight()); + } + cache = cacheBuilder.build(); + } + + public IndexFieldMap getOrInitialize(Index index) { + try { + return cache.computeIfAbsent(index, k -> new IndexFieldMap()); + } catch (ExecutionException ex) { + logger.error("Unexpected execution exception while initializing for index " + index); + } + + // Should never return null as the ExecutionException is only thrown + // if loader throws an exception or returns a null value, which cannot + // be the case in this scenario + return null; + } + + public void invalidate(Index index) { + cache.invalidate(index); + } + + public Iterable keySet() { + return cache.keys(); + } + + static class IndexFieldMap { + private ConcurrentHashMap fieldTypeMap; + private CounterMetric weight; + + IndexFieldMap() { + fieldTypeMap = new ConcurrentHashMap<>(); + weight = new CounterMetric(); + } + + public String get(String fieldName) { + return fieldTypeMap.get(fieldName); + } + + public void putIfAbsent(String key, String value) { + // Increment the weight only if the key value pair added to the Map + if (fieldTypeMap.putIfAbsent(key, value) == null) { + weight.inc(RamUsageEstimator.sizeOf(key) + RamUsageEstimator.sizeOf(value)); + } + } + + public long weight() { + return weight.count(); + } + } +} diff --git a/src/main/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeGenerator.java b/src/main/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeGenerator.java index f16257b..191c606 100644 --- a/src/main/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeGenerator.java +++ b/src/main/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeGenerator.java @@ -8,16 +8,17 @@ package org.opensearch.plugin.insights.core.service.categorizer; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import org.apache.lucene.util.BytesRef; -import org.opensearch.cluster.metadata.MappingMetadata; +import org.opensearch.cluster.ClusterChangedEvent; +import org.opensearch.cluster.ClusterStateListener; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.hash.MurmurHash3; import org.opensearch.core.common.io.stream.NamedWriteable; @@ -33,16 +34,36 @@ /** * Class to generate query shape */ -public class QueryShapeGenerator { +public class QueryShapeGenerator implements ClusterStateListener { static final String EMPTY_STRING = ""; static final String ONE_SPACE_INDENT = " "; private final ClusterService clusterService; private final String NO_FIELD_TYPE_VALUE = ""; - private final ConcurrentHashMap> fieldTypeMap; + private final IndicesFieldTypeCache indicesFieldTypeCache; public QueryShapeGenerator(ClusterService clusterService) { this.clusterService = clusterService; - this.fieldTypeMap = new ConcurrentHashMap<>(); + clusterService.addListener(this); + this.indicesFieldTypeCache = new IndicesFieldTypeCache(clusterService.getSettings()); + } + + public void clusterChanged(ClusterChangedEvent event) { + final List indicesDeleted = event.indicesDeleted(); + for (Index index : indicesDeleted) { + // remove the deleted index mapping from field type cache + indicesFieldTypeCache.invalidate(index); + } + + if (event.metadataChanged()) { + final Metadata previousMetadata = event.previousState().metadata(); + final Metadata currentMetadata = event.state().metadata(); + for (Index index : indicesFieldTypeCache.keySet()) { + if (previousMetadata.index(index) != currentMetadata.index(index)) { + // remove the updated index mapping from field type cache + indicesFieldTypeCache.invalidate(index); + } + } + } } /** @@ -127,20 +148,12 @@ public String buildShape( } private Map getPropertiesMapForIndex(Index index) { - Map indexMapping; - try { - indexMapping = clusterService.state().metadata().findMappings(new String[] { index.getName() }, input -> str -> true); - } catch (IOException e) { - // If an error occurs while retrieving mappings, return an empty map - return Collections.emptyMap(); - } - - MappingMetadata mappingMetadata = indexMapping.get(index.getName()); - if (mappingMetadata == null) { + IndexMetadata indexMetadata = clusterService.state().metadata().index(index); + if (indexMetadata == null) { return Collections.emptyMap(); } - Map propertiesMap = (Map) mappingMetadata.getSourceAsMap().get("properties"); + Map propertiesMap = (Map) indexMetadata.mapping().getSourceAsMap().get("properties"); if (propertiesMap == null) { return Collections.emptyMap(); } @@ -363,8 +376,7 @@ String getFieldType(String fieldName, Map propertiesAsMap, Index fieldType = getFieldTypeFromProperties(fieldName, propertiesAsMap); // Cache field type or NO_FIELD_TYPE_VALUE if not found - fieldTypeMap.computeIfAbsent(index, k -> new ConcurrentHashMap<>()) - .putIfAbsent(fieldName, fieldType != null ? fieldType : NO_FIELD_TYPE_VALUE); + indicesFieldTypeCache.getOrInitialize(index).putIfAbsent(fieldName, fieldType != null ? fieldType : NO_FIELD_TYPE_VALUE); return fieldType; } @@ -406,6 +418,6 @@ else if (currentMap.containsKey("type")) { } String getFieldTypeFromCache(String fieldName, Index index) { - return fieldTypeMap.getOrDefault(index, new ConcurrentHashMap<>()).get(fieldName); + return indicesFieldTypeCache.getOrInitialize(index).get(fieldName); } } diff --git a/src/main/java/org/opensearch/plugin/insights/settings/QueryCategorizationSettings.java b/src/main/java/org/opensearch/plugin/insights/settings/QueryCategorizationSettings.java index a5a65af..ec9c6af 100644 --- a/src/main/java/org/opensearch/plugin/insights/settings/QueryCategorizationSettings.java +++ b/src/main/java/org/opensearch/plugin/insights/settings/QueryCategorizationSettings.java @@ -9,6 +9,7 @@ package org.opensearch.plugin.insights.settings; import org.opensearch.common.settings.Setting; +import org.opensearch.core.common.unit.ByteSizeValue; /** * Settings for Query Categorization @@ -24,6 +25,12 @@ public class QueryCategorizationSettings { Setting.Property.Dynamic ); + public static final Setting SEARCH_QUERY_FIELD_TYPE_CACHE_SIZE_KEY = Setting.memorySizeSetting( + "search.query.fieldtype.cache.size", + "0.1%", + Setting.Property.NodeScope + ); + /** * Default constructor */ diff --git a/src/test/java/org/opensearch/plugin/insights/QueryInsightsPluginTests.java b/src/test/java/org/opensearch/plugin/insights/QueryInsightsPluginTests.java index e695994..de4354b 100644 --- a/src/test/java/org/opensearch/plugin/insights/QueryInsightsPluginTests.java +++ b/src/test/java/org/opensearch/plugin/insights/QueryInsightsPluginTests.java @@ -81,7 +81,8 @@ public void testGetSettings() { QueryInsightsSettings.TOP_N_QUERIES_MAX_GROUPS_EXCLUDING_N, QueryInsightsSettings.TOP_N_QUERIES_GROUPING_FIELD_NAME, QueryInsightsSettings.TOP_N_QUERIES_GROUPING_FIELD_TYPE, - QueryCategorizationSettings.SEARCH_QUERY_METRICS_ENABLED_SETTING + QueryCategorizationSettings.SEARCH_QUERY_METRICS_ENABLED_SETTING, + QueryCategorizationSettings.SEARCH_QUERY_FIELD_TYPE_CACHE_SIZE_KEY ), queryInsightsPlugin.getSettings() ); diff --git a/src/test/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeGeneratorTests.java b/src/test/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeGeneratorTests.java index dc603a8..f3eb25f 100644 --- a/src/test/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeGeneratorTests.java +++ b/src/test/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeGeneratorTests.java @@ -32,10 +32,12 @@ import java.util.UUID; import org.apache.lucene.search.join.ScoreMode; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.MappingMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.hash.MurmurHash3; +import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.DistanceUnit; import org.opensearch.core.compress.CompressorRegistry; import org.opensearch.core.index.Index; @@ -52,7 +54,9 @@ import org.opensearch.test.OpenSearchTestCase; public final class QueryShapeGeneratorTests extends OpenSearchTestCase { - final Set successfulSearchShardIndices = Set.of(new Index("index1", UUID.randomUUID().toString())); + final Index index1 = new Index("index1", UUID.randomUUID().toString()); + final Index index2 = new Index("index2", UUID.randomUUID().toString()); + final Set successfulSearchShardIndices = Set.of(index1); final QueryShapeGenerator queryShapeGenerator; private ClusterService mockClusterService; @@ -62,6 +66,7 @@ public final class QueryShapeGeneratorTests extends OpenSearchTestCase { public QueryShapeGeneratorTests() { CompressorRegistry.defaultCompressor(); this.mockClusterService = mock(ClusterService.class); + when(mockClusterService.getSettings()).thenReturn(Settings.EMPTY); this.mockClusterState = mock(ClusterState.class); this.mockMetaData = mock(Metadata.class); this.queryShapeGenerator = new QueryShapeGenerator(mockClusterService); @@ -70,18 +75,17 @@ public QueryShapeGeneratorTests() { when(mockClusterState.metadata()).thenReturn(mockMetaData); } - public void setUpMockMappings(String indexName, Map mappingProperties) throws IOException { + public void setUpMockMappings(Index index, Map mappingProperties) throws IOException { MappingMetadata mockMappingMetadata = mock(MappingMetadata.class); - when(mockMappingMetadata.getSourceAsMap()).thenReturn(mappingProperties); - - final Map indexMappingMap = Map.of(indexName, mockMappingMetadata); - when(mockMetaData.findMappings(any(), any())).thenReturn(indexMappingMap); + IndexMetadata indexMetadata = mock(IndexMetadata.class); + when(indexMetadata.mapping()).thenReturn(mockMappingMetadata); + when(mockMetaData.index(index)).thenReturn(indexMetadata); } public void testBasicSearchWithFieldNameAndType() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "properties", Map.of( @@ -113,7 +117,7 @@ public void testBasicSearchWithFieldNameAndType() throws IOException { // If field type is not found we leave the field type blank public void testFieldTypeNotFound() throws IOException { - setUpMockMappings("index1", Map.of("properties", Map.of("field1", Map.of("type", "keyword")))); + setUpMockMappings(index1, Map.of("properties", Map.of("field1", Map.of("type", "keyword")))); SearchSourceBuilder sourceBuilder = SearchSourceBuilderUtils.createQuerySearchSourceBuilder() .query( @@ -139,7 +143,7 @@ public void testFieldTypeNotFound() throws IOException { public void testEmptyMappings() throws IOException { setUpMockMappings( - "index1", + successfulSearchShardIndices.iterator().next(), Map.of( "properties", Map.of() // No fields defined @@ -170,9 +174,9 @@ public void testEmptyMappings() throws IOException { // Field type should be inferred from both the mappings public void testMultipleIndexMappings() throws IOException { - setUpMockMappings("index2", Map.of("properties", Map.of("field1", Map.of("type", "keyword")))); + setUpMockMappings(index2, Map.of("properties", Map.of("field1", Map.of("type", "keyword")))); - setUpMockMappings("index1", Map.of("properties", Map.of("field2", Map.of("type", "text"), "field4", Map.of("type", "long")))); + setUpMockMappings(index1, Map.of("properties", Map.of("field2", Map.of("type", "text"), "field4", Map.of("type", "long")))); SearchSourceBuilder sourceBuilder = SearchSourceBuilderUtils.createQuerySearchSourceBuilder(); @@ -190,7 +194,7 @@ public void testMultipleIndexMappings() throws IOException { public void testDifferentFieldTypes() throws IOException { setUpMockMappings( - "index1", + successfulSearchShardIndices.iterator().next(), Map.of( "properties", Map.of( @@ -222,7 +226,7 @@ public void testDifferentFieldTypes() throws IOException { public void testFieldWithNestedProperties() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "properties", Map.of( @@ -251,7 +255,7 @@ public void testFieldWithNestedProperties() throws IOException { public void testFieldWithArrayType() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "properties", Map.of("field1", Map.of("type", "keyword"), "field2", Map.of("type", "text"), "field3", Map.of("type", "keyword")) @@ -277,7 +281,7 @@ public void testFieldWithArrayType() throws IOException { } public void testFieldWithDateType() throws IOException { - setUpMockMappings("index1", Map.of("properties", Map.of("dateField", Map.of("type", "date")))); + setUpMockMappings(index1, Map.of("properties", Map.of("dateField", Map.of("type", "date")))); SearchSourceBuilder sourceBuilder = SearchSourceBuilderUtils.createQuerySearchSourceBuilder() .query(boolQuery().filter(rangeQuery("dateField").gte("2024-01-01").lte("2024-12-31"))); @@ -288,7 +292,7 @@ public void testFieldWithDateType() throws IOException { } public void testFieldWithGeoPointType() throws IOException { - setUpMockMappings("index1", Map.of("properties", Map.of("location", Map.of("type", "geo_point")))); + setUpMockMappings(index1, Map.of("properties", Map.of("location", Map.of("type", "geo_point")))); SearchSourceBuilder sourceBuilder = SearchSourceBuilderUtils.createQuerySearchSourceBuilder() .query(boolQuery().filter(geoDistanceQuery("location").point(40.73, -74.1).distance(200, DistanceUnit.KILOMETERS))); @@ -299,7 +303,10 @@ public void testFieldWithGeoPointType() throws IOException { } public void testFieldWithBinaryType() throws IOException { - setUpMockMappings("index1", Map.of("properties", Map.of("binaryField", Map.of("type", "binary")))); + setUpMockMappings( + successfulSearchShardIndices.iterator().next(), + Map.of("properties", Map.of("binaryField", Map.of("type", "binary"))) + ); SearchSourceBuilder sourceBuilder = SearchSourceBuilderUtils.createQuerySearchSourceBuilder() .query(boolQuery().must(termQuery("binaryField", "base64EncodedString"))); @@ -311,7 +318,7 @@ public void testFieldWithBinaryType() throws IOException { public void testFieldWithMixedTypes() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "properties", Map.of( @@ -339,7 +346,7 @@ public void testFieldWithMixedTypes() throws IOException { } public void testFieldWithInvalidQueries() throws IOException { - setUpMockMappings("index1", Map.of("properties", Map.of("field1", Map.of("type", "keyword")))); + setUpMockMappings(index1, Map.of("properties", Map.of("field1", Map.of("type", "keyword")))); SearchSourceBuilder sourceBuilder = SearchSourceBuilderUtils.createQuerySearchSourceBuilder() .query( @@ -353,7 +360,7 @@ public void testFieldWithInvalidQueries() throws IOException { public void testFieldWithDeeplyNestedStructure() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "properties", Map.of( @@ -378,7 +385,7 @@ public void testFieldWithDeeplyNestedStructure() throws IOException { // We are not parsing fields for scripts public void testFieldWithScriptedQuery() throws IOException { - setUpMockMappings("index1", Map.of("properties", Map.of("scriptedField", Map.of("type", "long")))); + setUpMockMappings(index1, Map.of("properties", Map.of("scriptedField", Map.of("type", "long")))); Script script = new Script( ScriptType.INLINE, @@ -398,7 +405,7 @@ public void testFieldWithScriptedQuery() throws IOException { public void testDynamicTemplateMappingWithTypeInference() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "dynamic_templates", List.of( @@ -424,7 +431,7 @@ public void testDynamicTemplateMappingWithTypeInference() throws IOException { } public void testFieldWithIpAddressType() throws IOException { - setUpMockMappings("index1", Map.of("properties", Map.of("ip_address", Map.of("type", "ip", "ignore_malformed", true)))); + setUpMockMappings(index1, Map.of("properties", Map.of("ip_address", Map.of("type", "ip", "ignore_malformed", true)))); SearchSourceBuilder sourceBuilder = SearchSourceBuilderUtils.createQuerySearchSourceBuilder() .query(boolQuery().must(termQuery("ip_address", "192.168.1.1")).filter(termQuery("ip_address", "invalid_ip"))); @@ -442,7 +449,7 @@ public void testFieldWithIpAddressType() throws IOException { // Nested query not working as expected public void testNestedQueryType() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "properties", Map.of( @@ -475,7 +482,7 @@ public void testNestedQueryType() throws IOException { public void testFlatObjectQueryType() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "properties", Map.of( @@ -515,7 +522,7 @@ public void testFlatObjectQueryType() throws IOException { public void testQueryTypeWithSorting() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "properties", Map.of("age", Map.of("type", "integer"), "name", Map.of("type", "keyword"), "score", Map.of("type", "float")) @@ -535,7 +542,7 @@ public void testQueryTypeWithSorting() throws IOException { } public void testQueryTypeWithAggregations() throws IOException { - setUpMockMappings("index1", Map.of("properties", Map.of("price", Map.of("type", "double"), "category", Map.of("type", "keyword")))); + setUpMockMappings(index1, Map.of("properties", Map.of("price", Map.of("type", "double"), "category", Map.of("type", "keyword")))); SearchSourceBuilder sourceBuilder = SearchSourceBuilderUtils.createQuerySearchSourceBuilder() .query(matchAllQuery()) @@ -551,7 +558,7 @@ public void testQueryTypeWithAggregations() throws IOException { // No field name and type being parsed for pipeline aggregations public void testQueryTypeWithPipelineAggregation() throws IOException { - setUpMockMappings("index1", Map.of("properties", Map.of("sales", Map.of("type", "double"), "timestamp", Map.of("type", "date")))); + setUpMockMappings(index1, Map.of("properties", Map.of("sales", Map.of("type", "double"), "timestamp", Map.of("type", "date")))); SearchSourceBuilder sourceBuilder = SearchSourceBuilderUtils.createQuerySearchSourceBuilder() .query(matchAllQuery()) @@ -579,7 +586,7 @@ public void testQueryTypeWithPipelineAggregation() throws IOException { // Should cache empty value when we do not find a field type to avoid doing the search again public void testFieldTypeCachingForNonExistentField() throws IOException { setUpMockMappings( - "index1", + index1, Map.of( "properties", Map.of("age", Map.of("type", "integer"), "name", Map.of("type", "keyword"), "score", Map.of("type", "float")) @@ -603,7 +610,7 @@ public void testFieldTypeCachingForNonExistentField() throws IOException { public void testMultifieldQueryCombined() throws IOException { setUpMockMappings( - "index1", + index1, Map.of("properties", Map.of("title", Map.of("type", "text", "fields", Map.of("raw", Map.of("type", "keyword"))))) ); diff --git a/src/test/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeVisitorTests.java b/src/test/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeVisitorTests.java index 5c9e4ba..eb363e4 100644 --- a/src/test/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeVisitorTests.java +++ b/src/test/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeVisitorTests.java @@ -9,9 +9,11 @@ package org.opensearch.plugin.insights.core.service.categorizer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.HashMap; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.Settings; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.ConstantScoreQueryBuilder; import org.opensearch.index.query.MatchQueryBuilder; @@ -31,8 +33,10 @@ public void testQueryShapeVisitor() { .mustNot(new RegexpQueryBuilder("color", "red.*")) ) .must(new TermsQueryBuilder("genre", "action", "drama", "romance")); + final ClusterService mockClusterService = mock(ClusterService.class); + when(mockClusterService.getSettings()).thenReturn(Settings.EMPTY); QueryShapeVisitor shapeVisitor = new QueryShapeVisitor( - new QueryShapeGenerator(mock(ClusterService.class)), + new QueryShapeGenerator(mockClusterService), new HashMap<>(), null, false,