Search code examples
c#asp.net-coreelasticsearchkibana.net-8.0

Elasticsearch: Problem when I try to add dinamically nested objects into filter using c#


I have a problem when I try to add dynamically nested objects into filter. My query works fine into Kibana but when I translate it into code (I'm using Elastic.Clients.Elasticsearch v8.11.0) last nested object overwrites the first. Objects are inserted using a foreach loop that calls the GetFacetNestedScope function.

This is my kibana query:

{
  "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

and this is my code (everything is called by GetQueryFunction):

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)))
                        )
                )
            )
        )
    );
}

Documentation for new version of this nuget package is missing and other posts doesn't solve this problem. Why results are different? Something is missing in my code?


Solution

  • Instead of trying to initialize multiple sub-queries within a single filterdescriptor, create a list of filter descriptors:

    if (facets != null && facets.Any())
    {
        var filterDescriptors = new List<Action<QueryDescriptor<Product>>>();
    
        foreach (var facet in facets)
        {
            filterDescriptors.Add(f => GetFacetNastedScope(f, facet));
        }
    
        b.Filter(filterDescriptors.ToArray());
    }