首页 > 解决方案 > ElasticSearch 匹配 int 和 string 列表

问题描述

我对 ElasticSearch 相当陌生,我正在尝试对我们的类别页面进行查询,ES 返回的每个产品都在该类别中。出于某种原因,它包括该类别之外的产品,我似乎无法弄清楚为什么。

产品是基本产品,包含一个类别 ID 列表(产品可以属于多个类别)。除了在 categoryId 上进行匹配外,它还应该在产品名称和变体的详细描述中进行搜索。

public IReadOnlyCollection<Product> GetByCategory(string value, int take, int categoryId)
    {
        value = string.Format("*{0}*", value);

        var query = new SearchDescriptor<Product>()
            .Index(this.index)
            .Query(q => q
              .Bool(b => b
                .Must(s => s
                  .Match(m => m
                    .Field(ff => ff
                      .AttachedCategoryIds.Contains(categoryId)
                    )
                  )
                )
                .Must(s => s
                  .QueryString(m => m
                    .Query(value)
                      .Fields(ff => ff
                        .Field(f => f.Name)
                        .Field(f => f.Variants.Select(input => input.LongDescription))
                      )
                      .Type(TextQueryType.CrossFields)
                    )
                  )
                )
              )
              .Size(take)
              .Sort(ss => ss.Descending(SortSpecialField.Score));

        var data = client.Search<Product>(query);

        var products = data.Documents;

        return products;
    }    

我希望只从弹性中获取当前类别中的产品,但由于某种原因,它给了我不属于某个类别/不同类别的产品。

标签: c#elasticsearchnest

解决方案


您的查询不正确。假设以下 POCO

public class Product
{
    public string Name { get; set; }
    public List<Variant> Variants { get; set; }
    public List<int> AttachedCategoryIds { get; set; }
}

public class Variant
{
    public string LongDescription { get; set; }
}

查询将类似于

var index = "index_name";
var categoryId = 1;

var value = "this is the query";
var take = 20;

var query = new SearchDescriptor<Product>()
    .Index(index)
    .Query(q => q
        .Bool(b => b
            .Must(s => s
                .QueryString(m => m
                    .Query(value)
                    .Fields(ff => ff
                        .Field(f => f.Name)
                        .Field(f => f.Variants.First().LongDescription)
                    )
                    .Type(TextQueryType.CrossFields)
                )
            )
            .Filter(f => f
                .Term(ff => ff.AttachedCategoryIds, categoryId)
            )
        )
    )
    .Size(take)
    .Sort(ss => ss.Descending(SortSpecialField.Score));

var searchResponse = client.Search<Product>(query);

几点

  1. .Field(f => f.Variants.First().LongDescription)是一个表达式,它将解析为将在 JSON 中序列化以定位 Elasticsearch 中的字段的字符串。在这种情况下,这将解决"variants.longDescription"

  2. term查询可用于确定 Elasticsearch 中的字段是否包含特定值。我已将查询放入bool查询过滤器子句中,因为我认为您不想为查询的这一部分计算相关性分数,即文档要么在字段中包含术语,要么没有。

这序列化为以下查询

POST http://localhost:9200/index_name/product/_search 
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "attachedCategoryIds": {
              "value": 1
            }
          }
        }
      ],
      "must": [
        {
          "query_string": {
            "fields": [
              "name",
              "variants.longDescription"
            ],
            "query": "this is the query",
            "type": "cross_fields"
          }
        }
      ]
    }
  },
  "size": 20,
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    }
  ]
}

此查询假设VariantsonProduct被映射为object数据类型。它可以更简洁地写成

var query = new SearchDescriptor<Product>()
    .Index(index)
    .Query(q => q
        .QueryString(m => m
            .Query(value)
            .Fields(ff => ff
                .Field(f => f.Name)
                .Field(f => f.Variants.First().LongDescription)
            )
            .Type(TextQueryType.CrossFields)
        ) && +q
        .Term(ff => ff.AttachedCategoryIds, categoryId)
    )
    .Size(take)
    .Sort(ss => ss.Descending(SortSpecialField.Score));

推荐阅读