Getting started
Elastic.Ingest.Elasticsearch gives you a production-ready, buffered ingestion pipeline for Elasticsearch. It batches documents into _bulk requests, retries failures, applies backpressure, and auto-configures index templates -- from a simple mapping declaration on your document type.
dotnet add package Elastic.Ingest.Elasticsearch
This pulls in Elastic.Mapping (source-generated mapping contexts) and Elastic.Channels (buffered channel infrastructure) as transitive dependencies.
Use Elastic.Mapping attributes to describe your document's Elasticsearch mapping:
public class Product
{
[Id]
[Keyword]
public string Sku { get; set; }
[Text(Analyzer = "standard")]
public string Name { get; set; }
[Keyword]
public string Category { get; set; }
}
| Attribute | What it does |
|---|---|
[Id] |
Uses this field as the Elasticsearch _id (enables upserts) |
[Keyword] |
Maps to a keyword field (exact match, aggregations) |
[Text] |
Maps to a text field (full-text search) |
[Timestamp] |
Marks the timestamp field (required for data streams, used for date-based index naming) |
The mapping context is a source-generated class that tells the channel what to create in Elasticsearch:
[ElasticsearchMappingContext]
[Entity<Product>]
public static partial class MyContext;
[Entity<Product>] with no parameters targets an index named product (the type name, lowercased). The source generator produces MyContext.Product.Context -- an ElasticsearchTypeContext containing mappings JSON, settings, and accessor delegates.
See mapping context for the full [Entity<>] parameter reference and how each option drives strategy selection.
var transport = new DistributedTransport(
new TransportConfiguration(new Uri("http://localhost:9200"))
);
var options = new IngestChannelOptions<Product>(transport, MyContext.Product.Context);
using var channel = new IngestChannel<Product>(options);
// Create templates and index in Elasticsearch
await channel.BootstrapElasticsearchAsync(BootstrapMethod.Failure);
// Write documents (buffered, batched automatically)
foreach (var product in products)
channel.TryWrite(product);
// Wait for all buffered documents to be sent
await channel.WaitForDrainAsync(TimeSpan.FromSeconds(30), ctx);
From [Entity<Product>] and the document attributes, the channel auto-resolved:
| Behavior | Resolved to | Why |
|---|---|---|
| Entity target | EntityTarget.Index |
Default when no Target specified |
| Ingest | TypeContextIndexIngestStrategy |
[Id] present: uses index operations (upserts) |
| Bootstrap | ComponentTemplateStep + IndexTemplateStep |
Index target needs component and index templates |
| Provisioning | AlwaysCreateProvisioning |
No [ContentHash] on the document |
| Alias | NoAliasStrategy |
No WriteAlias/ReadAlias configured |
Different [Entity<>] parameters resolve to different strategies. See mapping context for the full resolution table, or index management for more control over indices, data streams, and lifecycle.
- Bootstrap creates component templates (settings + mappings) and an index template in Elasticsearch. If the template already exists with the same content hash, it skips the update.
- TryWrite buffers documents in memory. When the batch reaches 1,000 items or 5 seconds elapse, the channel sends a
_bulkrequest. - Drain waits for all buffered and inflight documents to reach Elasticsearch before your code continues.
- Mapping context: full
[Entity<>]reference and strategy resolution - Channels: buffer tuning, callbacks, and channel lifecycle
- Use cases: end-to-end guides for every ingestion pattern