diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecks.java index b0348061cf34d..d1ddfc5a29079 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecks.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecks.java @@ -291,4 +291,95 @@ static DeprecationIssue checkGeoShapeTemplates(final ClusterState clusterState) return null; } } + + protected static boolean isSparseVector(Map property) { + return "sparse_vector".equals(property.get("type")); + } + + protected static String formatDeprecatedSparseVectorMessage(String type, Map.Entry entry) { + return entry.getKey().toString(); + } + + @SuppressWarnings("unchecked") + private static String getDetailsMessageForSparseVectorComponentTemplates(Map componentTemplates) { + String detailsForComponentTemplates = + componentTemplates.entrySet().stream().map((templateCursor) -> { + String templateName = templateCursor.getKey(); + ComponentTemplate componentTemplate = templateCursor.getValue(); + CompressedXContent mappings = componentTemplate.template().mappings(); + if (mappings != null) { + Tuple> tuple = XContentHelper.convertToMap(mappings.uncompressed(), true, + XContentType.JSON); + Map mappingAsMap = tuple.v2(); + List messages = mappingAsMap == null ? Collections.emptyList() : + IndexDeprecationChecks.findInPropertiesRecursively(LegacyGeoShapeFieldMapper.CONTENT_TYPE, + mappingAsMap, + ClusterDeprecationChecks::isSparseVector, + ClusterDeprecationChecks::formatDeprecatedSparseVectorMessage); + if (messages.isEmpty() == false) { + String messageForMapping = + "mappings in component template [" + templateName + "] contains deprecated sparse_vector fields: " + + messages.stream().collect(Collectors.joining(", ")); + return messageForMapping; + } + } + return null; + }).filter(messageForTemplate -> Strings.isEmpty(messageForTemplate) == false).collect(Collectors.joining("; ")); + return detailsForComponentTemplates; + } + + @SuppressWarnings("unchecked") + private static String getDetailsMessageForSparseVectorIndexTemplates(ImmutableOpenMap indexTemplates) { + String detailsForIndexTemplates = + StreamSupport.stream(indexTemplates.spliterator(), false).map((templateCursor) -> { + String templateName = templateCursor.key; + IndexTemplateMetadata indexTemplateMetadata = templateCursor.value; + String messageForTemplate = + StreamSupport.stream(indexTemplateMetadata.getMappings().spliterator(), false).map((mappingCursor) -> { + CompressedXContent mapping = mappingCursor.value; + Tuple> tuple = XContentHelper.convertToMap(mapping.uncompressed(), true, + XContentType.JSON); + Map mappingAsMap = (Map) tuple.v2().get("_doc"); + List messages = mappingAsMap == null ? Collections.emptyList() : + IndexDeprecationChecks.findInPropertiesRecursively(LegacyGeoShapeFieldMapper.CONTENT_TYPE, + mappingAsMap, + ClusterDeprecationChecks::isSparseVector, + ClusterDeprecationChecks::formatDeprecatedSparseVectorMessage); + return messages; + }).filter(messages -> messages.isEmpty() == false).map(messages -> { + String messageForMapping = + "mappings in index template " + templateName + " contains deprecated sparse_vector fields: " + + messages.stream().collect(Collectors.joining(", ")); + return messageForMapping; + }).collect(Collectors.joining("; ")); + return messageForTemplate; + }).filter(messageForTemplate -> Strings.isEmpty(messageForTemplate) == false).collect(Collectors.joining("; ")); + return detailsForIndexTemplates; + } + + @SuppressWarnings("unchecked") + static DeprecationIssue checkSparseVectorTemplates(final ClusterState clusterState) { + String detailsForComponentTemplates = + getDetailsMessageForSparseVectorComponentTemplates(clusterState.getMetadata().componentTemplates()); + String detailsForIndexTemplates = getDetailsMessageForSparseVectorIndexTemplates(clusterState.getMetadata().getTemplates()); + boolean deprecationInComponentTemplates = Strings.isEmpty(detailsForComponentTemplates) == false; + boolean deprecationInIndexTemplates = Strings.isEmpty(detailsForIndexTemplates) == false; + String url = "https://www.elastic.co/guide/en/elasticsearch/reference/master/migrating-8.0.html#breaking_80_search_changes"; + if (deprecationInComponentTemplates && deprecationInIndexTemplates) { + String message = "component templates and index templates contain deprecated sparse_vector fields that must be removed"; + String details = detailsForComponentTemplates + "; " + detailsForIndexTemplates; + return new DeprecationIssue(DeprecationIssue.Level.CRITICAL, message, url, details, false, + null); + } if (deprecationInComponentTemplates == false && deprecationInIndexTemplates) { + String message = "index templates contain deprecated sparse_vector fields that must be removed"; + return new DeprecationIssue(DeprecationIssue.Level.CRITICAL, message, url, detailsForIndexTemplates, false, + null); + } else if (deprecationInIndexTemplates == false && deprecationInComponentTemplates) { + String message = "component templates contain deprecated sparse_vector fields that must be removed"; + return new DeprecationIssue(DeprecationIssue.Level.CRITICAL, message, url, detailsForComponentTemplates, false, + null); + } else { + return null; + } + } } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java index f75d6e1413cd8..1a57233e94f22 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java @@ -40,7 +40,8 @@ private DeprecationChecks() { ClusterDeprecationChecks::checkTemplatesWithFieldNamesDisabled, ClusterDeprecationChecks::checkTemplatesWithMultipleTypes, ClusterDeprecationChecks::checkClusterRoutingAllocationIncludeRelocationsSetting, - ClusterDeprecationChecks::checkGeoShapeTemplates + ClusterDeprecationChecks::checkGeoShapeTemplates, + ClusterDeprecationChecks::checkSparseVectorTemplates )); static final List> diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecksTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecksTests.java index 0deb011c90e3c..876adfaa72238 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecksTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecksTests.java @@ -439,4 +439,81 @@ public void testCheckGeoShapeMappings() throws Exception { "[location]]", false, null) )); } + + public void testSparseVectorMappings() throws Exception { + // First, testing only an index template: + IndexTemplateMetadata indexTemplateMetadata = IndexTemplateMetadata.builder("single-type") + .patterns(Collections.singletonList("foo")) + .putMapping("_doc", "{\n" + + " \"_doc\":{\n" + + " \"properties\":{\n" + + " \"my_sparse_vector\":{\n" + + " \"type\":\"sparse_vector\"\n" + + " },\n" + + " \"nested_field\":{\n" + + " \"type\":\"nested\",\n" + + " \"properties\":{\n" + + " \"my_nested_sparse_vector\":{\n" + + " \"type\":\"sparse_vector\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}") + .build(); + ImmutableOpenMap templates = ImmutableOpenMap.builder() + .fPut("single-type", indexTemplateMetadata) + .build(); + Metadata badMetadata = Metadata.builder() + .templates(templates) + .build(); + ClusterState badState = ClusterState.builder(new ClusterName("test")).metadata(badMetadata).build(); + DeprecationIssue issue = ClusterDeprecationChecks.checkSparseVectorTemplates(badState); + + assertThat(issue, equalTo( + new DeprecationIssue(DeprecationIssue.Level.CRITICAL, + "index templates contain deprecated sparse_vector fields that must be removed", + "https://www.elastic.co/guide/en/elasticsearch/reference/master/migrating-8.0.html#breaking_80_search_changes", + "mappings in index template single-type contains deprecated sparse_vector fields: [my_sparse_vector], " + + "[my_nested_sparse_vector]", false, null) + )); + + // Second, testing only a component template: + String templateName = "my-template"; + Settings settings = Settings.builder().put("index.number_of_shards", 1).build(); + CompressedXContent mappings = new CompressedXContent("{\"properties\":{\"my_sparse_vector\":{\"type\":\"sparse_vector\"}}}"); + AliasMetadata alias = AliasMetadata.builder("alias").writeIndex(true).build(); + Template template = new Template(settings, mappings, Collections.singletonMap("alias", alias)); + ComponentTemplate componentTemplate = new ComponentTemplate(template, 1L, new HashMap<>()); + badMetadata = Metadata.builder() + .componentTemplates(Collections.singletonMap(templateName, componentTemplate)) + .build(); + badState = ClusterState.builder(new ClusterName("test")).metadata(badMetadata).build(); + issue = ClusterDeprecationChecks.checkSparseVectorTemplates(badState); + + assertThat(issue, equalTo( + new DeprecationIssue(DeprecationIssue.Level.CRITICAL, + "component templates contain deprecated sparse_vector fields that must be removed", + "https://www.elastic.co/guide/en/elasticsearch/reference/master/migrating-8.0.html#breaking_80_search_changes", + "mappings in component template [my-template] contains deprecated sparse_vector fields: [my_sparse_vector]", false, null) + )); + + // Third, trying a component template and an index template: + badMetadata = Metadata.builder() + .componentTemplates(Collections.singletonMap(templateName, componentTemplate)) + .templates(templates) + .build(); + badState = ClusterState.builder(new ClusterName("test")).metadata(badMetadata).build(); + issue = ClusterDeprecationChecks.checkSparseVectorTemplates(badState); + + assertThat(issue, equalTo( + new DeprecationIssue(DeprecationIssue.Level.CRITICAL, + "component templates and index templates contain deprecated sparse_vector fields that must be removed", + "https://www.elastic.co/guide/en/elasticsearch/reference/master/migrating-8.0.html#breaking_80_search_changes", + "mappings in component template [my-template] contains deprecated sparse_vector fields: [my_sparse_vector]; " + + "mappings in index template single-type contains deprecated sparse_vector fields: " + + "[my_sparse_vector], [my_nested_sparse_vector]", false, null) + )); + } }