Elastic.Mapping 0.30.0

Prefix Reserved
dotnet add package Elastic.Mapping --version 0.30.0
                    
NuGet\Install-Package Elastic.Mapping -Version 0.30.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Elastic.Mapping" Version="0.30.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Elastic.Mapping" Version="0.30.0" />
                    
Directory.Packages.props
<PackageReference Include="Elastic.Mapping" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Elastic.Mapping --version 0.30.0
                    
#r "nuget: Elastic.Mapping, 0.30.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Elastic.Mapping@0.30.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Elastic.Mapping&version=0.30.0
                    
Install as a Cake Addin
#tool nuget:?package=Elastic.Mapping&version=0.30.0
                    
Install as a Cake Tool

Elastic.Mapping

Compile-time Elasticsearch mappings for .NET. Native AOT ready. Define your index mappings, analysis chains, and field metadata with C# attributes and get reflection-free generated code that works with System.Text.Json source generation out of the box.

Why?

Elasticsearch field names are strings. Typos are silent. Refactors break queries. Manual JSON mappings drift from your code.

Elastic.Mapping fixes this with a source generator that turns your POCOs into type-safe, pre-computed mapping infrastructure at build time -- zero reflection, zero runtime overhead, fully AOT compatible.

Quick Start

1. Define your domain types (clean POCOs)

public class Product
{
    [Keyword]
    public string Id { get; set; }

    [Text(Analyzer = "standard")]
    public string Name { get; set; }

    public double Price { get; set; }

    public bool InStock { get; set; }

    [Nested]
    public List<Category> Categories { get; set; }
}

2. Register them in a mapping context

[ElasticsearchMappingContext]
[Index<Product>(Name = "products")]
[DataStream<ApplicationLog>(Type = "logs", Dataset = "myapp", Namespace = "production")]
public static partial class MyContext;

3. Use generated field constants and metadata

// Type-safe field names -- rename the C# property, these update automatically
MyContext.Product.Fields.Name      // "name"
MyContext.Product.Fields.Price     // "price"
MyContext.Product.Fields.InStock   // "inStock"

// Index targets
MyContext.Product.IndexStrategy.WriteTarget   // "products"

// Data stream naming follows Elastic conventions
MyContext.ApplicationLog.IndexStrategy.DataStreamName  // "logs-myapp-production"

// Pre-built JSON for index creation
var json = MyContext.Product.Context.GetIndexJson();

// Change detection -- only update when mappings actually change
if (clusterHash != MyContext.Product.Hash) UpdateMappings();

System.Text.Json Integration

Elastic.Mapping is built around System.Text.Json. Link your STJ source-generated JsonSerializerContext and the mapping generator inherits your serialization configuration automatically -- one source of truth for both JSON serialization and Elasticsearch field names.

// Your STJ source-generated context
[JsonSourceGenerationOptions(
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    UseStringEnumConverter = true,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(Product))]
[JsonSerializable(typeof(Order))]
public partial class MyJsonContext : JsonSerializerContext;

// Link it to the mapping context
[ElasticsearchMappingContext(JsonContext = typeof(MyJsonContext))]
[Index<Product>(Name = "products")]
[Index<Order>(Name = "orders")]
public static partial class MyContext;

The generator reads [JsonSourceGenerationOptions] at compile time and applies:

STJ Option Effect on Mappings
PropertyNamingPolicy Field names follow the same policy (camelCase, snake_case_lower, kebab-case-lower, etc.)
UseStringEnumConverter Enum fields map to keyword instead of integer
DefaultIgnoreCondition Ignored properties are excluded from mappings
IgnoreReadOnlyProperties Read-only properties are excluded from mappings

Per-property [JsonPropertyName("custom_name")] and [JsonIgnore] attributes are always respected, with or without a linked context.

This means your Elasticsearch field names, your JSON wire format, and your C# properties all stay in sync -- at compile time, with no reflection.

Native AOT

Every feature in Elastic.Mapping is AOT compatible:

  • No reflection at runtime -- all field names, mappings JSON, and type metadata are generated as constants at compile time
  • No dynamic code generation -- source generators run during build, not at runtime
  • Pre-computed JSON -- settings and mappings are embedded as string literals, ready to send to Elasticsearch
  • STJ source generation -- link your JsonSerializerContext for a fully AOT serialization pipeline

Publish with dotnet publish -p:PublishAot=true and everything works.

Field Type Attributes

Control how properties map to Elasticsearch field types:

Attribute Elasticsearch Type Use Case
[Text] text Full-text search, analyzers
[Keyword] keyword Exact match, aggregations, sorting
[Date] date Timestamps, date math
[Nested] nested Preserve array element relationships
[GeoPoint] geo_point Latitude/longitude
[DenseVector(Dims = 384)] dense_vector Embeddings, kNN search
[SemanticText] semantic_text ELSER / semantic search
[Ip] ip IPv4/IPv6 addresses
[Completion] completion Autocomplete suggestions

Properties without attributes are inferred from their CLR type (stringkeyword, intinteger, DateTimedate, etc.).

Custom Configuration with IConfigureElasticsearch<T>

Implement IConfigureElasticsearch<T> to configure analysis, mappings, and index settings for a document type. There are two ways to wire this up:

Option A: Dedicated configuration class

Create a separate class that implements the interface and reference it via Configuration = typeof(...):

public class ProductConfig : IConfigureElasticsearch<Product>
{
    public AnalysisBuilder ConfigureAnalysis(AnalysisBuilder analysis) => analysis
        .Analyzer("product_search", a => a.Custom()
            .Tokenizer(BuiltIn.Tokenizers.Standard)
            .Filters(BuiltIn.TokenFilters.Lowercase, "english_stemmer", "edge_ngram_3_8"))
        .TokenFilter("english_stemmer", f => f.Stemmer()
            .Language(BuiltIn.StemmerLanguages.English))
        .TokenFilter("edge_ngram_3_8", f => f.EdgeNGram()
            .MinGram(3).MaxGram(8));

