ElasticSearch中的关于对象数组查询,请注意要正确使用Nested类型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ElasticSearch中的关于对象数组查询,请注意要正确使用Nested类型相关的知识,希望对你有一定的参考价值。

参考技术A ElasticSearch中的关于对象数组查询,请注意要正确使用Nested类型,使用不当会导致检索时结果不正确。

假设张三和李四分别喜欢喝某品牌的啤酒,青岛(黑啤和白啤)燕京(黑啤和白啤)

要查询喜欢喝青岛白啤的用户

查询结果如下:

我们预想中,这个检索应该只能检索出李四,但是结果是张三也被匹配到了?为什么呢?

前面张三的文档将在内部转换为看起来更像这样的文档:

那我们该怎么做呢?

很简单,ElasticSearch官方告诉我们,将nested字段用于对象数组即可。

如果需要索引对象数组并保持数组中每个对象的独立性,请使用nested数据类型而不是object数据类型。

我们先将这条索引删除,重新定义mapping映射文件:

重新索引之前的张三、李四

请注意这个新查询,相应要增加nested path

得到正常结果:

参考文档:
https://www.elastic.co/guide/en/elasticsearch/reference/current/nested.html

通过elasticsearch.net中的字符串数组查询字符串数组

【中文标题】通过elasticsearch.net中的字符串数组查询字符串数组【英文标题】:Querying array of strings by array of strings in elasticsearch.net 【发布时间】:2016-04-02 13:39:40 【问题描述】:

我在 C# 中使用 elasticsearch.net 库,并尝试查询与指定过滤器匹配的对象。

我希望查询返回对象的名称集合中至少存在一个来自过滤器的输入名称的对象。

问题是我总是得到 0 次命中作为这个查询的结果,即使我确定数据库中确实存在与指定过滤器匹配的数据,我很想找出我的查询有什么问题......

型号:

public class A

    public int AId  get; set; 
    public IEnumerable<string> Names  get; set; 

过滤对象:

public class Filter

    public IEnumerable<string> NamesToSearch  get; set; 

查询数据的方法:

public async Task<IEnumerable<A>> GetFilteredData(Filter filter)

    var query = await _elasticClient.SearchAsync<A>(x => x.Query(q => q.Terms(a => a.Names, filter.NamesToSearch))
                                                            .Fields(a => a.AId, a => a.Names));

    return query.Hits
                .Select(x => new A
                                
                                    AId = x.Fields.FieldValues<A, int>(a => a.AId)[0]
                                )
                .ToList();

我也尝试过以下查询,但也没有产生预期的结果:

var query = await _elasticClient.SearchAsync<A>(x => x.Query(q => q.Nested(n => n.Filter(f => f.Terms(y => y.Names, filter.NamesToSearch))))
                                                              .Fields(a => a.AId, a => a.Names));

对我有用的解决方案:

我已经从Sławomir Rosiek's answer 升级了一些代码,以使用 ElasticSearch.net 1.7.1 进行实际编译,并且是类型安全的(没有通过字符串引用字段名),最后得到了以下扩展方法,这就像一个魅力对于我的场景:

public static QueryContainer MatchAnyTerm<T>(this QueryDescriptor<T> descriptor, Expression<Func<T, object>> field, object[] values) where T : class, new()

    var queryContainer = new QueryContainer();

    foreach (var value in values)
    
        queryContainer |= descriptor.Term(t => t.OnField(field).Value(value));
    

    return queryContainer;

及用法:

var query = await _elasticClient.SearchAsync<A>(x => x.Query(q =>
                                                                q.Bool(b =>
                                                                    b.Should(s => s.MatchAnyTerm(a => a.Names, filter.NamesToSearch.ToArray()))
                                                                        .Fields(a => a.AId, a => a.Names));

【问题讨论】:

您也可以使用Terms() 查询/过滤器来实现此目的,传递至少一个应该匹配的术语数组。 @RussCam 是的,我试过了,它产生了 0 次点击,就像我上面描述的那样。 您使用的是哪个版本的 NEST 以及您运行的是哪个版本的 Elasticsearch? 【参考方案1】:

我认为您的问题是您尝试将整个数组传递给查询。相反,您应该将其视为 OR 表达式。

以下是您应该使用的原始查询:


    "query": 
        "bool": 
            "should": [
                 "term": "names": "test"  ,
                 "term": "names": "xyz"  
            ]
        
    

而实现这一目标的 C# 代码。首先我定义了辅助函数:

private static QueryContainer TermAny<T>(QueryContainerDescriptor<T> descriptor, Field field, object[] values) where T : class

    QueryContainer q = new QueryContainer();
    foreach (var value in values)
    
        q |= descriptor.Term(t => t.Field(field).Value(value));
    
    return q;

现在是查询:

string[] values = new[]  "test", "xyz" ;
client.Search<A>(x => x.Query(
    q => q.Bool(
        b => b.Should(s => TermAny(s, "names", values)))));

【讨论】:

感谢您的回答,非常有帮助。我稍微修改了您的代码以使其正常工作,因为在我使用的 elasticsearch.net 版本(v. 1.7.1)中找不到类型“QueryContainerDescriptor”和“Field”。

以上是关于ElasticSearch中的关于对象数组查询,请注意要正确使用Nested类型的主要内容,如果未能解决你的问题,请参考以下文章

Elasticsearch:如何修改 nested 字段的值

Elasticsearch:如何修改 nested 字段的值

Elasticsearch 7.x Nested 嵌套类型查询 ES 干货

Elasticsearch 关于嵌套对象的重要术语

Elasticsearch如何管理 Elasticsearch 文档中的嵌套对象

ElasticSearch入门 第六篇:复合数据类型——数组,对象和嵌套