Querying array of strings by array of strings in elasticsearch.net

痴心易碎 提交于 2019-12-31 05:14:29

问题


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

回答1:


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


来源:https://stackoverflow.com/questions/36374003/querying-array-of-strings-by-array-of-strings-in-elasticsearch-net

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!