如何使用 Nest Elasticsearch 更新嵌套对象?
How to update nested objects using Nest Elasticsearch?
我有产品索引,为简单起见,它有两个字段 Id 和 ProductAttributes 作为嵌套对象,定义如下:
public class ProductType
{
public Guid Id { get; set; }
public List<ProductAttribute> ProductAttributes { get; set; }
}
public class ProductAttribute
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
以及以下映射:
elasticClient.CreateIndex("product", i => i
.Settings(s => s
.NumberOfShards(2)
.NumberOfReplicas(0)
)
.Mappings(m => m
.Map<ProductType>(map => map
.AutoMap()
.Properties(p => p
.Nested<ProductAttribute>(n => n
.Name(c => c.ProductAttributes)
.AutoMap()
.Properties(nc => nc
.Keyword(t => t
.Name(nn => nn.Name)
)
.Keyword(t => t
.Name(nn => nn.Value)
)
)
)
现在我正在尝试更新嵌套对象中的名称字段,并且我尝试使用如下脚本更新来实现它:
var scriptParams = new Dictionary<string, object>
{
{"name", "new name"}
};
var result = elasticClient.UpdateByQuery<ProductType>(u => u
.Script(sn => sn
.Inline(
$"ctx._source.productAttributes= params.name;"
)
.Params(scriptParams)
)
.Conflicts(Conflicts.Proceed)
.Refresh(true)
);
但是使用上面的查询我无法更新嵌套对象,你能告诉我如何使用 _update_by_query api 更新嵌套对象吗使用嵌套 ES?
看来问题出在这里 $"ctx._source.productAttributes= params.name;"
productAttributes 是一个对象(嵌套对象),params.name 是一个值(字符串),ES 在查询响应中抱怨什么。
不确定你到底想做什么,如果你的要求是更新所有 productAttributes 元素的名称,你可以试试这个:
var result = elasticClient.UpdateByQuery<ProductType>(u => u
.Index("product")
.Script(ss => ss.Source("for(int i=0; i<ctx._source.productAttributes.size(); i++){HashMap myKV = ctx._source.productAttributes.get(i);myKV.put(params.item.fieldName, params.item.fieldValue);}")
.Params(d => d.Add("item", new Dictionary<string, object>()
{
{"fieldName", "name" },
{"fieldValue", "new name" }
})).Lang("painless")));
最后我找到了如何根据特定嵌套对象的 id 更新名称 属性,如下所示:
var result = elasticClient.UpdateByQuery<ProductType>(u => u
.Query(q => q
.Nested(n => n
.Path(Infer.Field<ProductType>(ff => ff.ProductAttributes))
.Query(nq => nq
.Term(Infer.Field<ProductType>(ff => ff.ProductAttributes.First().Id), productAttributeId)
)
)
)
.Script(ss => ss.Inline("if (ctx._source.productAttributes != null){for (item in ctx._source.productAttributes){if (item.id == params.id) {item.name = params.name;}}}")
.Params(new Dictionary<string, object>()
{
{"id", productAttributeId},
{"name", productAttributeName}
}).Lang("painless")
)
.Conflicts(Conflicts.Proceed)
.Refresh(true)
);
这里是生成的查询:
POST product/producttype/_update_by_query?conflicts=proceed&refresh=true
{
"query": {
"bool": {
"must": [
{
"nested": {
"query": {
"term": {
"productAttributes.id": {
"value": "563243f0-8fbb-4adf-a78d-1339e5971a43"
}
}
},
"path": "productAttributes"
}
}
]
}
},
"script": {
"params": {
"id":"563243f0-8fbb-4adf-a78d-1339e5971a43",
"name": "CPU"
},
"lang": "painless",
"inline": "if (ctx._source.productAttributes != null){for (item in ctx._source.productAttributes){if (item.id == params.id) {item.name = params.name;}}}"
}
}
那么上面的查询是做什么的:
它首先搜索具有 563243f0-8fbb-4adf-a78d-1339e5971a43 id 的 productAttribute 的产品,然后迭代 productAttributes 嵌套对象以仅更新具有该 id 的属性和然后重新索引文档。
我希望我的回答能帮助其他在 Elasticsearch 中更新嵌套对象时遇到问题的人。
我有产品索引,为简单起见,它有两个字段 Id 和 ProductAttributes 作为嵌套对象,定义如下:
public class ProductType
{
public Guid Id { get; set; }
public List<ProductAttribute> ProductAttributes { get; set; }
}
public class ProductAttribute
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
以及以下映射:
elasticClient.CreateIndex("product", i => i
.Settings(s => s
.NumberOfShards(2)
.NumberOfReplicas(0)
)
.Mappings(m => m
.Map<ProductType>(map => map
.AutoMap()
.Properties(p => p
.Nested<ProductAttribute>(n => n
.Name(c => c.ProductAttributes)
.AutoMap()
.Properties(nc => nc
.Keyword(t => t
.Name(nn => nn.Name)
)
.Keyword(t => t
.Name(nn => nn.Value)
)
)
)
现在我正在尝试更新嵌套对象中的名称字段,并且我尝试使用如下脚本更新来实现它:
var scriptParams = new Dictionary<string, object>
{
{"name", "new name"}
};
var result = elasticClient.UpdateByQuery<ProductType>(u => u
.Script(sn => sn
.Inline(
$"ctx._source.productAttributes= params.name;"
)
.Params(scriptParams)
)
.Conflicts(Conflicts.Proceed)
.Refresh(true)
);
但是使用上面的查询我无法更新嵌套对象,你能告诉我如何使用 _update_by_query api 更新嵌套对象吗使用嵌套 ES?
看来问题出在这里 $"ctx._source.productAttributes= params.name;"
productAttributes 是一个对象(嵌套对象),params.name 是一个值(字符串),ES 在查询响应中抱怨什么。
不确定你到底想做什么,如果你的要求是更新所有 productAttributes 元素的名称,你可以试试这个:
var result = elasticClient.UpdateByQuery<ProductType>(u => u
.Index("product")
.Script(ss => ss.Source("for(int i=0; i<ctx._source.productAttributes.size(); i++){HashMap myKV = ctx._source.productAttributes.get(i);myKV.put(params.item.fieldName, params.item.fieldValue);}")
.Params(d => d.Add("item", new Dictionary<string, object>()
{
{"fieldName", "name" },
{"fieldValue", "new name" }
})).Lang("painless")));
最后我找到了如何根据特定嵌套对象的 id 更新名称 属性,如下所示:
var result = elasticClient.UpdateByQuery<ProductType>(u => u
.Query(q => q
.Nested(n => n
.Path(Infer.Field<ProductType>(ff => ff.ProductAttributes))
.Query(nq => nq
.Term(Infer.Field<ProductType>(ff => ff.ProductAttributes.First().Id), productAttributeId)
)
)
)
.Script(ss => ss.Inline("if (ctx._source.productAttributes != null){for (item in ctx._source.productAttributes){if (item.id == params.id) {item.name = params.name;}}}")
.Params(new Dictionary<string, object>()
{
{"id", productAttributeId},
{"name", productAttributeName}
}).Lang("painless")
)
.Conflicts(Conflicts.Proceed)
.Refresh(true)
);
这里是生成的查询:
POST product/producttype/_update_by_query?conflicts=proceed&refresh=true
{
"query": {
"bool": {
"must": [
{
"nested": {
"query": {
"term": {
"productAttributes.id": {
"value": "563243f0-8fbb-4adf-a78d-1339e5971a43"
}
}
},
"path": "productAttributes"
}
}
]
}
},
"script": {
"params": {
"id":"563243f0-8fbb-4adf-a78d-1339e5971a43",
"name": "CPU"
},
"lang": "painless",
"inline": "if (ctx._source.productAttributes != null){for (item in ctx._source.productAttributes){if (item.id == params.id) {item.name = params.name;}}}"
}
}
那么上面的查询是做什么的:
它首先搜索具有 563243f0-8fbb-4adf-a78d-1339e5971a43 id 的 productAttribute 的产品,然后迭代 productAttributes 嵌套对象以仅更新具有该 id 的属性和然后重新索引文档。
我希望我的回答能帮助其他在 Elasticsearch 中更新嵌套对象时遇到问题的人。