Поле запроса эластичного гнезда должно существовать filter - PullRequest
0 голосов
/ 14 ноября 2018

Я использую эластичный поиск 5.2 с клиентом Nest для запроса. У меня рабочий запрос с диапазоном дат выглядит так:

var boolQuery = new BoolQueryDescriptor<AttractionDocument>();

//https://github.com/elastic/elasticsearch-net/issues/2570 must is not additive, we cannot split out query as before it all has to be one big one

boolQuery.Must(
    mn => AddRegionQuery(permissions, mn),
    mn => AddOffersQuery(permissions, mn),
    mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableFrom).LessThanOrEquals(DateTime.Now)) : mn,
    mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableTo).GreaterThanOrEquals(DateTime.Now)) : mn,
    mn => AddGenresQuery(genres, mn)
);

Проблема, с которой я сталкиваюсь, доступна: не всегда заполнена, поэтому для некоторых документов она не существует.

Я пытался добавить следующее:

if (request.AddDateFilter)
{
    boolQuery.MustNot(mn => mn.Exists(f => f.Field(e => e.AvailableTo)));
}

Проблема в том, что запрос становится слишком ограниченным. В идеале существует часть, которую я хочу, как следует? Я пытаюсь добиться только применения диапазона дат для AvailableTo, если у нас есть это поле, иначе игнорируем и возвращаем результаты без этой даты. Если я достану деталь AvailableTo, я получу результаты.

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Вы должны иметь возможность объединить запрос exists с запросом range в AvailableTo, чтобы включить документы, для которых существует поле AvailableTo и которые должны удовлетворять условию диапазона, и создать дизъюнкцию с AvailableToсуществует запрос в предложении bool query must_not, т.е. инвертирует существующий.

Что-то вроде следующего (я закомментировал запросы, которые не были предоставлены)

var client = new ElasticClient(settings);

var request = new 
{
    AddDateFilter = true
};

var boolQuery = new BoolQueryDescriptor<AttractionDocument>();

boolQuery.Must(
    // mn => AddRegionQuery(permissions, mn),
    // mn => AddOffersQuery(permissions, mn),
    mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableFrom).LessThanOrEquals(DateTime.Now)) : mn,
    mn => request.AddDateFilter ? (mn.Exists(d => d.Field(f => f.AvailableTo)) &&
                                  mn.DateRange(d => d.Field(f => f.AvailableTo).GreaterThanOrEquals(DateTime.Now))) ||
                                  !mn.Exists(d => d.Field(f => f.AvailableTo)) : mn //,
    // mn => AddGenresQuery(genres, mn)
);

client.Search<AttractionDocument>(s => s
    .Query(q => q.Bool(b => boolQuery))
);

Это производит следующий запрос

{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "availableFrom": {
              "lte": "2018-11-15T20:18:10.528482+10:00"
            }
          }
        },
        {
          "bool": {
            "should": [
              {
                "bool": {
                  "must": [
                    {
                      "exists": {
                        "field": "availableTo"
                      }
                    },
                    {
                      "range": {
                        "availableTo": {
                          "gte": "2018-11-15T20:18:10.5304815+10:00"
                        }
                      }
                    }
                  ]
                }
              },
              {
                "bool": {
                  "must_not": [
                    {
                      "exists": {
                        "field": "availableTo"
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Поскольку диапазон исуществующие запросы являются предикатами (документ либо соответствует условию, либо нет) в отличие от запросов, которые должны вычислять оценки релевантности, это могут быть bool query filter условия

boolQuery.Must(
    // Uncomment below queries, or add (QueryContainer[])null to run
    // mn => AddRegionQuery(permissions, mn),
    // mn => AddOffersQuery(permissions, mn),
    // mn => AddGenresQuery(genres, mn)
).Filter(
    mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableFrom).LessThanOrEquals(DateTime.Now)) : mn,
    mn => request.AddDateFilter ? (+mn.Exists(d => d.Field(f => f.AvailableTo)) &&
                                  +mn.DateRange(d => d.Field(f => f.AvailableTo).GreaterThanOrEquals(DateTime.Now))) ||
                                  !mn.Exists(d => d.Field(f => f.AvailableTo)) : mn    
);

client.Search<AttractionDocument>(s => s
    .Query(q => q.Bool(b => boolQuery))
);

, которые создаютquery

{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "availableFrom": {
              "lte": "2018-11-15T20:22:25.4556963+10:00"
            }
          }
        },
        {
          "bool": {
            "should": [
              {
                "bool": {
                  "filter": [
                    {
                      "exists": {
                        "field": "availableTo"
                      }
                    },
                    {
                      "range": {
                        "availableTo": {
                          "gte": "2018-11-15T20:22:25.4587138+10:00"
                        }
                      }
                    }
                  ]
                }
              },
              {
                "bool": {
                  "must_not": [
                    {
                      "exists": {
                        "field": "availableTo"
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Перегрузка операторов на запросы действительно помогает здесь, более кратко писать сложные запросы bool

0 голосов
/ 14 ноября 2018

Хорошо, так что, думаю, я подозревал.Я вынул обязательный диапазон дат и сделал его обязательным, поэтому запрос теперь выглядит следующим образом:

 var boolQuery = new BoolQueryDescriptor<AttractionDocument>();

        if (request.AddDateFilter)
        {
            boolQuery.Should(mn => mn.DateRange(d => d.Field(f => f.AvailableTo).GreaterThanOrEquals(DateTime.Now)));
        }


        //https://github.com/elastic/elasticsearch-net/issues/2570 must is not additive, we cannot split out query as before it all has to be one big one
        boolQuery.Must(
            mn => AddRegionQuery(permissions, mn),

            mn => AddOffersQuery(permissions, mn),

            mn => request.AddDateFilter ? mn.DateRange(d => d.Field(f => f.AvailableFrom).LessThanOrEquals(DateTime.Now)) : mn,

            mn => AddGenresQuery(genres, mn)
         );

Теперь я получаю результаты с диапазоном как для дат, так и для совпадений, для которых у нас нет установленной даты availableTo.

...