0

私はC#でelasticsearch.netライブラリを使用しており、指定されたフィルターに一致するオブジェクトを照会しようとしています。

フィルターからの入力名の少なくとも 1 つがオブジェクトの 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ławomir Rosiekの回答から少しコードをアップグレードして、実際に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));
4

1 に答える 1

3

あなたの問題は、配列全体をクエリに渡そうとしていることだと思います。その代わりに、それを 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)))));
于 2016-04-02T18:15:07.637 に答える