Loading

Client-side reindex

ClientReindex<TDocument> orchestrates a client-side reindex by reading from a source index using PointInTimeSearch and writing to a destination using an IngestChannel<T>. This enables applying arbitrary transformations to documents in flight -- something the server-side _reindex API cannot do with complex application logic.

Use client-side reindex when you need to transform documents between indices using C# code. For simple copies, field renames, or painless-scriptable changes, prefer server-side reindex which runs entirely on the cluster and is faster.

using Elastic.Ingest.Elasticsearch;
using Elastic.Ingest.Elasticsearch.Helpers;

// Create and configure the destination channel (caller owns its lifecycle)
var channelOpts = new IngestChannelOptions<MyDocument>(transport, myTypeContext);
using var channel = new IngestChannel<MyDocument>(channelOpts);
await channel.BootstrapElasticsearchAsync(BootstrapMethod.Failure);

var reindex = new ClientReindex<MyDocument>(new ClientReindexOptions<MyDocument>
{
    Source = new PointInTimeSearchOptions
    {
        Index = "source-index",
        Size = 1000,
    },
    Destination = channel,
});

await foreach (var progress in reindex.RunAsync())
{
    Console.WriteLine($"Read: {progress.DocumentsRead}, Written: {progress.DocumentsWritten}");
}
		

Pass a Transform function to modify documents before they are written. When Transform is null (the default), documents are written as-is.

var options = new ClientReindexOptions<MyDocument>
{
    Source = new PointInTimeSearchOptions { Index = "source-index" },
    Destination = channel,
    Transform = doc =>
    {
        doc.Title = doc.Title.ToUpperInvariant();
        doc.MigratedAt = DateTimeOffset.UtcNow;
        return doc;
    },
};
		

Use QueryBody on the source options to limit which documents are reindexed:

Source = new PointInTimeSearchOptions
{
    Index = "source-index",
    QueryBody = """{"term":{"status":"active"}}""",
},
		

Instead of specifying index names as strings, you can use an ElasticsearchTypeContext on the source options. The index is resolved automatically from the type context's ReadAlias (if available) or WriteAlias:

var reindex = new ClientReindex<MyDocument>(new ClientReindexOptions<MyDocument>
{
    Source = new PointInTimeSearchOptions { TypeContext = myTypeContext },
    Destination = channel,
});
		
  1. Opens a PIT on the source index and iterates all matching documents using search_after pagination
  2. For each page, applies the Transform function (if set) and writes the batch to the destination channel via WaitToWriteManyAsync
  3. After all pages are read, calls WaitForDrainAsync to flush remaining writes
  4. Disposes the PIT in a finally block. The caller owns the channel lifecycle.

Serialization options are extracted from the destination channel's configuration automatically, so there's no need to pass them separately.

Property Type Default Description
Source PointInTimeSearchOptions required PIT search options for reading. See Point-in-time search.
Destination IngestChannel<TDocument> required The destination channel. Caller owns its lifecycle.
Transform Func<TDocument, TDocument>? null Optional per-document transform.

Each yielded progress snapshot exposes:

Property Type Description
DocumentsRead long Documents read from the source so far.
DocumentsWritten long Documents written to the destination so far.
IsCompleted bool Whether the reindex has completed.
Elapsed TimeSpan Time elapsed since the operation started.