如何使用 ElasticSearch 的 NEST .net 客户端库创建查询以匹配子集合属性?

How to create query to match child collection attributes using NEST .net client library for ElasticSearch?

我在 ElasticSearch 7.3 中的 http://xyzserver:9200/mydocs/brands 有以下 4 个文档 每个文件代表一个品牌的一些细节。一个品牌可以与多个组相关联,并且可以对 0 个或多个组有效。

更新 1:这是文档映射

{
    "mydocs": {
        "mappings": {
            "properties": {
                "Groups": {
                    "properties": {
                        "GroupId": {
                            "type": "long"
                        },
                        "GroupName": {
                            "type": "text"
                        },
                        "IsActive": {
                            "type": "boolean"
                        }
                    }
                },
                "Id": {
                    "type": "long"
                },
                "Name": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                }
            }
        }
    }
}

我加载了 4 个文档。

{
    Id: 100,
    Name: 'Diet Coke',
    Groups:
    [{
        GroupId: 200,
        GroupName: 'US East',
        IsActive: true
     },
     {
        GroupId: 201,
        GroupName: 'US West',
        IsActive: true
     },
     {
        GroupId: 202,
        GroupName: 'US South',
        IsActive: false
     }
    ]
}
{
    Id: 110,
    Name: 'Coke',
    Groups:
    [{
        GroupId: 200,
        GroupName: 'US East',
        IsActive: false
     },
     {
        GroupId: 201,
        GroupName: 'US West',
        IsActive: true
     },
     {
        GroupId: 202,
        GroupName: 'US South',
        IsActive: true
     }
    ]
}
{
    Id: 120,
    Name: 'Coke with Lime',
    Groups:
    [{
        GroupId: 200,
        GroupName: 'US East',
        IsActive: true
     },
     {
        GroupId: 201,
        GroupName: 'US West',
        IsActive: true
     },
     {
        GroupId: 202,
        GroupName: 'US South',
        IsActive: true
     }
    ]
}
{
    Id: 130,
    Name: 'Cola',
    Groups:
    [{
        GroupId: 300,
        GroupName: 'Europe East',
        IsActive: true
     },
     {
        GroupId: 400,
        GroupName: 'Mexico',
        IsActive: true
     },
     {
        GroupId: 410,
        GroupName: 'Brazile',
        IsActive: true
     }
    ]
}

我正在搜索 "Coke",它是“200 - US East Group”和“201 - US West Group”两个组的 "Active"。假设我有一个搜索条件类型

public class BrandSearchCriteria {
    public string Keyword {get; set;}
    public IEnumerable<int> GroupIds {get; set;} = new List<int>();
    public bool? IsActive {get; set;} //true if looking for only active items, false if looking for inactive items, null if looking for both
}

searchCriteria = new BrandSearchCriteria
{
    Keyword = "Coke",
    GroupIds = new List<int> { 200, 201 },
    IsActive = true
}

如何使用 NEST 库创建查询?这是我目前所拥有的

QueryContainerDescriptor<Brand> queryDescriptor = new QueryContainerDescriptor<Brand>();
queryDescriptor.Bool(b =>
    b.Must(q =>
        q.Match(m => m.Field(f => f.Name).Fuzziness(Fuzziness.Auto).Query(searchCriteria.KeyWord) &&
        q.Terms(t => t.Field("Groups.GroupId").Terms<int>(searchCriteria.GroupIds)) &&
        q.Term(t => t.Field("Groups.IsActive").Value(searchCriteria.IsActive.ToString()))
      )
    );

我假设只取回 ID 为 100(健怡可乐)和 120(酸橙可乐)的两个文档,因为这两个文档是两个组“200 - US East Group”&&“201”中仅有的两个活动文件- 美西集团。

但上面的查询是给我返回 3 个文档 100(健怡可乐)、110(可口可乐)、120(可口可乐加酸橙)。即使文档 110(可口可乐)对于组“20​​1 - US West Group”无效,它仍会包含在结果中。

我刚开始学习 NEST 库的使用,不知道如何制定查询来检索结果。任何帮助将不胜感激。

首先,我认为您需要更改映射。具体来说 Groups 应该嵌套

{
    "mappings": {
        "properties": {
            "Groups": {
              "type": "nested", 
                "properties": {
                    "GroupId": {
                        "type": "long"
                    },
                    "GroupName": {
                        "type": "text"
                    },
                    "IsActive": {
                        "type": "boolean"
                    }
                }
            },
            "Id": {
                "type": "long"
            },
            "Name": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                }
            }
        }
    }
}

那么基于您的嵌套代码如下所示:

// preparing the query
var groupIds = new[] { 200, 201 };
Func<QueryContainerDescriptor<Brand>, int, QueryContainer> nestedQuery = (q, groupId) => q.Nested(n => n.Path("Groups").Query(nq => nq.Bool(nb => nb.Filter(
        fq => fq.Term(t => t.Field("Groups.GroupId").Value(groupId)),
        fq => fq.Term(t => t.Field("Groups.IsActive").Value(true))))
    )
);
var query = groupIds.Select(groupId => nestedQuery(new QueryContainerDescriptor<Brand>(), groupId)).ToList();
query.Add(new QueryContainerDescriptor<Brand>().Match(m => m.Field("Name").Fuzziness(Fuzziness.Auto).Query("coke")));

var queryDescriptor = new QueryContainerDescriptor<Brand>();
queryDescriptor.Bool(b => b.Must(query.ToArray()));

几点建议:

  • Field

  • 等方法中使用 Expression 而不是字符串 ES 端的
  • boolean 类型在 C# 上是 bool。不确定为什么要将其转换为字符串。

  • 在不需要评分的时候使用过滤器,基本上是yes/no答案。

  • 我会在这里使用无痛脚本。会看起来更干净