Elasticsearch должен с пересечением подзапроса

#elasticsearch #nest

#elasticsearch #гнездо

Вопрос:

Я хочу выполнить запрос Elasticsearch, который объединяет два подзапроса (И operator ), каждый из которых выполняет поиск в разных полях (ИЛИ operator ).

Например, если я передаю параметр «name», он выполняет поиск только в полях имени (firstname lastname), если я передаю параметр «contact», он выполняет поиск в полях контактов (ContactEmail ContactTelephone).

Приведенный ниже код возвращает :

  • Все результаты, если имя равно нулю, но указан контакт (должен возвращать только правую часть)
  • Все результаты, если контакт равен нулю, но указано имя (должно возвращать только левую часть)
  • результаты объединения (ИЛИ оператор), если указаны значения name и contact (должны возвращать left пересекаться с right)
 searchQuery = searchQuery
    .MinScore(minScore)
    .Query(qu => qu
        .Bool(b => b
            .Must(m => m
                .MultiMatch(mm=> mm
                    .Fields(fs=> fs
                        .Field(f => f.Firstname)
                        .Field(f => f.Lastname)
                    )
                    .Query(name)
                    .Operator(Operator.Or)
                )
            )
            .Must(m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.ContactEmail)
                        .Field(f => f.ContactTelephone)
                    )
                    .Query(contact)
                    .Operator(Operator.Or)
                )
            )
        )
    );
  

Я использую Must , потому что мне нужна соответствующая оценка.

Я думаю, что есть 2 проблемы: применение И вместо ИЛИ и игнорирование подзапроса, если критерий пуст. Есть идеи?

Ответ №1:

Два запроса в предложениях must должны быть частью одного и того же .Must() вызова.

Учитывая следующий POCO

 public class Person
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public string ContactEmail {get;set;}
    public string ContactTelephone {get;set;}

}
  

Запрос должен выглядеть следующим образом

 var client = new ElasticClient(settings);

var minScore = 2;
string name = "name";
string contact = "contact";

var response = client.Search<Person>(s => s
    .MinScore(minScore)
    .Query(qu => qu
        .Bool(b => b
            .Must(m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.Firstname)
                        .Field(f => f.Lastname)
                    )
                    .Query(name)
                    .Operator(Operator.Or)
                ), m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.ContactEmail)
                        .Field(f => f.ContactTelephone)
                    )
                    .Query(contact)
                    .Operator(Operator.Or)
                )
            )
        )
    )
);
  

что приводит к следующему

 {
  "min_score": 2.0,
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "fields": [
              "contactEmail",
              "contactTelephone"
            ],
            "operator": "or",
            "query": "contact"
          }
        }
      ]
    }
  }
}
  

Если либо name или contact есть null , этот запрос будет опущен. Например, установка name в null

 string name = null;

var response = client.Search<Person>(s => s
    .MinScore(minScore)
    .Query(qu => qu
        .Bool(b => b
            .Must(m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.Firstname)
                        .Field(f => f.Lastname)
                    )
                    .Query(name)
                    .Operator(Operator.Or)
                ), m => m
                .MultiMatch(mm => mm
                    .Fields(fs => fs
                        .Field(f => f.ContactEmail)
                        .Field(f => f.ContactTelephone)
                    )
                    .Query(contact)
                    .Operator(Operator.Or)
                )
            )
        )
    )
);
  

выдает

 {
  "min_score": 2.0,
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "fields": [
              "contactEmail",
              "contactTelephone"
            ],
            "operator": "or",
            "query": "contact"
          }
        }
      ]
    }
  }
}
  

Это использует преимущества клиентской функции, называемой запросами без условий — конкретный запрос считается не имеющим условий, если компонент запроса пуст, когда он не должен быть, для формирования полного запроса. Например, для multi_match запроса он считается не имеющим условий, если Query null

Запрос с несколькими совпадениями без условий

Цель запросов без условий заключается в том, что это упрощает написание более сложных запросов, а поведение можно обойти, указав Verbatim() в запросе. Однако, поскольку они противоречат принципу наименьшего удивления, есть намерение удалить их в будущем. Для выполнения одного и того же запроса, не полагаясь на поведение без условий

 var response = client.Search<Person>(s => s
    .MinScore(minScore)
    .Query(qu => qu
        .Bool(b => b
            .Must(m =>
                {
                    if (name == null)
                        return m;
                        
                    return m
                        .MultiMatch(mm => mm
                            .Fields(fs => fs
                                .Field(f => f.Firstname)
                                .Field(f => f.Lastname)
                            )
                            .Query(name)
                            .Operator(Operator.Or)
                        );
                }, m => 
                {
                    if (contact == null)
                        return m;

                    return m
                        .MultiMatch(mm => mm
                            .Fields(fs => fs
                                .Field(f => f.ContactEmail)
                                .Field(f => f.ContactTelephone)
                            )
                            .Query(contact)
                            .Operator(Operator.Or)
                        );
                }
            )
        )
    )
);
  

Комментарии:

1. Спасибо @Russ Cam за такой качественный ответ!

2. Не беспокойтесь! Надеюсь, это поможет