    public MappingsBuilder<Product> ConfigureMappings(MappingsBuilder<Product> mappings) => mappings
        .Name(f => f.Analyzer("product_search")
            .MultiField("keyword", mf => mf.Keyword().IgnoreAbove(256)));

    public IReadOnlyDictionary<string, string> IndexSettings => new Dictionary<string, string>
    {
        ["index.default_pipeline"] = "product-enrichment"
    };
}

[ElasticsearchMappingContext]
[Index<Product>(Name = "products", Configuration = typeof(ProductConfig))]
public static partial class MyContext;

Option B: Self-configuring entity

If you prefer to keep configuration on the entity itself, implement the interface directly:

public class Product : IConfigureElasticsearch<Product>
{
    [Keyword]
    public string Id { get; set; }

    [Text(Analyzer = "product_search")]
    public string Name { get; set; }

    public double Price { get; set; }

    public AnalysisBuilder ConfigureAnalysis(AnalysisBuilder analysis) => analysis
        .Analyzer("product_search", a => a.Custom()
            .Tokenizer(BuiltIn.Tokenizers.Standard)
            .Filters(BuiltIn.TokenFilters.Lowercase));

    public MappingsBuilder<Product> ConfigureMappings(MappingsBuilder<Product> mappings) => mappings
        .Name(f => f.Analyzer("product_search")
            .MultiField("keyword", mf => mf.Keyword().IgnoreAbove(256)));
}

// No Configuration needed -- the generator detects the interface on the entity itself
[ElasticsearchMappingContext]
[Index<Product>(Name = "products")]
public static partial class MyContext;

All three interface members use default implementations, so you only need to override what you customize:

Member Default Purpose
ConfigureAnalysis no-op Custom analyzers, tokenizers, filters
ConfigureMappings no-op Field overrides, runtime fields, dynamic templates
IndexSettings null Additional index settings (e.g. default_pipeline)

The generated ProductAnalysis class gives you type-safe constants for your custom components:

MyContext.Product.Analysis.Analyzers.ProductSearch     // "product_search"
MyContext.Product.Analysis.TokenFilters.EnglishStemmer  // "english_stemmer"

Dependency Injection

If your configuration class needs services (e.g. to read settings from IConfiguration), call RegisterServiceProvider before first access to mapping JSON:

// In your startup / Program.cs
services.AddSingleton<IConfigureElasticsearch<Product>, ProductConfig>();

var sp = services.BuildServiceProvider();
MyContext.RegisterServiceProvider(sp);

When no IServiceProvider is registered (or the service isn't found), the generator falls back to new ProductConfig() (parameterless constructor).

Index Strategies

Traditional Index

[Index<Product>(
    Name = "products",
    WriteAlias = "products-write",
    ReadAlias = "products-read",
    Shards = 3,
    RefreshInterval = "5s"
)]

Rolling Date Index

[Index<Order>(Name = "orders", DatePattern = "yyyy.MM")]
// Write target: orders-2025.02
// Search pattern: orders-*

Data Stream (logs, metrics, traces)

[DataStream<ApplicationLog>(Type = "logs", Dataset = "ecommerce.app", Namespace = "production")]
// Data stream: logs-ecommerce.app-production
// Search pattern: logs-ecommerce.app-*

Mappings Builder

The source generator creates extension methods on MappingsBuilder<T> for each property on your registered types. Customize mappings at the property level with full IntelliSense:

public MappingsBuilder<Product> ConfigureMappings(MappingsBuilder<Product> mappings) => mappings
    .Name(f => f.Analyzer("product_search"))
    .Price(f => f.DocValues(true))
    .AddRuntimeField("discount_pct", r => r.Double()
        .Script("emit((doc['price'].value - doc['sale_price'].value) / doc['price'].value * 100)"))
    .AddDynamicTemplate("labels_as_keyword", dt => dt
        .PathMatch("labels.*")
        .Mapping(m => m.Keyword()));

What Gets Generated

For each registered type, the source generator produces:

  • Field constants -- MyContext.Product.Fields.Name (compile-time safe field names)
  • Bidirectional field mapping -- PropertyToField / FieldToProperty dictionaries
  • Index/search strategy -- write targets, search patterns, data stream names
  • Settings + mappings JSON -- pre-computed, ready for index creation
  • Content hashes -- detect when mappings change
  • Analysis accessors -- type-safe constants for custom analyzers/filters
  • Mappings builder extensions -- per-property fluent API on MappingsBuilder<T> for customization

All generated at compile time. Zero reflection at runtime. AOT compatible. Aligned with your System.Text.Json configuration.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Elastic.Mapping:

Package Downloads
Elastic.Ingest.Elasticsearch

Offers an easy to use ChannelWriter implementation to push data concurrently to Elasticsearch using Elastic.Transport

Elastic.Esql

LINQ-to-ES|QL query translation for Elasticsearch

Elastic.Clients.Esql

Elasticsearch ES|QL client with LINQ support and HTTP transport

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.30.0 154 2/26/2026
0.29.0 253 2/24/2026
0.28.0 159 2/24/2026
0.27.0 234 2/23/2026
0.26.0 146 2/23/2026
0.25.0 140 2/23/2026
0.24.0 310 2/22/2026
0.4.1 436 2/18/2026
0.4.0 436 2/17/2026
0.3.0 202 2/16/2026
0.2.0 354 2/12/2026