在elasticsearch.net中按字符串数组查询字符串数组 [英] Querying array of strings by array of strings in elasticsearch.net

查看:179
本文介绍了在elasticsearch.net中按字符串数组查询字符串数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我希望查询返回对象,其中对象的Names集合中至少存在来自过滤器的输入名称之一.

问题在于,此查询的结果总是使我命中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ławomirRosiek的答案升级了一些代码,以使用ElasticSearch.net 1.7.1进行实际编译并输入-safe(不按字符串引用字段名称),并以以下扩展方法结束,这种扩展方法在我的情况下就像一个魅力:

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

解决方案

我认为您的问题是您试图将整个数组传递给查询.与其相反,您应该将其视为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)))));

I'm using elasticsearch.net library in C# and I'm trying to query for objects matching specified filter.

I would like the query to return objects where at least one of input names from filter exists in object's Names collection.

The problem is that I always get 0 hits as result with this query, even tho I am certain that data matching specified filter does exist in the database and I would love to find out what's wrong with my query...

The model:

public class A
{
    public int AId { get; set; }
    public IEnumerable<string> Names { get; set; }
}

The filtering object:

public class Filter
{
    public IEnumerable<string> NamesToSearch { get; set; }
}

The method for querying data:

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

I have also tried following query, but it didn't yield expected result neither:

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

SOLUTION WHICH WORKED FOR ME:

I have upgraded a bit code from Sławomir Rosiek's answer to actually compile using ElasticSearch.net 1.7.1 and be type-safe (no references to field name by string) and ended up with following extension method, which worked like a charm for my scenario:

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

and usage:

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

解决方案

I think that your problem is that you tries to pass whole array to query. Instead of that you should treat that as OR expression.

Below is the raw query that you should use:

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

And that the C# code to achive that. First I have defined helper function:

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

And now the query:

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

这篇关于在elasticsearch.net中按字符串数组查询字符串数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