|
12 | 12 | import org.elasticsearch.ElasticsearchStatusException; |
13 | 13 | import org.elasticsearch.ExceptionsHelper; |
14 | 14 | import org.elasticsearch.action.ActionFuture; |
| 15 | +import org.elasticsearch.action.ActionType; |
15 | 16 | import org.elasticsearch.action.DocWriteRequest; |
16 | 17 | import org.elasticsearch.action.DocWriteResponse; |
17 | 18 | import org.elasticsearch.action.RequestBuilder; |
|
41 | 42 | import org.elasticsearch.action.admin.indices.stats.ShardStats; |
42 | 43 | import org.elasticsearch.action.admin.indices.template.delete.TransportDeleteComposableIndexTemplateAction; |
43 | 44 | import org.elasticsearch.action.admin.indices.template.get.GetComposableIndexTemplateAction; |
| 45 | +import org.elasticsearch.action.admin.indices.template.put.PutComponentTemplateAction; |
44 | 46 | import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; |
45 | 47 | import org.elasticsearch.action.admin.indices.template.put.TransportPutComposableIndexTemplateAction; |
46 | 48 | import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequestBuilder; |
|
52 | 54 | import org.elasticsearch.action.datastreams.GetDataStreamAction; |
53 | 55 | import org.elasticsearch.action.datastreams.GetDataStreamAction.Response.DataStreamInfo; |
54 | 56 | import org.elasticsearch.action.datastreams.ModifyDataStreamsAction; |
| 57 | +import org.elasticsearch.action.datastreams.UpdateDataStreamMappingsAction; |
55 | 58 | import org.elasticsearch.action.delete.DeleteRequest; |
56 | 59 | import org.elasticsearch.action.index.IndexRequest; |
57 | 60 | import org.elasticsearch.action.search.MultiSearchRequestBuilder; |
|
62 | 65 | import org.elasticsearch.cluster.ClusterStateUpdateTask; |
63 | 66 | import org.elasticsearch.cluster.health.ClusterHealthStatus; |
64 | 67 | import org.elasticsearch.cluster.metadata.AliasMetadata; |
| 68 | +import org.elasticsearch.cluster.metadata.ComponentTemplate; |
65 | 69 | import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; |
66 | 70 | import org.elasticsearch.cluster.metadata.DataStream; |
67 | 71 | import org.elasticsearch.cluster.metadata.DataStreamAction; |
|
72 | 76 | import org.elasticsearch.cluster.metadata.IndexMetadataStats; |
73 | 77 | import org.elasticsearch.cluster.metadata.IndexWriteLoad; |
74 | 78 | import org.elasticsearch.cluster.metadata.Metadata; |
| 79 | +import org.elasticsearch.cluster.metadata.ProjectId; |
| 80 | +import org.elasticsearch.cluster.metadata.ProjectMetadata; |
75 | 81 | import org.elasticsearch.cluster.metadata.Template; |
76 | 82 | import org.elasticsearch.cluster.routing.IndexRoutingTable; |
77 | 83 | import org.elasticsearch.cluster.routing.ShardRouting; |
78 | 84 | import org.elasticsearch.cluster.service.ClusterService; |
79 | 85 | import org.elasticsearch.common.Strings; |
80 | 86 | import org.elasticsearch.common.compress.CompressedXContent; |
81 | 87 | import org.elasticsearch.common.settings.Settings; |
| 88 | +import org.elasticsearch.common.xcontent.XContentHelper; |
82 | 89 | import org.elasticsearch.core.Nullable; |
83 | 90 | import org.elasticsearch.index.Index; |
84 | 91 | import org.elasticsearch.index.IndexNotFoundException; |
85 | 92 | import org.elasticsearch.index.mapper.DataStreamTimestampFieldMapper; |
86 | 93 | import org.elasticsearch.index.mapper.DateFieldMapper; |
87 | 94 | import org.elasticsearch.index.query.TermQueryBuilder; |
88 | 95 | import org.elasticsearch.index.shard.IndexingStats; |
| 96 | +import org.elasticsearch.indices.IndicesService; |
89 | 97 | import org.elasticsearch.indices.InvalidAliasNameException; |
90 | 98 | import org.elasticsearch.indices.InvalidIndexNameException; |
91 | 99 | import org.elasticsearch.plugins.Plugin; |
@@ -2411,6 +2419,162 @@ public void testShardSizeIsForecastedDuringRollover() throws Exception { |
2411 | 2419 | assertThat(forecastedShardSizeInBytes.getAsLong(), is(equalTo(expectedTotalSizeInBytes / shardCount))); |
2412 | 2420 | } |
2413 | 2421 |
|
| 2422 | + public void testGetEffectiveMappings() throws Exception { |
| 2423 | + /* |
| 2424 | + * This test creates a composable template with a mapping and with two component templates with mappings. It then makes sure that |
| 2425 | + * DataStream.getEffectiveMappings returns a mapping that merges the template's mapping, the component templates' mappings, and the |
| 2426 | + * mapping override given. It then makes sure we get the same result calling the non-static version of getEffectiveMappings. |
| 2427 | + */ |
| 2428 | + ComposableIndexTemplate composableIndexTemplate; |
| 2429 | + { |
| 2430 | + ComponentTemplate ct1 = new ComponentTemplate(new Template(null, new CompressedXContent(""" |
| 2431 | + { |
| 2432 | + "_doc":{ |
| 2433 | + "dynamic":"strict", |
| 2434 | + "properties":{ |
| 2435 | + "field1":{ |
| 2436 | + "type":"text" |
| 2437 | + } |
| 2438 | + } |
| 2439 | + } |
| 2440 | + } |
| 2441 | + """), null), 3L, null); |
| 2442 | + ComponentTemplate ct2 = new ComponentTemplate(new Template(null, new CompressedXContent(""" |
| 2443 | + { |
| 2444 | + "_doc":{ |
| 2445 | + "dynamic":"strict", |
| 2446 | + "properties":{ |
| 2447 | + "field2":{ |
| 2448 | + "type":"text" |
| 2449 | + } |
| 2450 | + } |
| 2451 | + } |
| 2452 | + } |
| 2453 | + """), null), 3L, null); |
| 2454 | + client().execute(PutComponentTemplateAction.INSTANCE, new PutComponentTemplateAction.Request("ct1").componentTemplate(ct1)) |
| 2455 | + .get(); |
| 2456 | + client().execute(PutComponentTemplateAction.INSTANCE, new PutComponentTemplateAction.Request("ct2").componentTemplate(ct2)) |
| 2457 | + .get(); |
| 2458 | + |
| 2459 | + List<String> componentTemplates = List.of("ct1", "ct2"); |
| 2460 | + String templateName = "effective-mapping-template"; |
| 2461 | + TransportPutComposableIndexTemplateAction.Request request = new TransportPutComposableIndexTemplateAction.Request(templateName); |
| 2462 | + request.indexTemplate( |
| 2463 | + ComposableIndexTemplate.builder() |
| 2464 | + .indexPatterns(List.of("effective-*")) |
| 2465 | + .template(Template.builder().mappings(CompressedXContent.fromJSON(""" |
| 2466 | + { |
| 2467 | + "_doc":{ |
| 2468 | + "dynamic":"strict", |
| 2469 | + "properties":{ |
| 2470 | + "field3":{ |
| 2471 | + "type":"text" |
| 2472 | + } |
| 2473 | + } |
| 2474 | + } |
| 2475 | + } |
| 2476 | + """))) |
| 2477 | + .componentTemplates(componentTemplates) |
| 2478 | + .dataStreamTemplate(new ComposableIndexTemplate.DataStreamTemplate()) |
| 2479 | + .build() |
| 2480 | + ); |
| 2481 | + client().execute(TransportPutComposableIndexTemplateAction.TYPE, request).actionGet(); |
| 2482 | + GetComposableIndexTemplateAction.Response getTemplateResponse = client().execute( |
| 2483 | + GetComposableIndexTemplateAction.INSTANCE, |
| 2484 | + new GetComposableIndexTemplateAction.Request(TEST_REQUEST_TIMEOUT, templateName) |
| 2485 | + ).actionGet(); |
| 2486 | + composableIndexTemplate = getTemplateResponse.indexTemplates().values().iterator().next(); |
| 2487 | + } |
| 2488 | + // The mappingOverrides changes the type of one field, and adds another field: |
| 2489 | + CompressedXContent mappingOverrides = new CompressedXContent(""" |
| 2490 | + { |
| 2491 | + "properties":{ |
| 2492 | + "field1":{ |
| 2493 | + "type":"keyword" |
| 2494 | + }, |
| 2495 | + "field4":{ |
| 2496 | + "type":"keyword" |
| 2497 | + } |
| 2498 | + } |
| 2499 | + } |
| 2500 | + """); |
| 2501 | + |
| 2502 | + String dataStreamName = "effective-mappings-test"; |
| 2503 | + Index writeIndex; |
| 2504 | + { |
| 2505 | + CreateDataStreamAction.Request createDataStreamRequest = new CreateDataStreamAction.Request( |
| 2506 | + TEST_REQUEST_TIMEOUT, |
| 2507 | + TEST_REQUEST_TIMEOUT, |
| 2508 | + dataStreamName |
| 2509 | + ); |
| 2510 | + client().execute(CreateDataStreamAction.INSTANCE, createDataStreamRequest).get(); |
| 2511 | + writeIndex = getDataStream(dataStreamName).getWriteIndex(); |
| 2512 | + } |
| 2513 | + |
| 2514 | + ProjectMetadata projectMetadata = client().admin() |
| 2515 | + .cluster() |
| 2516 | + .state(new ClusterStateRequest(TEST_REQUEST_TIMEOUT)) |
| 2517 | + .get() |
| 2518 | + .getState() |
| 2519 | + .metadata() |
| 2520 | + .getProject(ProjectId.DEFAULT); |
| 2521 | + IndicesService indicesService = internalCluster().getInstance(IndicesService.class); |
| 2522 | + CompressedXContent effectiveMappings = DataStream.getEffectiveMappings( |
| 2523 | + projectMetadata, |
| 2524 | + composableIndexTemplate, |
| 2525 | + mappingOverrides, |
| 2526 | + writeIndex, |
| 2527 | + indicesService |
| 2528 | + ); |
| 2529 | + assertNotNull(effectiveMappings); |
| 2530 | + Map<String, Object> effectiveMappingMap = XContentHelper.convertToMap(effectiveMappings.uncompressed(), true, XContentType.JSON) |
| 2531 | + .v2(); |
| 2532 | + Map<String, Object> expectedEffectiveMappingMap = Map.of( |
| 2533 | + "_doc", |
| 2534 | + Map.of( |
| 2535 | + "dynamic", |
| 2536 | + "strict", |
| 2537 | + "_data_stream_timestamp", |
| 2538 | + Map.of("enabled", true), |
| 2539 | + "properties", |
| 2540 | + Map.of( |
| 2541 | + "@timestamp", |
| 2542 | + Map.of("type", "date"), |
| 2543 | + "field1", |
| 2544 | + Map.of("type", "keyword"), |
| 2545 | + "field2", |
| 2546 | + Map.of("type", "text"), |
| 2547 | + "field3", |
| 2548 | + Map.of("type", "text"), |
| 2549 | + "field4", |
| 2550 | + Map.of("type", "keyword") |
| 2551 | + ) |
| 2552 | + ) |
| 2553 | + ); |
| 2554 | + assertThat(effectiveMappingMap, equalTo(expectedEffectiveMappingMap)); |
| 2555 | + |
| 2556 | + // Add the same mappingOverrides to the data stream: |
| 2557 | + client().execute( |
| 2558 | + new ActionType<UpdateDataStreamMappingsAction.Response>(UpdateDataStreamMappingsAction.NAME), |
| 2559 | + new UpdateDataStreamMappingsAction.Request(mappingOverrides, false, TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT).indices( |
| 2560 | + dataStreamName |
| 2561 | + ) |
| 2562 | + ).actionGet(); |
| 2563 | + assertThat(getDataStream(dataStreamName).getEffectiveMappings(projectMetadata, indicesService), equalTo(effectiveMappings)); |
| 2564 | + } |
| 2565 | + |
| 2566 | + private DataStream getDataStream(String dataStreamName) throws ExecutionException, InterruptedException { |
| 2567 | + return client().admin() |
| 2568 | + .cluster() |
| 2569 | + .state(new ClusterStateRequest(TEST_REQUEST_TIMEOUT)) |
| 2570 | + .get() |
| 2571 | + .getState() |
| 2572 | + .getMetadata() |
| 2573 | + .getProject(ProjectId.DEFAULT) |
| 2574 | + .dataStreams() |
| 2575 | + .get(dataStreamName); |
| 2576 | + } |
| 2577 | + |
2414 | 2578 | private void indexDocsAndEnsureThereIsCapturedWriteLoad(String dataStreamName) throws Exception { |
2415 | 2579 | assertBusy(() -> { |
2416 | 2580 | for (int i = 0; i < 10; i++) { |
|
0 commit comments