Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions config/synonyms.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
synonyms:
- ".net,.NET,c#,csharp,dotnet,net,NET,DOTNET"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit torn on this.

I understand that it was easier to use , as a delimiter instead of having a 2d list here.

I'm a bit worried that this will be prone to undetected errors.

What are your thoughts on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... If it has a parsing error, it would be loud.

But in general, I don't have a strong feeling one way or the other. I imagined this file won't be manipulated nearly as much outside from people involved in looking at the dashboards.

It would be better for this branch's CI to create a quick PR with just the file, so maybe I could update it already.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM. your call

Copy link
Member

@Mpdreamz Mpdreamz Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using commas currently also mimics how synonyms are stored historically in synonym text files. I kinda like it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sees comment just after changing it

- "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"
1 change: 1 addition & 0 deletions docs-builder.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
3 changes: 3 additions & 0 deletions src/Elastic.Documentation.Configuration/BuildContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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; }

Expand Down Expand Up @@ -85,6 +87,7 @@ public BuildContext(
ReadFileSystem = readFileSystem;
WriteFileSystem = writeFileSystem;
AvailableExporters = availableExporters;
SynonymsConfiguration = configurationContext.SynonymsConfiguration;
VersionsConfiguration = configurationContext.VersionsConfiguration;
ConfigurationFileProvider = configurationContext.ConfigurationFileProvider;
ProductsConfiguration = configurationContext.ProductsConfiguration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down Expand Up @@ -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; }
Expand All @@ -108,6 +109,7 @@ public ConfigurationFileProvider(

public IFileInfo LegacyUrlMappingsFile { get; }

public IFileInfo SynonymsFile { get; }
public IFileInfo CreateNavigationFile(AssemblyConfiguration configuration)
{
var privateRepositories = configuration.PrivateRepositories;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@
<EmbeddedResource Include="$(SolutionRoot)\config\assembler.yml" />
<EmbeddedResource Include="$(SolutionRoot)\config\navigation.yml" />
<EmbeddedResource Include="$(SolutionRoot)\config\legacy-url-mappings.yml" />
<EmbeddedResource Include="$(SolutionRoot)\config\synonyms.yml" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -15,6 +16,8 @@ public interface IConfigurationContext
DocumentationEndpoints Endpoints { get; }
ProductsConfiguration ProductsConfiguration { get; }
LegacyUrlMappingConfiguration LegacyUrlMappings { get; }
SynonymsConfiguration SynonymsConfiguration { get; }

}

/// Used only to seed <see cref="IConfigurationContext"/> in DI, you primarily want to depend on <see cref="IDocumentationConfigurationContext"/>
Expand All @@ -35,6 +38,8 @@ public class ConfigurationContext : IConfigurationContext
/// <inheritdoc />
public required LegacyUrlMappingConfiguration LegacyUrlMappings { get; init; }

/// <inheritdoc />
public required SynonymsConfiguration SynonymsConfiguration { get; init; }
}

public interface IDocumentationConfigurationContext : IDocumentationContext, IConfigurationContext;
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Original file line number Diff line number Diff line change
@@ -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<string> Synonyms { get; init; }
}

internal sealed record SynonymsConfigDto
{
public List<string> Synonyms { get; set; } = [];
}

