2016-04-02 4 views
0

Я использую библиотеку elasticsearch.net на 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)); 
+0

Вы также можете использовать 'Термины()' запрос/фильтр для достижения этой цели, передавая массив терминов, в которых в по крайней мере, один должен соответствовать. –

+0

@ RussCam Да, я пробовал, и он дал 0 ударов, как я описал выше. –

+0

какую версию NEST вы используете и с какой версией Elasticsearch вы работаете? –

ответ

2

Я думаю, что ваша проблема в том, что вы пытаетесь передать весь массив для запроса. Вместо этого вы должны рассматривать это как выражение 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))))); 
+0

Спасибо за ваш ответ, это было очень полезно. Я немного изменил ваш код, чтобы он работал, поскольку типы «QueryContainerDescriptor» и «Field» не были найдены в версии elasticsearch.net, которую я использую (v. 1.7.1). –

Смежные вопросы