From 0283c25e8b73fd08ea2df6e937744320c7e9ff6b Mon Sep 17 00:00:00 2001 From: Toby Brain Date: Thu, 27 Nov 2025 23:06:04 +1100 Subject: [PATCH 1/2] Add mapping_total_fields_limit --- docs/resources/elasticsearch_index.md | 1 + .../elasticsearch/index/index/acc_test.go | 116 +++++------------- internal/elasticsearch/index/index/models.go | 2 + internal/elasticsearch/index/index/schema.go | 4 + .../TestAccResourceIndexFromSDK/index.tf | 78 ++++++++++++ .../create/index.tf | 79 ++++++++++++ .../update/index.tf | 79 ++++++++++++ 7 files changed, 275 insertions(+), 84 deletions(-) create mode 100644 internal/elasticsearch/index/index/testdata/TestAccResourceIndexFromSDK/index.tf create mode 100644 internal/elasticsearch/index/index/testdata/TestAccResourceIndexSettings/create/index.tf create mode 100644 internal/elasticsearch/index/index/testdata/TestAccResourceIndexSettings/update/index.tf diff --git a/docs/resources/elasticsearch_index.md b/docs/resources/elasticsearch_index.md index d603863c9..49050f783 100644 --- a/docs/resources/elasticsearch_index.md +++ b/docs/resources/elasticsearch_index.md @@ -85,6 +85,7 @@ resource "elasticstack_elasticsearch_index" "my_index" { - `indexing_slowlog_threshold_index_warn` (String) Set the cutoff for shard level slow search logging of slow searches for indexing queries, in time units, e.g. `10s` - `load_fixed_bitset_filters_eagerly` (Boolean) Indicates whether cached filters are pre-loaded for nested queries. This can be set only on creation. - `mapping_coerce` (Boolean) Set index level coercion setting that is applied to all mapping types. +- `mapping_total_fields_limit` (Number) The maximum number of fields in an index. Field type parameters count towards this limit. The default value is 1000. - `mappings` (String) Mapping for fields in the index. If specified, this mapping can include: field names, [field data types](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html), [mapping parameters](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-params.html). **NOTE:** diff --git a/internal/elasticsearch/index/index/acc_test.go b/internal/elasticsearch/index/index/acc_test.go index 554c10782..82206f63d 100644 --- a/internal/elasticsearch/index/index/acc_test.go +++ b/internal/elasticsearch/index/index/acc_test.go @@ -1,12 +1,14 @@ package index_test import ( + _ "embed" "fmt" "regexp" "testing" "github.com/elastic/terraform-provider-elasticstack/internal/acctest" "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/hashicorp/terraform-plugin-testing/config" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -77,6 +79,9 @@ func TestAccResourceIndex(t *testing.T) { }) } +//go:embed testdata/TestAccResourceIndexFromSDK/index.tf +var sdkCreateTestConfig string + func TestAccResourceIndexFromSDK(t *testing.T) { indexName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) @@ -92,7 +97,10 @@ func TestAccResourceIndexFromSDK(t *testing.T) { VersionConstraint: "0.11.3", }, }, - Config: testAccResourceIndexSettingsCreate(indexName), + Config: sdkCreateTestConfig, + ConfigVariables: config.Variables{ + "index_name": config.StringVariable(indexName), + }, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "name", indexName), resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "number_of_shards", "2"), @@ -132,7 +140,10 @@ func TestAccResourceIndexFromSDK(t *testing.T) { }, { ProtoV6ProviderFactories: acctest.Providers, - Config: testAccResourceIndexSettingsCreate(indexName), + ConfigDirectory: acctest.NamedTestCaseDirectory(""), + ConfigVariables: config.Variables{ + "index_name": config.StringVariable(indexName), + }, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "name", indexName), resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "number_of_shards", "2"), @@ -178,12 +189,15 @@ func TestAccResourceIndexSettings(t *testing.T) { indexName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - CheckDestroy: checkResourceIndexDestroy, - ProtoV6ProviderFactories: acctest.Providers, + PreCheck: func() { acctest.PreCheck(t) }, + CheckDestroy: checkResourceIndexDestroy, Steps: []resource.TestStep{ { - Config: testAccResourceIndexSettingsCreate(indexName), + ProtoV6ProviderFactories: acctest.Providers, + ConfigDirectory: acctest.NamedTestCaseDirectory("create"), + ConfigVariables: config.Variables{ + "index_name": config.StringVariable(indexName), + }, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "name", indexName), resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "number_of_shards", "2"), @@ -194,6 +208,7 @@ func TestAccResourceIndexSettings(t *testing.T) { resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "sort_field.0", "sort_key"), resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "sort_order.0", "asc"), resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "mapping_coerce", "true"), + resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "mapping_total_fields_limit", "2000"), resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "auto_expand_replicas", "0-5"), resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "search_idle_after", "30s"), resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "refresh_interval", "10s"), @@ -221,6 +236,17 @@ func TestAccResourceIndexSettings(t *testing.T) { resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "settings.0.setting.0.value", "2"), ), }, + { + ProtoV6ProviderFactories: acctest.Providers, + ConfigDirectory: acctest.NamedTestCaseDirectory("update"), + ConfigVariables: config.Variables{ + "index_name": config.StringVariable(indexName), + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "name", indexName), + resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test_settings", "mapping_total_fields_limit", "3000"), + ), + }, }, }) } @@ -348,84 +374,6 @@ resource "elasticstack_elasticsearch_index" "test" { `, name) } -func testAccResourceIndexSettingsCreate(name string) string { - return fmt.Sprintf(` -provider "elasticstack" { - elasticsearch {} -} - -resource "elasticstack_elasticsearch_index" "test_settings" { - name = "%s" - - mappings = jsonencode({ - properties = { - field1 = { type = "text" } - sort_key = { type = "keyword" } - } - }) - - number_of_shards = 2 - number_of_routing_shards = 2 - codec = "best_compression" - routing_partition_size = 1 - shard_check_on_startup = "false" - sort_field = ["sort_key"] - sort_order = ["asc"] - mapping_coerce = true - auto_expand_replicas = "0-5" - search_idle_after = "30s" - refresh_interval = "10s" - max_result_window = 5000 - max_inner_result_window = 2000 - max_rescore_window = 1000 - max_docvalue_fields_search = 1500 - max_script_fields = 500 - max_ngram_diff = 100 - max_shingle_diff = 200 - max_refresh_listeners = 10 - analyze_max_token_count = 500000 - highlight_max_analyzed_offset = 1000 - max_terms_count = 10000 - max_regex_length = 1000 - query_default_field = ["field1"] - routing_allocation_enable = "primaries" - routing_rebalance_enable = "primaries" - gc_deletes = "30s" - unassigned_node_left_delayed_timeout = "5m" - - analysis_char_filter = jsonencode({ - zero_width_spaces = { - type = "mapping" - mappings = ["\\u200C=>\\u0020"] - } - }) - analysis_filter = jsonencode({ - minimal_english_stemmer = { - type = "stemmer" - language = "minimal_english" - } - }) - analysis_analyzer = jsonencode({ - text_en = { - type = "custom" - tokenizer = "standard" - char_filter = "zero_width_spaces" - filter = ["lowercase", "minimal_english_stemmer"] - } - }) - - settings { - setting { - name = "number_of_replicas" - value = "2" - } - } - - deletion_protection = false -} - `, name) -} - func testAccResourceIndexRemovingFieldCreate(name string) string { return fmt.Sprintf(` provider "elasticstack" { diff --git a/internal/elasticsearch/index/index/models.go b/internal/elasticsearch/index/index/models.go index 0a8c7de99..df9ba78df 100644 --- a/internal/elasticsearch/index/index/models.go +++ b/internal/elasticsearch/index/index/models.go @@ -36,6 +36,7 @@ var ( "auto_expand_replicas", "refresh_interval", "search.idle.after", + "mapping.total_fields.limit", "max_result_window", "max_inner_result_window", "max_rescore_window", @@ -97,6 +98,7 @@ type tfModel struct { SortField types.Set `tfsdk:"sort_field"` SortOrder types.List `tfsdk:"sort_order"` MappingCoerce types.Bool `tfsdk:"mapping_coerce"` + MappingTotalFieldsLimit types.Int64 `tfsdk:"mapping_total_fields_limit"` NumberOfReplicas types.Int64 `tfsdk:"number_of_replicas"` AutoExpandReplicas types.String `tfsdk:"auto_expand_replicas"` SearchIdleAfter types.String `tfsdk:"search_idle_after"` diff --git a/internal/elasticsearch/index/index/schema.go b/internal/elasticsearch/index/index/schema.go index a0a8a4bb7..00fb481bc 100644 --- a/internal/elasticsearch/index/index/schema.go +++ b/internal/elasticsearch/index/index/schema.go @@ -226,6 +226,10 @@ func getSchema() schema.Schema { }, }, // Dynamic settings that can be changed at runtime + "mapping_total_fields_limit": schema.Int64Attribute{ + Description: "The maximum number of fields in an index. Field type parameters count towards this limit. The default value is 1000.", + Optional: true, + }, "number_of_replicas": schema.Int64Attribute{ Description: "Number of shard replicas.", Optional: true, diff --git a/internal/elasticsearch/index/index/testdata/TestAccResourceIndexFromSDK/index.tf b/internal/elasticsearch/index/index/testdata/TestAccResourceIndexFromSDK/index.tf new file mode 100644 index 000000000..4cd3fb3ee --- /dev/null +++ b/internal/elasticsearch/index/index/testdata/TestAccResourceIndexFromSDK/index.tf @@ -0,0 +1,78 @@ +variable "index_name" { + description = "The index name" + type = string +} + +provider "elasticstack" { + elasticsearch {} +} + +resource "elasticstack_elasticsearch_index" "test_settings" { + name = var.index_name + + mappings = jsonencode({ + properties = { + field1 = { type = "text" } + sort_key = { type = "keyword" } + } + }) + + number_of_shards = 2 + number_of_routing_shards = 2 + codec = "best_compression" + routing_partition_size = 1 + shard_check_on_startup = "false" + sort_field = ["sort_key"] + sort_order = ["asc"] + mapping_coerce = true + auto_expand_replicas = "0-5" + search_idle_after = "30s" + refresh_interval = "10s" + max_result_window = 5000 + max_inner_result_window = 2000 + max_rescore_window = 1000 + max_docvalue_fields_search = 1500 + max_script_fields = 500 + max_ngram_diff = 100 + max_shingle_diff = 200 + max_refresh_listeners = 10 + analyze_max_token_count = 500000 + highlight_max_analyzed_offset = 1000 + max_terms_count = 10000 + max_regex_length = 1000 + query_default_field = ["field1"] + routing_allocation_enable = "primaries" + routing_rebalance_enable = "primaries" + gc_deletes = "30s" + unassigned_node_left_delayed_timeout = "5m" + + analysis_char_filter = jsonencode({ + zero_width_spaces = { + type = "mapping" + mappings = ["\\u200C=>\\u0020"] + } + }) + analysis_filter = jsonencode({ + minimal_english_stemmer = { + type = "stemmer" + language = "minimal_english" + } + }) + analysis_analyzer = jsonencode({ + text_en = { + type = "custom" + tokenizer = "standard" + char_filter = "zero_width_spaces" + filter = ["lowercase", "minimal_english_stemmer"] + } + }) + + settings { + setting { + name = "number_of_replicas" + value = "2" + } + } + + deletion_protection = false +} diff --git a/internal/elasticsearch/index/index/testdata/TestAccResourceIndexSettings/create/index.tf b/internal/elasticsearch/index/index/testdata/TestAccResourceIndexSettings/create/index.tf new file mode 100644 index 000000000..719149886 --- /dev/null +++ b/internal/elasticsearch/index/index/testdata/TestAccResourceIndexSettings/create/index.tf @@ -0,0 +1,79 @@ +variable "index_name" { + description = "The index name" + type = string +} + +provider "elasticstack" { + elasticsearch {} +} + +resource "elasticstack_elasticsearch_index" "test_settings" { + name = var.index_name + + mappings = jsonencode({ + properties = { + field1 = { type = "text" } + sort_key = { type = "keyword" } + } + }) + + number_of_shards = 2 + number_of_routing_shards = 2 + codec = "best_compression" + routing_partition_size = 1 + shard_check_on_startup = "false" + sort_field = ["sort_key"] + sort_order = ["asc"] + mapping_coerce = true + mapping_total_fields_limit = 2000 + auto_expand_replicas = "0-5" + search_idle_after = "30s" + refresh_interval = "10s" + max_result_window = 5000 + max_inner_result_window = 2000 + max_rescore_window = 1000 + max_docvalue_fields_search = 1500 + max_script_fields = 500 + max_ngram_diff = 100 + max_shingle_diff = 200 + max_refresh_listeners = 10 + analyze_max_token_count = 500000 + highlight_max_analyzed_offset = 1000 + max_terms_count = 10000 + max_regex_length = 1000 + query_default_field = ["field1"] + routing_allocation_enable = "primaries" + routing_rebalance_enable = "primaries" + gc_deletes = "30s" + unassigned_node_left_delayed_timeout = "5m" + + analysis_char_filter = jsonencode({ + zero_width_spaces = { + type = "mapping" + mappings = ["\\u200C=>\\u0020"] + } + }) + analysis_filter = jsonencode({ + minimal_english_stemmer = { + type = "stemmer" + language = "minimal_english" + } + }) + analysis_analyzer = jsonencode({ + text_en = { + type = "custom" + tokenizer = "standard" + char_filter = "zero_width_spaces" + filter = ["lowercase", "minimal_english_stemmer"] + } + }) + + settings { + setting { + name = "number_of_replicas" + value = "2" + } + } + + deletion_protection = false +} diff --git a/internal/elasticsearch/index/index/testdata/TestAccResourceIndexSettings/update/index.tf b/internal/elasticsearch/index/index/testdata/TestAccResourceIndexSettings/update/index.tf new file mode 100644 index 000000000..30df6c180 --- /dev/null +++ b/internal/elasticsearch/index/index/testdata/TestAccResourceIndexSettings/update/index.tf @@ -0,0 +1,79 @@ +variable "index_name" { + description = "The index name" + type = string +} + +provider "elasticstack" { + elasticsearch {} +} + +resource "elasticstack_elasticsearch_index" "test_settings" { + name = var.index_name + + mappings = jsonencode({ + properties = { + field1 = { type = "text" } + sort_key = { type = "keyword" } + } + }) + + number_of_shards = 2 + number_of_routing_shards = 2 + codec = "best_compression" + routing_partition_size = 1 + shard_check_on_startup = "false" + sort_field = ["sort_key"] + sort_order = ["asc"] + mapping_coerce = true + mapping_total_fields_limit = 3000 + auto_expand_replicas = "0-5" + search_idle_after = "30s" + refresh_interval = "10s" + max_result_window = 5000 + max_inner_result_window = 2000 + max_rescore_window = 1000 + max_docvalue_fields_search = 1500 + max_script_fields = 500 + max_ngram_diff = 100 + max_shingle_diff = 200 + max_refresh_listeners = 10 + analyze_max_token_count = 500000 + highlight_max_analyzed_offset = 1000 + max_terms_count = 10000 + max_regex_length = 1000 + query_default_field = ["field1"] + routing_allocation_enable = "primaries" + routing_rebalance_enable = "primaries" + gc_deletes = "30s" + unassigned_node_left_delayed_timeout = "5m" + + analysis_char_filter = jsonencode({ + zero_width_spaces = { + type = "mapping" + mappings = ["\\u200C=>\\u0020"] + } + }) + analysis_filter = jsonencode({ + minimal_english_stemmer = { + type = "stemmer" + language = "minimal_english" + } + }) + analysis_analyzer = jsonencode({ + text_en = { + type = "custom" + tokenizer = "standard" + char_filter = "zero_width_spaces" + filter = ["lowercase", "minimal_english_stemmer"] + } + }) + + settings { + setting { + name = "number_of_replicas" + value = "2" + } + } + + deletion_protection = false +} From 583b2590b2e52031cbab25e537a2168d15682a07 Mon Sep 17 00:00:00 2001 From: Toby Brain Date: Thu, 27 Nov 2025 23:14:27 +1100 Subject: [PATCH 2/2] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a09de02a..e6c5d1278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ alias = [ - Support `.bedrock` and `.gen-ai` connectors ([#1467](https://github.com/elastic/terraform-provider-elasticstack/pull/1467)) - Support the `solution` attribute in `elasticstack_kibana_space` from 8.16 ([#1486](https://github.com/elastic/terraform-provider-elasticstack/pull/1486)) - Add `elasticstack_elasticsearch_alias` resource ([#1343](https://github.com/elastic/terraform-provider-elasticstack/pull/1343)) +- Add `mapping_total_fields_limit` to `elasticstack_elasticsearch_index` ([#1494](https://github.com/elastic/terraform-provider-elasticstack/pull/1494)) ## [0.12.2] - 2025-11-19 - Fix `elasticstack_elasticsearch_snapshot_lifecycle` metadata type conversion causing terraform apply to fail ([#1409](https://github.com/elastic/terraform-provider-elasticstack/issues/1409))