From 9e478f41e43ea074dfaecfc0041b69f11e66a3cc Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 22 Oct 2025 04:09:34 -0300 Subject: [PATCH 1/8] Introduce synonyms.yaml to standardize declarations. Apply synonyms via a PUT request. --- config/synonyms.yml | 24 +++++++++++ docs-builder.sln | 1 + .../BuildContext.cs | 3 ++ .../ConfigurationFileProvider.cs | 4 +- .../IDocumentationConfigurationContext.cs | 5 +++ .../Serialization/YamlStaticContext.cs | 2 + .../Synonyms/SynonymsConfiguration.cs | 25 ++++++++++++ .../AppDefaultsExtensions.cs | 3 ++ .../Serialization/SourceGenerationContext.cs | 1 + .../Elasticsearch/ElasticsearchExporter.cs | 15 ++++--- .../ElasticsearchMarkdownExporter.cs | 40 ++++++++++++++++++- .../Exporters/ExporterExtensions.cs | 2 +- .../AssembleContext.cs | 3 ++ .../DocumentationTooling.cs | 5 ++- .../Elastic.ApiExplorer.Tests/TestHelpers.cs | 5 ++- tests/Elastic.Markdown.Tests/TestHelpers.cs | 5 ++- tests/authoring/Framework/Setup.fs | 4 +- .../src/docs-assembler.Tests/TestHelpers.cs | 6 ++- 18 files changed, 136 insertions(+), 17 deletions(-) create mode 100644 config/synonyms.yml create mode 100644 src/Elastic.Documentation.Configuration/Synonyms/SynonymsConfiguration.cs diff --git a/config/synonyms.yml b/config/synonyms.yml new file mode 100644 index 000000000..1b9fda52f --- /dev/null +++ b/config/synonyms.yml @@ -0,0 +1,24 @@ +synonyms: + - ".net,.NET,c#,csharp,dotnet,net,NET,DOTNET" + - "esql,es|ql" + - "motlp,managed otlp,Managed OTLP" + - "s3,aws s3,amazon s3" + - "es,elasticsearch" + - "Elastic Learned Sparse EncodeR,elser" + - "ccs,cross cluster search,cross-cluster search,spaghetti" + - "apm,application performance monitoring" + - "ecctl,Elastic Cloud Control" + - "opentelemetry,otel" + - "eck,Elastic Cloud on Kubernetes" + - "ece,Elastic Cloud Enterprise" + - "ELv2,Elastic License v2" + - "kql,Kibana Query Language" + - "ccr,cross-cluster replication,cross cluster replication" + - "ESaaS,Elastic Stack as a service" + - "knn,k-Nearest Neighbors" + - "ech,Elastic Cloud Hosted,ess" + - "aws,amazon" + - "ilm,index lifecycle management" + - "javascript,js,node,nodejs,node.js" + - "edot,elastic distribution of opentelemetry" + - "k8s,kubernetes" \ No newline at end of file diff --git a/docs-builder.sln b/docs-builder.sln index 51348c58e..9bce0329e 100644 --- a/docs-builder.sln +++ b/docs-builder.sln @@ -122,6 +122,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{6FAB56 config\legacy-url-mappings.yml = config\legacy-url-mappings.yml config\navigation.yml = config\navigation.yml config\products.yml = config\products.yml + config\synonyms.yml = config\synonyms.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests-integration", "tests-integration", "{BCAD38D5-6C83-46E2-8398-4BE463931098}" diff --git a/src/Elastic.Documentation.Configuration/BuildContext.cs b/src/Elastic.Documentation.Configuration/BuildContext.cs index 6b3b383c3..e1c1c93d0 100644 --- a/src/Elastic.Documentation.Configuration/BuildContext.cs +++ b/src/Elastic.Documentation.Configuration/BuildContext.cs @@ -8,6 +8,7 @@ using Elastic.Documentation.Configuration.Builder; using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; @@ -34,6 +35,7 @@ public record BuildContext : IDocumentationSetContext, IDocumentationConfigurati public ProductsConfiguration ProductsConfiguration { get; } public LegacyUrlMappingConfiguration LegacyUrlMappings { get; } + public SynonymsConfiguration SynonymsConfiguration { get; } public IFileInfo ConfigurationPath { get; } @@ -85,6 +87,7 @@ public BuildContext( ReadFileSystem = readFileSystem; WriteFileSystem = writeFileSystem; AvailableExporters = availableExporters; + SynonymsConfiguration = configurationContext.SynonymsConfiguration; VersionsConfiguration = configurationContext.VersionsConfiguration; ConfigurationFileProvider = configurationContext.ConfigurationFileProvider; ProductsConfiguration = configurationContext.ProductsConfiguration; diff --git a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs index 7b6f5c03b..a18d4baf5 100644 --- a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs +++ b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs @@ -49,7 +49,7 @@ public ConfigurationFileProvider( ConfigurationSource = source; else { - string[] spotChecks = ["navigation.yml", "versions.yml", "products.yml", "assembler.yml"]; + string[] spotChecks = ["navigation.yml", "versions.yml", "products.yml", "assembler.yml", "synonyms.yml"]; var defaultSource = fileSystem.Directory.Exists(LocalConfigurationDirectory) && spotChecks.All(f => fileSystem.File.Exists(Path.Combine(LocalConfigurationDirectory, f))) @@ -92,6 +92,7 @@ public ConfigurationFileProvider( AssemblerFile = CreateTemporaryConfigurationFile("assembler.yml"); NavigationFile = CreateTemporaryConfigurationFile("navigation.yml"); LegacyUrlMappingsFile = CreateTemporaryConfigurationFile("legacy-url-mappings.yml"); + SynonymsFile = CreateTemporaryConfigurationFile("synonyms.yml"); } public bool SkipPrivateRepositories { get; } @@ -108,6 +109,7 @@ public ConfigurationFileProvider( public IFileInfo LegacyUrlMappingsFile { get; } + public IFileInfo SynonymsFile { get; } public IFileInfo CreateNavigationFile(AssemblyConfiguration configuration) { var privateRepositories = configuration.PrivateRepositories; diff --git a/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs b/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs index 4a44881cc..694b27771 100644 --- a/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs +++ b/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs @@ -4,6 +4,7 @@ using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Configuration.Versions; namespace Elastic.Documentation.Configuration; @@ -15,6 +16,8 @@ public interface IConfigurationContext DocumentationEndpoints Endpoints { get; } ProductsConfiguration ProductsConfiguration { get; } LegacyUrlMappingConfiguration LegacyUrlMappings { get; } + SynonymsConfiguration SynonymsConfiguration { get; } + } /// Used only to seed in DI, you primarily want to depend on @@ -35,6 +38,8 @@ public class ConfigurationContext : IConfigurationContext /// public required LegacyUrlMappingConfiguration LegacyUrlMappings { get; init; } + /// + public required SynonymsConfiguration SynonymsConfiguration { get; init; } } public interface IDocumentationConfigurationContext : IDocumentationContext, IConfigurationContext; diff --git a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs index 5a9c6ef8a..1b0c570af 100644 --- a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs +++ b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs @@ -5,6 +5,7 @@ using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Serialization; @@ -23,4 +24,5 @@ namespace Elastic.Documentation.Configuration.Serialization; [YamlSerializable(typeof(ProductDto))] [YamlSerializable(typeof(LegacyUrlMappingDto))] [YamlSerializable(typeof(LegacyUrlMappingConfigDto))] +[YamlSerializable(typeof(SynonymsConfigDto))] // Add this line public partial class YamlStaticContext; diff --git a/src/Elastic.Documentation.Configuration/Synonyms/SynonymsConfiguration.cs b/src/Elastic.Documentation.Configuration/Synonyms/SynonymsConfiguration.cs new file mode 100644 index 000000000..2c3f2c3fc --- /dev/null +++ b/src/Elastic.Documentation.Configuration/Synonyms/SynonymsConfiguration.cs @@ -0,0 +1,25 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +namespace Elastic.Documentation.Configuration.Synonyms; + +public record SynonymsConfiguration +{ + public required IReadOnlyCollection Synonyms { get; init; } +} + +internal sealed record SynonymsConfigDto +{ + public List Synonyms { get; set; } = []; +} + +public static class SynonymsConfigurationExtensions +{ + public static SynonymsConfiguration CreateSynonymsConfiguration(this ConfigurationFileProvider provider) + { + var synonymsFile = provider.SynonymsFile; + var synonymsDto = ConfigurationFileProvider.Deserializer.Deserialize(synonymsFile.OpenText()); + return new SynonymsConfiguration { Synonyms = synonymsDto.Synonyms }; + } +} diff --git a/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs b/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs index 1786f3a1f..18691fb34 100644 --- a/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs +++ b/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs @@ -5,6 +5,7 @@ using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.ServiceDefaults.Logging; using Microsoft.Extensions.DependencyInjection; @@ -34,9 +35,11 @@ public static TBuilder AddDocumentationServiceDefaults(this TBuilder b { var versionConfiguration = p.CreateVersionConfiguration(); var products = p.CreateProducts(versionConfiguration); + var synonyms = p.CreateSynonymsConfiguration(); _ = s.AddSingleton(p.CreateLegacyUrlMappings(products)); _ = s.AddSingleton(products); _ = s.AddSingleton(versionConfiguration); + _ = s.AddSingleton(synonyms); configure?.Invoke(s, p); }); _ = builder.Services.AddElasticDocumentationLogging(globalArgs.LogLevel); diff --git a/src/Elastic.Documentation/Serialization/SourceGenerationContext.cs b/src/Elastic.Documentation/Serialization/SourceGenerationContext.cs index 5feb7a18f..7a97730a3 100644 --- a/src/Elastic.Documentation/Serialization/SourceGenerationContext.cs +++ b/src/Elastic.Documentation/Serialization/SourceGenerationContext.cs @@ -28,4 +28,5 @@ namespace Elastic.Documentation.Serialization; [JsonSerializable(typeof(Applicability))] [JsonSerializable(typeof(ProductLifecycle))] [JsonSerializable(typeof(SemVersion))] +[JsonSerializable(typeof(string[]))] public sealed partial class SourceGenerationContext : JsonSerializerContext; diff --git a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchExporter.cs b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchExporter.cs index 199724929..ce612066a 100644 --- a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchExporter.cs +++ b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchExporter.cs @@ -33,7 +33,7 @@ DistributedTransport transport { "batch_index_date", d.BatchIndexDate.ToString("o") } }), GetMapping = () => CreateMapping(null), - GetMappingSettings = CreateMappingSetting, + GetMappingSettings = () => CreateMappingSetting("docs"), IndexFormat = $"{endpoint.IndexNamePrefix.Replace("semantic", "lexical").ToLowerInvariant()}-{indexNamespace.ToLowerInvariant()}-{{0:yyyy.MM.dd.HHmmss}}", ActiveSearchAlias = $"{endpoint.IndexNamePrefix.Replace("semantic", "lexical").ToLowerInvariant()}-{indexNamespace.ToLowerInvariant()}" @@ -51,7 +51,7 @@ DistributedTransport transport { BulkOperationIdLookup = d => d.Url, GetMapping = (inferenceId, _) => CreateMapping(inferenceId), - GetMappingSettings = (_, _) => CreateMappingSetting(), + GetMappingSettings = (_, _) => CreateMappingSetting("docs"), IndexFormat = $"{endpoint.IndexNamePrefix.ToLowerInvariant()}-{indexNamespace.ToLowerInvariant()}-{{0:yyyy.MM.dd.HHmmss}}", ActiveSearchAlias = $"{endpoint.IndexNamePrefix}-{indexNamespace.ToLowerInvariant()}", IndexNumThreads = endpoint.IndexNumThreads, @@ -139,9 +139,9 @@ public async ValueTask TryWrite(DocumentationDocument document, Cancel ctx } - protected static string CreateMappingSetting() => + protected static string CreateMappingSetting(string synonymSetName) => // language=json - """ + $$""" { "analysis": { "analyzer": { @@ -163,10 +163,9 @@ protected static string CreateMappingSetting() => }, "filter": { "synonyms_filter": { - "type": "synonym", - "synonyms_set": "docs", - "updateable": true - }, + "type": "synonym_graph", + "synonyms_set": "{{synonymSetName}}" + }, "english_stop": { "type": "stop", "stopwords": "_english_" diff --git a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs index 64c3198e0..e88d4574b 100644 --- a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs @@ -3,7 +3,10 @@ // See the LICENSE file in the project root for more information using System.IO.Abstractions; +using System.Text.Json; +using System.Text.Json.Serialization; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Diagnostics; using Elastic.Documentation.Search; using Elastic.Ingest.Elasticsearch; @@ -21,6 +24,16 @@ namespace Elastic.Markdown.Exporters.Elasticsearch; [EnumExtensions] public enum IngestStrategy { Reindex, Multiplex } +internal sealed record SynonymSetRequest +{ + [JsonPropertyName("synonyms")] + public required string[] Synonyms { get; init; } +} + +[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower)] +[JsonSerializable(typeof(SynonymSetRequest))] +internal sealed partial class SynonymSerializerContext : JsonSerializerContext { }; + public class ElasticsearchMarkdownExporter : IMarkdownExporter, IDisposable { private readonly IDiagnosticsCollector _collector; @@ -34,11 +47,14 @@ public class ElasticsearchMarkdownExporter : IMarkdownExporter, IDisposable private readonly DistributedTransport _transport; private IngestStrategy _indexStrategy; + private readonly IReadOnlyCollection _synonyms; + public ElasticsearchMarkdownExporter( ILoggerFactory logFactory, IDiagnosticsCollector collector, DocumentationEndpoints endpoints, - string indexNamespace + string indexNamespace, + SynonymsConfiguration synonyms ) { _collector = collector; @@ -72,7 +88,7 @@ string indexNamespace }; _transport = new DistributedTransport(configuration); - + _synonyms = synonyms.Synonyms; _lexicalChannel = new ElasticsearchLexicalExporter(logFactory, collector, es, indexNamespace, _transport); _semanticChannel = new ElasticsearchSemanticExporter(logFactory, collector, es, indexNamespace, _transport); } @@ -80,6 +96,7 @@ string indexNamespace /// public async ValueTask StartAsync(Cancel ctx = default) { + await PublishSynonymsAsync("docs", ctx); _ = await _lexicalChannel.Channel.BootstrapElasticsearchAsync(BootstrapMethod.Failure, null, ctx); var semanticIndex = _semanticChannel.Channel.IndexName; @@ -102,6 +119,25 @@ public async ValueTask StartAsync(Cancel ctx = default) _logger.LogInformation("Using {IndexStrategy} to sync lexical index to semantic index", _indexStrategy.ToStringFast(true)); } + private async Task PublishSynonymsAsync(string name, CancellationToken ctx) + { + _logger.LogInformation("Publishing synonym set '{Name}' to Elasticsearch", name); + + var requestBody = new SynonymSetRequest { Synonyms = _synonyms.ToArray() }; + var json = JsonSerializer.Serialize(requestBody, SynonymSerializerContext.Default.SynonymSetRequest); + + var response = await _transport.PutAsync($"_synonyms/{name}", PostData.String(json), ctx); + + if (!response.ApiCallDetails.HasSuccessfulStatusCode) + { + _collector.EmitGlobalError($"Failed to publish synonym set '{name}'. Reason: {response.ApiCallDetails.OriginalException?.Message ?? response.ToString()}"); + } + else + { + _logger.LogInformation("Successfully published synonym set '{Name}'.", name); + } + } + private async ValueTask CountAsync(string index, string body, Cancel ctx = default) { var countResponse = await _transport.PostAsync($"/{index}/_count", PostData.String(body), ctx); diff --git a/src/Elastic.Markdown/Exporters/ExporterExtensions.cs b/src/Elastic.Markdown/Exporters/ExporterExtensions.cs index 298244330..3f63c7281 100644 --- a/src/Elastic.Markdown/Exporters/ExporterExtensions.cs +++ b/src/Elastic.Markdown/Exporters/ExporterExtensions.cs @@ -25,7 +25,7 @@ string indexNamespace if (exportOptions.Contains(Exporter.Configuration)) markdownExporters.Add(new ConfigurationExporter(logFactory, context.ConfigurationFileProvider, context)); if (exportOptions.Contains(Exporter.Elasticsearch)) - markdownExporters.Add(new ElasticsearchMarkdownExporter(logFactory, context.Collector, context.Endpoints, indexNamespace)); + markdownExporters.Add(new ElasticsearchMarkdownExporter(logFactory, context.Collector, context.Endpoints, indexNamespace, context.SynonymsConfiguration)); return markdownExporters; } } diff --git a/src/services/Elastic.Documentation.Assembler/AssembleContext.cs b/src/services/Elastic.Documentation.Assembler/AssembleContext.cs index 7ef37b5e2..f7ee32689 100644 --- a/src/services/Elastic.Documentation.Assembler/AssembleContext.cs +++ b/src/services/Elastic.Documentation.Assembler/AssembleContext.cs @@ -7,6 +7,7 @@ using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; @@ -31,6 +32,7 @@ public class AssembleContext : IDocumentationConfigurationContext public ProductsConfiguration ProductsConfiguration { get; } public LegacyUrlMappingConfiguration LegacyUrlMappings { get; } + public SynonymsConfiguration SynonymsConfiguration { get; } public IDirectoryInfo CheckoutDirectory { get; } @@ -56,6 +58,7 @@ public AssembleContext( Configuration = configuration; ConfigurationFileProvider = configurationContext.ConfigurationFileProvider; VersionsConfiguration = configurationContext.VersionsConfiguration; + SynonymsConfiguration = configurationContext.SynonymsConfiguration; Endpoints = configurationContext.Endpoints; ProductsConfiguration = configurationContext.ProductsConfiguration; LegacyUrlMappings = configurationContext.LegacyUrlMappings; diff --git a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs index 32c714b7b..a75e3350c 100644 --- a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs +++ b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs @@ -9,6 +9,7 @@ using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; using Elastic.Documentation.ServiceDefaults; @@ -76,13 +77,15 @@ public static TBuilder AddDocumentationToolingDefaults(this TBuilder b var versionsConfiguration = sp.GetRequiredService(); var products = sp.GetRequiredService(); var legacyUrlMappings = sp.GetRequiredService(); + var synonyms = sp.GetRequiredService(); // Add this line return new ConfigurationContext { ConfigurationFileProvider = configurationFileProvider, VersionsConfiguration = versionsConfiguration, Endpoints = endpoints, ProductsConfiguration = products, - LegacyUrlMappings = legacyUrlMappings + LegacyUrlMappings = legacyUrlMappings, + SynonymsConfiguration = synonyms }; }); diff --git a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs index 7efab701c..bb4269897 100644 --- a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs +++ b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs @@ -8,6 +8,7 @@ using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Configuration.Versions; using Microsoft.Extensions.Logging.Abstractions; @@ -15,7 +16,7 @@ namespace Elastic.ApiExplorer.Tests; public static class TestHelpers { - public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null) + public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null, SynonymsConfiguration? synonyms = null) { versionsConfiguration ??= new VersionsConfiguration { @@ -45,6 +46,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS } }.ToFrozenDictionary() }; + synonyms = new SynonymsConfiguration { Synonyms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints @@ -55,6 +57,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS VersionsConfiguration = versionsConfiguration, ProductsConfiguration = productsConfiguration, LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, + SynonymsConfiguration = synonyms }; } } diff --git a/tests/Elastic.Markdown.Tests/TestHelpers.cs b/tests/Elastic.Markdown.Tests/TestHelpers.cs index cadad408c..f1f8a856e 100644 --- a/tests/Elastic.Markdown.Tests/TestHelpers.cs +++ b/tests/Elastic.Markdown.Tests/TestHelpers.cs @@ -8,13 +8,14 @@ using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Configuration.Versions; namespace Elastic.Markdown.Tests; public static class TestHelpers { - public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null) + public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null, SynonymsConfiguration synonyms = null) { versionsConfiguration ??= new VersionsConfiguration { @@ -60,6 +61,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS } }.ToFrozenDictionary() }; + synonyms = new SynonymsConfiguration{ Synonyms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints @@ -69,6 +71,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS ConfigurationFileProvider = new ConfigurationFileProvider(new TestLoggerFactory(TestContext.Current.TestOutputHelper), fileSystem), VersionsConfiguration = versionsConfiguration, ProductsConfiguration = productsConfiguration, + SynonymsConfiguration = synonyms, LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, }; } diff --git a/tests/authoring/Framework/Setup.fs b/tests/authoring/Framework/Setup.fs index 83ecd7a33..6876c4fa2 100644 --- a/tests/authoring/Framework/Setup.fs +++ b/tests/authoring/Framework/Setup.fs @@ -16,6 +16,7 @@ open Elastic.Documentation.Configuration open Elastic.Documentation.Configuration.LegacyUrlMappings open Elastic.Documentation.Configuration.Versions open Elastic.Documentation.Configuration.Products +open Elastic.Documentation.Configuration.Synonyms open Elastic.Markdown open Elastic.Markdown.IO open JetBrains.Annotations @@ -280,7 +281,8 @@ type Setup = ConfigurationFileProvider = configurationFileProvider, Endpoints=DocumentationEndpoints(Elasticsearch = ElasticsearchEndpoint.Default), ProductsConfiguration = ProductsConfiguration(Products = productDict.ToFrozenDictionary()), - LegacyUrlMappings = LegacyUrlMappingConfiguration(Mappings = []) + LegacyUrlMappings = LegacyUrlMappingConfiguration(Mappings = []), + SynonymsConfiguration = SynonymsConfiguration(Synonyms = []) ) let context = BuildContext( collector, diff --git a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs index 778c2b926..9d2f9d678 100644 --- a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs +++ b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs @@ -8,6 +8,7 @@ using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Configuration.Versions; using Microsoft.Extensions.Logging.Abstractions; @@ -19,7 +20,8 @@ public static IConfigurationContext CreateConfigurationContext( IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ConfigurationFileProvider? configurationFileProvider = null, - ProductsConfiguration? productsConfiguration = null + ProductsConfiguration? productsConfiguration = null, + SynonymsConfiguration? synonyms = null ) { configurationFileProvider ??= new ConfigurationFileProvider(NullLoggerFactory.Instance, fileSystem); @@ -51,6 +53,7 @@ public static IConfigurationContext CreateConfigurationContext( } }.ToFrozenDictionary() }; + synonyms = new SynonymsConfiguration { Synonyms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints @@ -60,6 +63,7 @@ public static IConfigurationContext CreateConfigurationContext( ConfigurationFileProvider = configurationFileProvider, VersionsConfiguration = versionsConfiguration, ProductsConfiguration = productsConfiguration, + SynonymsConfiguration = synonyms, LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, }; } From b8fbd6d43c3bbec86bd2b02a95f1438a875669a3 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 22 Oct 2025 04:14:17 -0300 Subject: [PATCH 2/8] Touch up --- .../Elasticsearch/ElasticsearchMarkdownExporter.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs index e88d4574b..30183877d 100644 --- a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs @@ -119,22 +119,22 @@ public async ValueTask StartAsync(Cancel ctx = default) _logger.LogInformation("Using {IndexStrategy} to sync lexical index to semantic index", _indexStrategy.ToStringFast(true)); } - private async Task PublishSynonymsAsync(string name, CancellationToken ctx) + private async Task PublishSynonymsAsync(string setName, CancellationToken ctx) { - _logger.LogInformation("Publishing synonym set '{Name}' to Elasticsearch", name); + _logger.LogInformation("Publishing synonym set '{SetName}' to Elasticsearch", setName); var requestBody = new SynonymSetRequest { Synonyms = _synonyms.ToArray() }; var json = JsonSerializer.Serialize(requestBody, SynonymSerializerContext.Default.SynonymSetRequest); - var response = await _transport.PutAsync($"_synonyms/{name}", PostData.String(json), ctx); + var response = await _transport.PutAsync($"_synonyms/{setName}", PostData.String(json), ctx); if (!response.ApiCallDetails.HasSuccessfulStatusCode) { - _collector.EmitGlobalError($"Failed to publish synonym set '{name}'. Reason: {response.ApiCallDetails.OriginalException?.Message ?? response.ToString()}"); + _collector.EmitGlobalError($"Failed to publish synonym set '{setName}'. Reason: {response.ApiCallDetails.OriginalException?.Message ?? response.ToString()}"); } else { - _logger.LogInformation("Successfully published synonym set '{Name}'.", name); + _logger.LogInformation("Successfully published synonym set '{SetName}'.", setName); } } From 9bb389d350013d300420a3d629f2bbf32405f360 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 22 Oct 2025 04:25:12 -0300 Subject: [PATCH 3/8] Add synonyms to project resources. --- .../Elastic.Documentation.Configuration.csproj | 1 + .../src/docs-assembler.Tests/AssemblerConfigurationTests.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj index c7defd0a3..f48dbe4de 100644 --- a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj +++ b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj @@ -24,5 +24,6 @@ + diff --git a/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs b/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs index b4f9b347b..12b30a74b 100644 --- a/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs +++ b/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs @@ -71,6 +71,7 @@ public void ReadsConfigurationFiles() Context.ConfigurationFileProvider.NavigationFile.Name.Should().Be("navigation.yml"); Context.ConfigurationFileProvider.AssemblerFile.Name.Should().Be("assembler.yml"); Context.ConfigurationFileProvider.LegacyUrlMappingsFile.Name.Should().Be("legacy-url-mappings.yml"); + Context.ConfigurationFileProvider.SynonymsFile.Name.Should().Be("synonyms.yml"); } [Fact] From ab55d3821796725d8b6bd65cd9ad59149c3bbdfe Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 22 Oct 2025 09:54:32 -0300 Subject: [PATCH 4/8] Adjust to parse yaml lists --- .../Serialization/YamlStaticContext.cs | 2 +- .../Synonyms/SynonymsConfiguration.cs | 11 +++++++++-- .../DocumentationTooling.cs | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs index 1b0c570af..92d930f24 100644 --- a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs +++ b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs @@ -24,5 +24,5 @@ namespace Elastic.Documentation.Configuration.Serialization; [YamlSerializable(typeof(ProductDto))] [YamlSerializable(typeof(LegacyUrlMappingDto))] [YamlSerializable(typeof(LegacyUrlMappingConfigDto))] -[YamlSerializable(typeof(SynonymsConfigDto))] // Add this line +[YamlSerializable(typeof(SynonymsConfigDto))] public partial class YamlStaticContext; diff --git a/src/Elastic.Documentation.Configuration/Synonyms/SynonymsConfiguration.cs b/src/Elastic.Documentation.Configuration/Synonyms/SynonymsConfiguration.cs index 2c3f2c3fc..47872f2d3 100644 --- a/src/Elastic.Documentation.Configuration/Synonyms/SynonymsConfiguration.cs +++ b/src/Elastic.Documentation.Configuration/Synonyms/SynonymsConfiguration.cs @@ -2,6 +2,8 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Immutable; + namespace Elastic.Documentation.Configuration.Synonyms; public record SynonymsConfiguration @@ -11,7 +13,7 @@ public record SynonymsConfiguration internal sealed record SynonymsConfigDto { - public List Synonyms { get; set; } = []; + public List> Synonyms { get; set; } = []; } public static class SynonymsConfigurationExtensions @@ -19,7 +21,12 @@ public static class SynonymsConfigurationExtensions public static SynonymsConfiguration CreateSynonymsConfiguration(this ConfigurationFileProvider provider) { var synonymsFile = provider.SynonymsFile; + + if (!synonymsFile.Exists) + return new SynonymsConfiguration { Synonyms = [] }; + var synonymsDto = ConfigurationFileProvider.Deserializer.Deserialize(synonymsFile.OpenText()); - return new SynonymsConfiguration { Synonyms = synonymsDto.Synonyms }; + var flattenedSynonyms = synonymsDto.Synonyms.Select(sl => string.Join(',', sl)).ToImmutableArray(); + return new SynonymsConfiguration { Synonyms = flattenedSynonyms }; } } diff --git a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs index a75e3350c..4653c3db4 100644 --- a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs +++ b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs @@ -77,7 +77,7 @@ public static TBuilder AddDocumentationToolingDefaults(this TBuilder b var versionsConfiguration = sp.GetRequiredService(); var products = sp.GetRequiredService(); var legacyUrlMappings = sp.GetRequiredService(); - var synonyms = sp.GetRequiredService(); // Add this line + var synonyms = sp.GetRequiredService(); return new ConfigurationContext { ConfigurationFileProvider = configurationFileProvider, From 6cbce2bd162fbf55dd3d52ec26363e076be3fcba Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 22 Oct 2025 11:10:57 -0300 Subject: [PATCH 5/8] Add preliminary docs --- docs/_docset.yml | 1 + docs/configure/site/index.md | 4 ++++ docs/configure/site/synonyms.md | 12 ++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 docs/configure/site/synonyms.md diff --git a/docs/_docset.yml b/docs/_docset.yml index 4c87c4e85..3eed8cba0 100644 --- a/docs/_docset.yml +++ b/docs/_docset.yml @@ -70,6 +70,7 @@ toc: - file: products.md - file: versions.md - file: legacy-url-mappings.md + - file: synonyms.md - folder: content-set children: - file: index.md diff --git a/docs/configure/site/index.md b/docs/configure/site/index.md index e255e46a2..f8e077c61 100644 --- a/docs/configure/site/index.md +++ b/docs/configure/site/index.md @@ -16,6 +16,10 @@ Configure the documentation site in these files: Redirects are also configured at the site-level. See [](../../contribute/redirects.md) for more information. +## Synonyms + +Synonyms applied to our Serverless observability project are declared as a site-level configuration. See [](../../contribute/synonyms.md) for more information. + ## V3 site-level diagram *Coming soon* diff --git a/docs/configure/site/synonyms.md b/docs/configure/site/synonyms.md new file mode 100644 index 000000000..aab00ec4b --- /dev/null +++ b/docs/configure/site/synonyms.md @@ -0,0 +1,12 @@ +# `synonyms.yml` + +The [`synonyms.yml`](https://github.com/elastic/docs-builder/blob/main/config/synonyms.yml) file provides a way to define synonyms for our Serverless observability project. + +Synonyms updates are sent during Elasticsearch-specific export procedures in the CI workflow. + +```yml +synonyms: + - [ ".net", "c#", "csharp", "dotnet", "net" ] + - [ "esql", "es|ql" ] + - [ "motlp", "managed otlp" ] + ``` From a3c0bc89d3a182a0ba0f5e7026046bd5727450da Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 22 Oct 2025 11:48:55 -0300 Subject: [PATCH 6/8] Fix link --- docs/configure/site/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configure/site/index.md b/docs/configure/site/index.md index f8e077c61..f4d783f3a 100644 --- a/docs/configure/site/index.md +++ b/docs/configure/site/index.md @@ -18,7 +18,7 @@ Redirects are also configured at the site-level. See [](../../contribute/redirec ## Synonyms -Synonyms applied to our Serverless observability project are declared as a site-level configuration. See [](../../contribute/synonyms.md) for more information. +Synonyms applied to our Serverless observability project are declared as a site-level configuration. See [](./synonyms.md) for more information. ## V3 site-level diagram From 5202f0563e980154f5328153cca19448be574683 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 22 Oct 2025 11:55:45 -0300 Subject: [PATCH 7/8] Fix linting --- tests/Elastic.ApiExplorer.Tests/TestHelpers.cs | 4 ++-- tests/Elastic.Markdown.Tests/TestHelpers.cs | 4 ++-- .../src/docs-assembler.Tests/TestHelpers.cs | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs index bb4269897..a073f999d 100644 --- a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs +++ b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs @@ -16,7 +16,7 @@ namespace Elastic.ApiExplorer.Tests; public static class TestHelpers { - public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null, SynonymsConfiguration? synonyms = null) + public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null) { versionsConfiguration ??= new VersionsConfiguration { @@ -46,7 +46,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS } }.ToFrozenDictionary() }; - synonyms = new SynonymsConfiguration { Synonyms = [] }; + var synonyms = new SynonymsConfiguration { Synonyms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints diff --git a/tests/Elastic.Markdown.Tests/TestHelpers.cs b/tests/Elastic.Markdown.Tests/TestHelpers.cs index f1f8a856e..e6f653a63 100644 --- a/tests/Elastic.Markdown.Tests/TestHelpers.cs +++ b/tests/Elastic.Markdown.Tests/TestHelpers.cs @@ -15,7 +15,7 @@ namespace Elastic.Markdown.Tests; public static class TestHelpers { - public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null, SynonymsConfiguration synonyms = null) + public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null) { versionsConfiguration ??= new VersionsConfiguration { @@ -61,7 +61,7 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS } }.ToFrozenDictionary() }; - synonyms = new SynonymsConfiguration{ Synonyms = [] }; + var synonyms = new SynonymsConfiguration { Synonyms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints diff --git a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs index 9d2f9d678..1bc2a19a8 100644 --- a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs +++ b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs @@ -20,8 +20,7 @@ public static IConfigurationContext CreateConfigurationContext( IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ConfigurationFileProvider? configurationFileProvider = null, - ProductsConfiguration? productsConfiguration = null, - SynonymsConfiguration? synonyms = null + ProductsConfiguration? productsConfiguration = null ) { configurationFileProvider ??= new ConfigurationFileProvider(NullLoggerFactory.Instance, fileSystem); @@ -53,7 +52,7 @@ public static IConfigurationContext CreateConfigurationContext( } }.ToFrozenDictionary() }; - synonyms = new SynonymsConfiguration { Synonyms = [] }; + var synonyms = new SynonymsConfiguration { Synonyms = [] }; return new ConfigurationContext { Endpoints = new DocumentationEndpoints From 442d00db07d8b7d673dfa9711cbf111712ed587e Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Wed, 22 Oct 2025 12:01:14 -0300 Subject: [PATCH 8/8] Lint --- .../Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs index 727467624..d3662d3d5 100644 --- a/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/Elasticsearch/ElasticsearchMarkdownExporter.cs @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information using System.IO.Abstractions; -using Elastic.Documentation.AppliesTo; using System.Text.Json; using System.Text.Json.Serialization; +using Elastic.Documentation.AppliesTo; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Synonyms; using Elastic.Documentation.Diagnostics;