如何在C# NEST中正确映射字典类型的Elasticsearch索引字段?
代码问题分析与修正
你的写法不正确,核心问题在于对Elasticsearch嵌套字段的理解和NEST映射配置的偏差:
问题点
- 嵌套类型选型错误:直接将
IDictionary<string, decimal>作为嵌套类型不符合ES嵌套字段的定义——ES嵌套字段本质是结构化对象的数组,而字典默认会被AutoMap扁平化,生成以字典键为名称的动态字段,这正是你要避免的情况。 - 属性映射不匹配:原代码中试图映射
x.Values和x.Keys,但这两个是字典的集合属性,无法对应ES嵌套对象的单个字段,逻辑上不成立。
修正方案
方案1:修改模型为结构化对象数组(推荐)
先定义一个存储键值对的实体类,替代字典类型:
public class SalaryElement { public string Key { get; set; } public decimal Value { get; set; } }
将MainTermsDataModel中的GrossSalaryElements字段类型改为List<SalaryElement>,然后配置映射:
ElasticClient.Indices.Create(indexNameWithTenantId, c => c .InitializeUsing(new IndexState { Settings = new IndexSettings() }) .Map<MainTermsDataModel>(m => m .AutoMap() .Properties(p => p .Nested<SalaryElement>(n => n .Name(x => x.GrossSalaryElements) .Properties(pp => pp .Keyword(k => k.Name(e => e.Key)) .Number(nn => nn.Name(e => e.Value).Type(NumberType.Double)) ) ) ) ) );
方案2:保留原字典类型,自定义序列化与映射
如果无法修改原模型,需要通过自定义序列化将字典转换为对象数组,同时配置对应映射:
// 初始化客户端时配置自定义序列化 var settings = new ConnectionSettings(new Uri("http://localhost:9200")) .DefaultMappingFor<MainTermsDataModel>(m => m .Serialize(p => p.GrossSalaryElements, (writer, dict) => { writer.WriteStartArray(); foreach (var kvp in dict) { writer.WriteStartObject(); writer.WritePropertyName("key"); writer.WriteString(kvp.Key); writer.WritePropertyName("value"); writer.WriteDecimal(kvp.Value); writer.WriteEndObject(); } writer.WriteEndArray(); }) ); var client = new ElasticClient(settings); // 创建索引映射 client.Indices.Create(indexNameWithTenantId, c => c .InitializeUsing(new IndexState { Settings = new IndexSettings() }) .Map<MainTermsDataModel>(m => m .AutoMap() .Properties(p => p .Nested<object>(n => n .Name(x => x.GrossSalaryElements) .Properties(pp => pp .Keyword(k => k.Name("key")) .Number(nn => nn.Name("value").Type(NumberType.Double)) ) ) ) ) );
两种方案最终都会在ES索引中生成gross_salary_elements嵌套字段,结构为包含key(关键词类型)和value(数值类型)的对象数组,不会再为字典的每个键生成独立字段。
内容的提问来源于stack exchange,提问作者BigJamo
相关产品推荐
相关产品推荐