public static class SynonymsConfigurationExtensions
{
public static SynonymsConfiguration CreateSynonymsConfiguration(this ConfigurationFileProvider provider)
{
var synonymsFile = provider.SynonymsFile;
var synonymsDto = ConfigurationFileProvider.Deserializer.Deserialize<SynonymsConfigDto>(synonymsFile.OpenText());
return new SynonymsConfiguration { Synonyms = synonymsDto.Synonyms };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -34,9 +35,11 @@ public static TBuilder AddDocumentationServiceDefaults<TBuilder>(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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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()}"
Expand All @@ -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,
Expand Down Expand Up @@ -139,9 +139,9 @@ public async ValueTask<bool> TryWrite(DocumentationDocument document, Cancel ctx
}


protected static string CreateMappingSetting() =>
protected static string CreateMappingSetting(string synonymSetName) =>
// language=json
"""
$$"""
{
"analysis": {
"analyzer": {
Expand All @@ -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_"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

using System.IO.Abstractions;
using Elastic.Documentation.AppliesTo;
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;
Expand All @@ -22,6 +25,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;
Expand All @@ -37,11 +50,14 @@ public class ElasticsearchMarkdownExporter : IMarkdownExporter, IDisposable
private string _currentLexicalHash = string.Empty;
private string _currentSemanticHash = string.Empty;

private readonly IReadOnlyCollection<string> _synonyms;

public ElasticsearchMarkdownExporter(
ILoggerFactory logFactory,
IDiagnosticsCollector collector,
DocumentationEndpoints endpoints,
string indexNamespace
string indexNamespace,
SynonymsConfiguration synonyms
)
{
_collector = collector;
Expand Down Expand Up @@ -75,7 +91,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);
}
Expand All @@ -86,6 +102,7 @@ public async ValueTask StartAsync(Cancel ctx = default)
_currentLexicalHash = await _lexicalChannel.Channel.GetIndexTemplateHashAsync(ctx) ?? string.Empty;
_currentSemanticHash = await _semanticChannel.Channel.GetIndexTemplateHashAsync(ctx) ?? string.Empty;

await PublishSynonymsAsync("docs", ctx);
_ = await _lexicalChannel.Channel.BootstrapElasticsearchAsync(BootstrapMethod.Failure, null, ctx);

// if the previous hash does not match the current hash, we know already we want to multiplex to a new index
Expand Down Expand Up @@ -132,6 +149,25 @@ public async ValueTask StartAsync(Cancel ctx = default)
async ValueTask<bool> IndexExists(string name) => (await _transport.HeadAsync(name, ctx)).ApiCallDetails.HasSuccessfulStatusCode;
}

private async Task PublishSynonymsAsync(string setName, CancellationToken ctx)
{
_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<StringResponse>($"_synonyms/{setName}", PostData.String(json), ctx);

if (!response.ApiCallDetails.HasSuccessfulStatusCode)
{
_collector.EmitGlobalError($"Failed to publish synonym set '{setName}'. Reason: {response.ApiCallDetails.OriginalException?.Message ?? response.ToString()}");
}
else
{
_logger.LogInformation("Successfully published synonym set '{SetName}'.", setName);
}
}

private async ValueTask<long> CountAsync(string index, string body, Cancel ctx = default)
{
var countResponse = await _transport.PostAsync<DynamicResponse>($"/{index}/_count", PostData.String(body), ctx);
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.Markdown/Exporters/ExporterExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -31,6 +32,7 @@ public class AssembleContext : IDocumentationConfigurationContext

public ProductsConfiguration ProductsConfiguration { get; }
public LegacyUrlMappingConfiguration LegacyUrlMappings { get; }
public SynonymsConfiguration SynonymsConfiguration { get; }

public IDirectoryInfo CheckoutDirectory { get; }

Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -76,13 +77,15 @@ public static TBuilder AddDocumentationToolingDefaults<TBuilder>(this TBuilder b
var versionsConfiguration = sp.GetRequiredService<VersionsConfiguration>();
var products = sp.GetRequiredService<ProductsConfiguration>();
var legacyUrlMappings = sp.GetRequiredService<LegacyUrlMappingConfiguration>();
var synonyms = sp.GetRequiredService<SynonymsConfiguration>(); // Add this line
return new ConfigurationContext
{
ConfigurationFileProvider = configurationFileProvider,
VersionsConfiguration = versionsConfiguration,
Endpoints = endpoints,
ProductsConfiguration = products,
LegacyUrlMappings = legacyUrlMappings
LegacyUrlMappings = legacyUrlMappings,
SynonymsConfiguration = synonyms
};
});

Expand Down
Loading
Loading