Объединение вложенного запроса с логическим запросом в эластичном поиске

#elasticsearch #elasticsearch-nested

#elasticsearch #elasticsearch-вложенный

Вопрос:

Я пытаюсь отфильтровать гостиничные номера по ценовому диапазону в эластичном поиске. В номерах установлена цена за ночь по умолчанию, а также могут быть установлены пользовательские цены на определенные дни.

Я сохраняю nightlyPrice и вложенный объект для пользовательских цен вместе с датами. Сопоставление является smt. Нравится:

 {
  "adverts": {
    "mappings": {
      "advert": {
        "properties": {
          "nightlyPrice": {"type": "float"},
          "customPrices": {
            "type": "nested",
            "properties": {
              "date": {"type": "date"},
              "price": {"type": "float"}
            }
          }
        }
      }
    }
  }
}
  

Например, я хочу получить номера в ценовом диапазоне от 100 до 200 долларов в период с 1 по 7 июля.

Итак, я придумал эту логику:

  1. Либо customPrices.date должно быть между 2019-07-01 и 2019-07-07 и customPrices.price между 100 и 200.
  2. или nightlyPrice должно быть от 100 до 200, а между 05 и 07 июля не customPrices.date установлено.

Однако я не смог применить эту логику к эластичному поиску, вложенные объекты / запросы, я думаю, довольно сложны.

Это последний запрос, который я придумал:

 {
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "status": "active"
          }
        }
      ],
      "must": [
        {
          "bool": {
            "should": [
              {
                "nested": {
                  "path": "customPrices",
                  "query": {
                    "bool": {
                      "must": [
                        {
                          "range": {
                            "date": {
                              "from": "2019-07-01",
                              "to": "2019-07-07"
                            }
                          }
                        },
                        {
                          "range": {
                            "price": {
                              "from": 100,
                              "to": 200
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              },
              {
                "bool": {
                  "must": [
                    {
                      "range": {
                        "nightlyPrice": {
                          "from": 100,
                          "to": 200
                        }
                      }
                    }
                  ],
                  "must_not": [
                    {
                      "nested": {
                        "path": "customPrices",
                        "query": {
                          "range": {
                            "customPrices.date": {
                              "from": "2019-07-01",
                              "to": "2019-07-07"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  }
}

  

Проблема с этим запросом заключается в том, что если пользовательские цены.дата соответствует диапазону дат, он никогда не соответствует документу, независимо от ценового диапазона. Я экспериментировал с ценовым диапазоном от 1 до 100000 долларов, и он по-прежнему не соответствует.

Пытался использовать explain API, чтобы понять, почему конкретный документ не соответствует, но я этого не понимаю, там написано user requested match_none query, но есть этот should запрос, поэтому он должен соответствовать вложенному запросу (первому):

 {
  "_index": "adverts",
  "_type": "advert",
  "_id": "13867",
  "matched": false,
  "explanation": {
    "value": 0.0,
    "description": "Failure to meet condition(s) of required/prohibited clause(s)",
    "details": [
      {
        "value": 0.0,
        "description": "no match on required clause ( (ToParentBlockJoinQuery (MatchNoDocsQuery("User requested "match_none" query.")) ( nightlyPrice:[100.0 TO 200.0] -ToParentBlockJoinQuery (customListingPrices.date:[1561939200000 TO 1562543999999]))) #status:active",
        "details": [
          {
            "value": 0.0,
            "description": "Failure to meet condition(s) of required/prohibited clause(s)",
            "details": [
              {
                "value": 0.0,
                "description": "no match on required clause (ToParentBlockJoinQuery (MatchNoDocsQuery("User requested "match_none" query.")) ( nightlyPrice:[100.0 TO 200.0] -ToParentBlockJoinQuery (customListingPrices.date:[1561939200000 TO 1562543999999])))",
                "details": [
                  {
                    "value": 0.0,
                    "description": "No matching clauses",
                    "details": []
                  }
                ]
              },
              {
                "value": 0.0,
                "description": "match on required clause, product of:",
                "details": [
                  {
                    "value": 0.0,
                    "description": "# clause",
                    "details": []
                  },
                  {
                    "value": 1.0,
                    "description": "status:active",
                    "details": []
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "value": 0.0,
        "description": "match on required clause, product of:",
        "details": [
          {
            "value": 0.0,
            "description": "# clause",
            "details": []
          },
          {
            "value": 1.0,
            "description": "DocValuesFieldExistsQuery [field=_primary_term]",
            "details": []
          }
        ]
      }
    ]
  }
}
  

Любая помощь или идея приветствуются…

Ответ №1:

Если вы внимательно посмотрите на первое must предложение, окажется, что вы не указали весь путь к полю.

 {  
   "range":{  
      "date":{               <-- must be "customPrices.date"
         "from":"2019-07-01",
         "to":"2019-07-07"
      }
   }
},
{  
   "range":{  
      "price":{             <-- must be "customPrices.price"
         "from":100,
         "to":200
      }
   }
}
  

Ниже показано, каким должен быть запрос, и он должен нормально работать в вашем случае использования.

Запрос

 POST <your_index_name>/_search
{  
   "query":{  
      "bool":{  
         "filter":{  
            "term":{  
               "status":"active"
            }
         },
         "must":[  
            {  
               "bool":{  
                  "should":[  
                     {  
                        "bool":{  
                           "must":[  
                              {  
                                 "nested":{  
                                    "path":"customPrices",
                                    "query":{  
                                       "bool":{  
                                          "must":[  
                                             {  
                                                "range":{  
                                                   "customPrices.date":{  
                                                      "gte":"2019-07-01",
                                                      "lte":"2019-07-09"
                                                   }
                                                }
                                             },
                                             {  
                                                "range":{  
                                                   "customPrices.price":{  
                                                      "gte":100,
                                                      "lte":200
                                                   }
                                                }
                                             }
                                          ]
                                       }
                                    }
                                 }
                              }
                           ]
                        }
                     },
                     {  
                        "bool":{  
                           "must":[  
                              {  
                                 "range":{  
                                    "nightlyPrice":{  
                                       "gte":100,
                                       "lte":200
                                    }
                                 }
                              }
                           ],
                           "must_not":[  
                              {  
                                 "nested":{  
                                    "path":"customPrices",
                                    "query":{  
                                       "range":{  
                                          "customPrices.date":{  
                                             "gte":"2019-07-05",
                                             "lte":"2019-07-07"
                                          }
                                       }
                                    }
                                 }
                              }
                           ]
                        }
                     }
                  ]
               }
            }
         ]
      }
   }
}
  

Надеюсь, это поможет!

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

1. привет, @ madpoet, вышеуказанное решило проблему, с которой вы столкнулись. Вы ищете что-то еще?

2. Любопытно, почему у вас есть как should , так и a must вокруг вложенного запроса. Конечно, must само по себе прекрасно?

3. Ну, изначально я думал об этом, но, возможно, у OP должен быть гораздо более сложный запрос, который он не раскрыл. Я только что поделился с ним точным решением, относящимся к проблеме, из-за которой он застрял. Также это зависит от глубины запроса, например, что, если у него есть несколько предложений should и must на разных глубинах . Возможно, именно поэтому я думаю, что он знает, что делает, и поделился с ним решением, в котором он застрял. Я видел гораздо более сложные запросы на гораздо более глубоких уровнях, и я собираюсь дать ему возможность усомниться в этом.