Elasticsearch自定义客户端UUID作为_id批量插入的问题及正确方式
问题
我使用的Elasticsearch Docker镜像版本为:docker.elastic.co/elasticsearch/elasticsearch:7.16.2。此前一直采用dynamic: strict模式,_id字段由Elasticsearch自动生成UUID。现在为了让Elasticsearch接受客户端自定义的UUID作为_id字段值,我将映射中的"dynamic": "strict"改为true。但插入新文档后发现,文档中新增了一个id字段,内容与元数据字段_id完全一致。我想知道这是否属于预期行为,以及当前方式是否是实现「客户端生成UUID并批量插入至Elasticsearch」目标的正确做法。
我使用C#语言及.NET 7中的NEST NuGet包进行开发。另外还遇到一个问题:通过Postman插入单个文档时,我将客户端生成的UUID赋值给_id字段,但Elasticsearch会忽略该值并自动生成新的_id。
代码片段
Dto/Model代码
public interface IElasticDocumentId { string id { get; set; } } public class SurfaceScanDocument : IElasticDocumentId { public SurfaceScanDocument() { fs = new Fs(); } public Fs fs { get; set; } public string id { get; set; } public string file_name { get; set; } public object file_type { get; set; } public DateTime? ingest { get; set; } }
插入操作的NEST代码
public async Task InsertDocumentWithId<T>(string indexName, T document) where T : class, IElasticDocumentId { try { var client = _elasticSearchClientProvider.GetElasticClient(); var indexResponse = await client.IndexAsync(document, i => i .Index(indexName) .Id(document.id) .Refresh(Refresh.True)); if (!indexResponse.IsValid) { throw new Exception($"Error to insert document: {indexResponse.DebugInformation}"); } } catch (Exception ex) { _logger.LogError(ex, $"An error occurred: {ex.Message}"); throw; } }
解答
1. 新增id字段的原因
这是预期行为。你的实体类SurfaceScanDocument中定义了id属性,当dynamic设为true时,Elasticsearch会自动将该属性作为文档字段存储;同时你在调用IndexAsync时通过.Id(document.id)指定了元数据_id,因此出现了文档内id与元数据_id值一致的情况。
2. 客户端自定义_id的正确方式
你不需要将dynamic从strict改为true,这会导致Elasticsearch自动添加未在映射中定义的字段。正确做法是:
- 保持
dynamic: strict模式,避免意外插入未定义字段 - 如果不需要将
_id作为文档内字段存储,直接删除实体类中的id属性 - 插入时通过NEST的
.Id()方法指定客户端生成的UUID即可,Elasticsearch会使用该值作为_id,不会自动生成,同时文档中也不会多出额外的id字段
如果确实需要将_id值同时作为文档字段存储,可在映射中显式定义id字段,继续保持dynamic: strict模式即可。
3. Postman插入时忽略自定义_id的问题
Postman插入文档时,需注意请求格式:
- 使用POST/PUT请求时,需将自定义UUID放在URL路径中,格式为
{{es_host}}/{{index_name}}/_doc/{{your_uuid}} - 仅在请求体中写入
"_id": "xxx"的话,POST请求下Elasticsearch会忽略该值,自动生成新_id
4. 批量插入的实现
使用NEST的BulkAsync方法实现批量插入,在每个操作中指定Id即可:
public async Task BulkInsertDocuments<T>(string indexName, IEnumerable<T> documents) where T : class, IElasticDocumentId { var client = _elasticSearchClientProvider.GetElasticClient(); var bulkRequest = new BulkRequest(indexName) { Operations = documents.Select(d => new BulkIndexOperation<T>(d) { Id = d.id }).ToList() }; var bulkResponse = await client.BulkAsync(bulkRequest); if (!bulkResponse.IsValid) { throw new Exception($"批量插入失败: {bulkResponse.DebugInformation}"); } }
同样保持dynamic: strict模式,实体类按需保留或删除id属性即可。
内容的提问来源于stack exchange,提问作者IbrarMumtaz

