Elasticsearch C#动态添加嵌套过滤对象被覆盖问题求助
问题描述
我在尝试给Elasticsearch过滤器动态添加嵌套对象时遇到问题。该查询在Kibana中运行正常,但转换为C#代码(使用Elastic.Clients.Elasticsearch v8.11.0)后,最后一个嵌套对象会覆盖第一个对象。这些对象是通过foreach循环调用GetFacetNestedScope函数插入的。
Kibana查询示例
{ "size": 2000, "query": { "bool": { "should": [ { "match": { "description.title": { "query": "borsa termica", "prefix_length": 1, "fuzziness": "AUTO", "_name": "title" } } }, { "match": { "description.longDescription": { "query": "borsa termica", "prefix_length": 1, "fuzziness": "AUTO", "_name": "longDescription" } } }, { "match": { "options.code": { "query": "borsa termica", "prefix_length": 1, "fuzziness": "AUTO", "_name": "code" } } } ], "filter": [ { "nested": { "_name": "MARCA_guzzini", "path": "erpAttributes", "query": { "bool": { "must": [ { "term": { "erpAttributes.erpId": { "value": "MARCA" } } }, { "nested": { "path": "erpAttributes.values", "query": { "term": { "erpAttributes.values.id": { "value": "guzzini" } } } } } ] } } } }, { "nested": { "_name": "colore_bianco", "path": "erpAttributes", "query": { "bool": { "must": [ { "term": { "erpAttributes.erpId": { "value": "COL" } } }, { "nested": { "path": "erpAttributes.values", "query": { "term": { "erpAttributes.values.id": { "value": "bianco" } } } } } ] } } } } ] } }, "aggs": { "attributi_unici": { "nested": { "path": "erpAttributes" }, "aggs": { "nomi_attributi": { "terms": { "field": "erpAttributes.name", "size": 10 }, "aggs": { "valori_unici": { "nested": { "path": "erpAttributes.values" }, "aggs": { "id_valori": { "terms": { "field": "erpAttributes.values.id", "size": 10 } } } } } } } } } }
问题C#代码
public static QueryDescriptor<Product> GetQuery(QueryDescriptor<Product> q, IEnumerable<Facet> facets, string searchTerm, string categorySlug) { return q .Bool(b => { if (!string.IsNullOrWhiteSpace(categorySlug)) { b.Must(mu => mu .Term(t => t.Field(f => f.Categories.First().Id.Suffix(Constants.ElasticSearch.SuffixKeyword)) .Value(categorySlug))); } if (!string.IsNullOrWhiteSpace(searchTerm)) { b.Should( sh => GetSearchTermMatchScope(sh, searchTerm, Constants.ElasticSearch.SearchTermFields.Title), sh => GetSearchTermMatchScope(sh, searchTerm, Constants.ElasticSearch.SearchTermFields.LongDescription), sh => GetSearchTermMatchScope(sh, searchTerm, Constants.ElasticSearch.SearchTermFields.Code) ); } if (facets != null && facets.Any()) { b.Filter(f => { foreach (var facet in facets) { GetFacetNastedScope(f, facet); } }); } }); } public static QueryDescriptor<Product> GetFacetNastedScope(QueryDescriptor<Product> f, Facet facet) { return f.Nested(n => n .QueryName(facet.Name) .Path(Constants.ElasticSearch.ProductFacetsNastedFields.ErpAttributes) .Query(qu => qu .Bool(nb => nb .Must(m => m .Term(t => t .Field(Constants.ElasticSearch.ProductFacetsNastedFields.ErpAttributesErpId) .Value(facet.Name) ), m => m .Nested(nn => nn .Path(Constants.ElasticSearch.ProductFacetsNastedFields.ErpAttributesValues) .Query(nnq => nnq .Term(t => t .Field(Constants.ElasticSearch.ProductFacetsNastedFields.ErpAttributesValuesId) .Value(facet.Value))) ) ) ) ) ); }
问题原因与解决方法
核心问题
你在Filter的lambda中循环调用GetFacetNastedScope时,每次都是直接操作同一个QueryDescriptor实例,但没有将生成的嵌套查询添加到Filter集合中。QueryDescriptor的链式方法(比如.Nested())是替换当前的查询配置,而不是追加新的查询,所以最后一次调用会覆盖之前所有的设置。
另外还有一个细节问题:Kibana中嵌套查询的_name是MARCA_guzzini这种「属性名_属性值」的组合,但你C#代码里只传了facet.Name,会导致查询标识不一致,建议同步修正。
修正代码
1. 修改GetFacetNastedScope返回单个Query
让它返回独立的Query对象,而不是修改传入的QueryDescriptor:
public static Query GetFacetNastedScope(Facet facet) { // 生成组合名称,和Kibana中的_name保持一致 var queryName = $"{facet.Name}_{facet.Value}"; return new QueryDescriptor<Product>() .Nested(n => n .QueryName(queryName) .Path(Constants.ElasticSearch.ProductFacetsNastedFields.ErpAttributes) .Query(qu => qu .Bool(nb => nb .Must( m => m.Term(t => t .Field(Constants.ElasticSearch.ProductFacetsNastedFields.ErpAttributesErpId) .Value(facet.Name)), m => m.Nested(nn => nn .Path(Constants.ElasticSearch.ProductFacetsNastedFields.ErpAttributesValues) .Query(nnq => nnq.Term(t => t .Field(Constants.ElasticSearch.ProductFacetsNastedFields.ErpAttributesValuesId) .Value(facet.Value))) ) ) ) ) ); }
2. 调整GetQuery中处理Facets的逻辑
先收集所有facet对应的查询,再一次性添加到Filter中:
if (facets != null && facets.Any()) { // 将每个facet转换为对应的查询 var facetQueries = facets.Select(GetFacetNastedScope).ToArray(); // 把所有查询添加到Filter中 b.Filter(facetQueries); }
原理说明
Elastic.Clients.Elasticsearch的BoolQueryDescriptor.Filter()方法支持接收多个Query对象,会自动将它们组合成一个数组(对应Kibana中的filter数组)。通过先收集所有查询再批量传入,就能避免覆盖问题,生成和Kibana一致的查询结构。
内容的提问来源于stack exchange,提问作者P_R
相关产品推荐
相关产品推荐

