From 11111e2d3daa9d3b54c956cde316766b8d7e2fdd Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 30 Mar 2026 18:25:25 +0200 Subject: [PATCH 1/5] Search: Include synonym ID in synonym rule values The synonym dictionary uses the first term as the key and remaining terms as values. When publishing to Elasticsearch, only the values were included in the synonym string, omitting the key term itself. This meant e.g. "ilm" was the rule ID but not part of the synonym set, so it wouldn't match "index lifecycle management". Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs index 2899fefba..748c2a683 100644 --- a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs @@ -238,7 +238,8 @@ private async Task PublishSynonymsAsync(Cancel ctx) var synonymRules = _synonyms.Aggregate(new List(), (acc, synonym) => { var id = synonym.Key; - acc.Add(new SynonymRule { Id = id, Synonyms = string.Join(", ", synonym.Value) }); + var synonyms = string.Join(", ", [id, .. synonym.Value]); + acc.Add(new SynonymRule { Id = id, Synonyms = synonyms }); return acc; }); From 5cea709f22f7bb6feba2ad6a623fcd70dfb020f9 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 30 Mar 2026 18:25:39 +0200 Subject: [PATCH 2/5] Search: Change synonyms from dictionary to flat list The dictionary structure used the first term as the key and Skip(1) for values, which artificially separated one synonym from its group. This caused the key term to be omitted from synonym rules sent to Elasticsearch. Replace Dictionary with IReadOnlyList so all terms in a synonym group are treated equally. The first term is still used as the ES rule ID for readability but is no longer excluded from the synonym string. Co-Authored-By: Claude Opus 4.6 (1M context) --- .superset/config.json | 7 ++++++ .../Search/SearchConfiguration.cs | 17 +++++++------- .../ElasticsearchMarkdownExporter.cs | 22 +++++++------------ .../TestHelpers.cs | 2 +- .../McpToolsIntegrationTestsBase.cs | 2 +- .../Elastic.ApiExplorer.Tests/TestHelpers.cs | 2 +- .../Changelogs/ChangelogTestBase.cs | 2 +- .../TestHelpers.cs | 2 +- tests/Elastic.Markdown.Tests/TestHelpers.cs | 2 +- 9 files changed, 30 insertions(+), 28 deletions(-) create mode 100644 .superset/config.json diff --git a/.superset/config.json b/.superset/config.json new file mode 100644 index 000000000..2f285f8a8 --- /dev/null +++ b/.superset/config.json @@ -0,0 +1,7 @@ +{ + "setup": [ + "dotnet tool restore && dotnet husky install" + ], + "teardown": [], + "run": [] +} \ No newline at end of file diff --git a/src/Elastic.Documentation.Configuration/Search/SearchConfiguration.cs b/src/Elastic.Documentation.Configuration/Search/SearchConfiguration.cs index ae52726af..b75501bd7 100644 --- a/src/Elastic.Documentation.Configuration/Search/SearchConfiguration.cs +++ b/src/Elastic.Documentation.Configuration/Search/SearchConfiguration.cs @@ -9,9 +9,9 @@ namespace Elastic.Documentation.Configuration.Search; public record SearchConfiguration { - private readonly IReadOnlyDictionary _synonyms; + private readonly IReadOnlyList _synonyms; - public required IReadOnlyDictionary Synonyms + public required IReadOnlyList Synonyms { get => _synonyms; [MemberNotNull(nameof(_synonyms))] @@ -19,7 +19,6 @@ public required IReadOnlyDictionary Synonyms { _synonyms = value; SynonymBiDirectional = value - .Select(kv => kv.Value.Concat([kv.Key]).ToArray()) .SelectMany(a => { var targets = new List(); @@ -120,15 +119,17 @@ public static class SearchConfigurationExtensions public static SearchConfiguration CreateSearchConfiguration(this ConfigurationFileProvider provider) { var searchFile = provider.SearchFile; - var synonyms = new Dictionary(); if (!searchFile.Exists) - return new SearchConfiguration { Synonyms = synonyms, Rules = [], DiminishTerms = [] }; + return new SearchConfiguration { Synonyms = [], Rules = [], DiminishTerms = [] }; var searchDto = ConfigurationFileProvider.Deserializer.Deserialize(searchFile.OpenText()); - synonyms = searchDto.Synonyms + var synonyms = searchDto.Synonyms .Where(s => s.Count > 1) - .ToDictionary(k => k[0], sl => sl.Skip(1).ToArray(), StringComparer.OrdinalIgnoreCase); + .Select(s => s.ToArray()) + .GroupBy(s => s[0], StringComparer.OrdinalIgnoreCase) + .Select(g => g.First()) + .ToArray(); var rules = searchDto.Rules.Select(ParseRule).ToImmutableArray(); var diminishTerms = searchDto.DiminishTerms.ToImmutableArray(); return new SearchConfiguration { Synonyms = synonyms, Rules = rules, DiminishTerms = diminishTerms }; @@ -154,4 +155,4 @@ private static QueryRuleCriteria ParseCriteria(QueryRuleCriteriaDto dto) => Metadata = dto.Metadata, Values = dto.Values.ToImmutableArray() }; -} +} \ No newline at end of file diff --git a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs index 748c2a683..b62d7be12 100644 --- a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs @@ -38,7 +38,7 @@ public partial class ElasticsearchMarkdownExporter : IMarkdownExporter, IDisposa private readonly ElasticsearchTypeContext _semanticTypeContext; private readonly VersionsConfiguration _versionsConfiguration; - private readonly IReadOnlyDictionary _synonyms; + private readonly IReadOnlyList _synonyms; private readonly IReadOnlyCollection _rules; private readonly string _fixedSynonymsHash; @@ -74,12 +74,10 @@ IDocumentationConfigurationContext context _operations = new ElasticsearchOperations(_transport, _logger, collector); string[] fixedSynonyms = ["esql", "data-stream", "data-streams", "machine-learning"]; - var indexTimeSynonyms = _synonyms.Aggregate(new List(), (acc, synonym) => - { - var id = synonym.Key; - acc.Add(new SynonymRule { Id = id, Synonyms = string.Join(", ", synonym.Value) }); - return acc; - }).Where(r => fixedSynonyms.Contains(r.Id)).Select(r => r.Synonyms).ToArray(); + var indexTimeSynonyms = _synonyms + .Where(s => s.Any(t => fixedSynonyms.Contains(t))) + .Select(s => string.Join(", ", s)) + .ToArray(); _fixedSynonymsHash = HashedBulkUpdate.CreateHash(string.Join(",", indexTimeSynonyms)); var synonymSetName = $"docs-{_buildType}-{_environment}"; @@ -235,13 +233,9 @@ private async Task PublishSynonymsAsync(Cancel ctx) var setName = $"docs-{_buildType}-{_environment}"; _logger.LogInformation("Publishing synonym set '{SetName}' to Elasticsearch", setName); - var synonymRules = _synonyms.Aggregate(new List(), (acc, synonym) => - { - var id = synonym.Key; - var synonyms = string.Join(", ", [id, .. synonym.Value]); - acc.Add(new SynonymRule { Id = id, Synonyms = synonyms }); - return acc; - }); + var synonymRules = _synonyms + .Select(s => new SynonymRule { Id = s[0], Synonyms = string.Join(", ", s) }) + .ToList(); var synonymsSet = new SynonymsSet { Synonyms = synonymRules }; await PutSynonyms(synonymsSet, setName, ctx); diff --git a/tests-integration/Elastic.Assembler.IntegrationTests/TestHelpers.cs b/tests-integration/Elastic.Assembler.IntegrationTests/TestHelpers.cs index 7de28f21b..473dfae09 100644 --- a/tests-integration/Elastic.Assembler.IntegrationTests/TestHelpers.cs +++ b/tests-integration/Elastic.Assembler.IntegrationTests/TestHelpers.cs @@ -57,7 +57,7 @@ public static IConfigurationContext CreateConfigurationContext( ProductDisplayNames = products.ToDictionary(p => p.Key, p => p.Value.DisplayName).ToFrozenDictionary() }; } - var search = new SearchConfiguration { Synonyms = new Dictionary(), Rules = [], DiminishTerms = [] }; + var search = new SearchConfiguration { Synonyms = [], Rules = [], DiminishTerms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints diff --git a/tests-integration/Mcp.Remote.IntegrationTests/McpToolsIntegrationTestsBase.cs b/tests-integration/Mcp.Remote.IntegrationTests/McpToolsIntegrationTestsBase.cs index a494d6dff..d2a3b48ea 100644 --- a/tests-integration/Mcp.Remote.IntegrationTests/McpToolsIntegrationTestsBase.cs +++ b/tests-integration/Mcp.Remote.IntegrationTests/McpToolsIntegrationTestsBase.cs @@ -92,7 +92,7 @@ private static ElasticsearchClientAccessor CreateElasticsearchClientAccessor() var searchConfig = new SearchConfiguration { - Synonyms = new Dictionary(), + Synonyms = [], Rules = [], DiminishTerms = ["plugin", "client", "integration", "glossary"] }; diff --git a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs index 25cbfb10c..eb7478d0f 100644 --- a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs +++ b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs @@ -53,7 +53,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS ProductDisplayNames = products.ToDictionary(p => p.Key, p => p.Value.DisplayName).ToFrozenDictionary() }; } - var search = new SearchConfiguration { Synonyms = new Dictionary(), Rules = [], DiminishTerms = [] }; + var search = new SearchConfiguration { Synonyms = [], Rules = [], DiminishTerms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints diff --git a/tests/Elastic.Changelog.Tests/Changelogs/ChangelogTestBase.cs b/tests/Elastic.Changelog.Tests/Changelogs/ChangelogTestBase.cs index 1bc0e1eb7..c6453ce1f 100644 --- a/tests/Elastic.Changelog.Tests/Changelogs/ChangelogTestBase.cs +++ b/tests/Elastic.Changelog.Tests/Changelogs/ChangelogTestBase.cs @@ -103,7 +103,7 @@ protected ChangelogTestBase(ITestOutputHelper output) ConfigurationFileProvider = new ConfigurationFileProvider(NullLoggerFactory.Instance, FileSystem), VersionsConfiguration = versionsConfiguration, ProductsConfiguration = productsConfiguration, - SearchConfiguration = new SearchConfiguration { Synonyms = new Dictionary(), Rules = [], DiminishTerms = [] }, + SearchConfiguration = new SearchConfiguration { Synonyms = [], Rules = [], DiminishTerms = [] }, LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, }; } diff --git a/tests/Elastic.Documentation.Build.Tests/TestHelpers.cs b/tests/Elastic.Documentation.Build.Tests/TestHelpers.cs index c36142c84..0a1bacfc2 100644 --- a/tests/Elastic.Documentation.Build.Tests/TestHelpers.cs +++ b/tests/Elastic.Documentation.Build.Tests/TestHelpers.cs @@ -62,7 +62,7 @@ public static IConfigurationContext CreateConfigurationContext( }; } - var search = new SearchConfiguration { Synonyms = new Dictionary(), Rules = [], DiminishTerms = [] }; + var search = new SearchConfiguration { Synonyms = [], Rules = [], DiminishTerms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints diff --git a/tests/Elastic.Markdown.Tests/TestHelpers.cs b/tests/Elastic.Markdown.Tests/TestHelpers.cs index 92919b221..99adaad4b 100644 --- a/tests/Elastic.Markdown.Tests/TestHelpers.cs +++ b/tests/Elastic.Markdown.Tests/TestHelpers.cs @@ -106,7 +106,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS ProductDisplayNames = products.ToDictionary(p => p.Key, p => p.Value.DisplayName).ToFrozenDictionary() }; } - var search = new SearchConfiguration { Synonyms = new Dictionary(), Rules = [], DiminishTerms = [] }; + var search = new SearchConfiguration { Synonyms = [], Rules = [], DiminishTerms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints From 784cd25a20290ada6c9ec5c43ec35fa26e3b0404 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Tue, 14 Apr 2026 10:46:08 +0200 Subject: [PATCH 3/5] Add .superset to gitignore and remove from tracking Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 1 + .superset/config.json | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 .superset/config.json diff --git a/.gitignore b/.gitignore index 88656a307..6e1ac1186 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore .artifacts +.superset .claude/* !.claude/settings.json !.claude/skills/ diff --git a/.superset/config.json b/.superset/config.json deleted file mode 100644 index 2f285f8a8..000000000 --- a/.superset/config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "setup": [ - "dotnet tool restore && dotnet husky install" - ], - "teardown": [], - "run": [] -} \ No newline at end of file From fb51bb9d6f681a0fd0d57e29ae6cff7aec0c3471 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Tue, 14 Apr 2026 10:53:59 +0200 Subject: [PATCH 4/5] Search: Fix F# test to use IReadOnlyList for synonyms The synonyms type was changed from Dictionary to IReadOnlyList in 5cea709f2 but the F# authoring test was not updated. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/authoring/Framework/Setup.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/authoring/Framework/Setup.fs b/tests/authoring/Framework/Setup.fs index 33bb1f2f5..e35e0d320 100644 --- a/tests/authoring/Framework/Setup.fs +++ b/tests/authoring/Framework/Setup.fs @@ -301,7 +301,7 @@ type Setup = Products = productDict.ToFrozenDictionary(), ProductDisplayNames = (productDict |> Seq.map (fun p -> KeyValuePair(p.Key, p.Value.DisplayName)) |> fun s -> Dictionary(s)).ToFrozenDictionary()), LegacyUrlMappings = LegacyUrlMappingConfiguration(Mappings = []), - SearchConfiguration = SearchConfiguration(Synonyms = Dictionary(), Rules = [], DiminishTerms = []) + SearchConfiguration = SearchConfiguration(Synonyms = Array.empty, Rules = [], DiminishTerms = []) ) let context = BuildContext( collector, From f923e66ff156f613022aeddb3e1e8c96146c4183 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Tue, 14 Apr 2026 10:57:17 +0200 Subject: [PATCH 5/5] Search: Add missing final newline to SearchConfiguration.cs Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Search/SearchConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Documentation.Configuration/Search/SearchConfiguration.cs b/src/Elastic.Documentation.Configuration/Search/SearchConfiguration.cs index b75501bd7..55c102af0 100644 --- a/src/Elastic.Documentation.Configuration/Search/SearchConfiguration.cs +++ b/src/Elastic.Documentation.Configuration/Search/SearchConfiguration.cs @@ -155,4 +155,4 @@ private static QueryRuleCriteria ParseCriteria(QueryRuleCriteriaDto dto) => Metadata = dto.Metadata, Values = dto.Values.ToImmutableArray() }; -} \ No newline at end of file +}