Search code examples
elasticsearchnested

Elastic Search nested query does not handle multiple MUST queries


I have an ES 6.5 index with the following mappings -

{
  "hotel": {
    "mappings": {
      "hotel": {
        "properties": {
          "hotelAmenities": {
            "type": "nested",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "featured": {
                "type": "boolean"
              },
              "key": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          },
          "hotelStatus": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "name": {
            "type": "text",
            "fields": {
              "verbatim": {
                "type": "keyword"
              }
            },
            "analyzer": "english"
          }
        }
      }
    }
  }
}

Sample document -

{
  "name": "Hotel with amenities",
  "hotelStatus": "BOOKABLE",
  "hotelAmenities": [
    {
      "key": "POOL",
      "name": "Pool",
      "enabled": true,
      "featured": true
    },
    {
      "key": "LAUNDRY",
      "name": "Laundry",
      "enabled": true,
      "featured": true
    }
  ]
}

I am using a nested query to target the hotelAmenities field, and the intent is to have an AND relationship (the hotel has POOL and LAUNDRY amenity), but it only works when there is a single nested field searched.

{
  "from": 0,
  "size": 200,
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "query": {
              "bool": {
                "must": [
                  // ONLY 1 works here
                  {
                    "match": {
                      "hotelAmenities.name": "laundry"
                    }
                  },
                  {
                    "match": {
                      "hotelAmenities.name": "pool"
                    }
                  }
                ],
                "adjust_pure_negative": true,
                "boost": 1.0
              }
            },
            "path": "hotelAmenities",
            "ignore_unmapped": false,
            "score_mode": "max",
            "boost": 1.0
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "hotelStatus": {
              "query": "DECOMMISSIONED",
              "operator": "OR",
              "prefix_length": 0,
              "max_expansions": 50,
              "fuzzy_transpositions": true,
              "lenient": false,
              "zero_terms_query": "NONE",
              "auto_generate_synonyms_phrase_query": true,
              "boost": 1.0
            }
          }
        }
      ],
      "adjust_pure_negative": true,
      "boost": 1.0
    }
  },
  "post_filter": {
    "geo_distance": {
      ...
    }
  },
  "version": true
}

I also tried with a query approach on the nested fields like this, and it exhibits the same behavior where only one nested amenity can be searched.

{
  "from": 0,
  "size": 200,
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "query": {
              "bool": {
                "must": [
                  // ONLY 1 works here
                  {
                    "match": {
                      "hotelAmenities.name": {
                        "query": "laundry",
                        "operator": "OR",
                        "prefix_length": 0,
                        "max_expansions": 50,
                        "fuzzy_transpositions": true,
                        "lenient": false,
                        "zero_terms_query": "NONE",
                        "auto_generate_synonyms_phrase_query": true,
                        "boost": 1.0
                      }
                    }
                  },
                  {
                    "match": {
                      "hotelAmenities.name": {
                        "query": "pool",
                        "operator": "OR",
                        "prefix_length": 0,
                        "max_expansions": 50,
                        "fuzzy_transpositions": true,
                        "lenient": false,
                        "zero_terms_query": "NONE",
                        "auto_generate_synonyms_phrase_query": true,
                        "boost": 1.0
                      }
                    }
                  }
                ],
                "adjust_pure_negative": true,
                "boost": 1.0
              }
            },
            "path": "hotelAmenities",
            "ignore_unmapped": false,
            "score_mode": "max",
            "boost": 1.0
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "hotelStatus": {
              "query": "DECOMMISSIONED",
              "operator": "OR",
              "prefix_length": 0,
              "max_expansions": 50,
              "fuzzy_transpositions": true,
              "lenient": false,
              "zero_terms_query": "NONE",
              "auto_generate_synonyms_phrase_query": true,
              "boost": 1.0
            }
          }
        }
      ],
      "adjust_pure_negative": true,
      "boost": 1.0
    }
  },
  "post_filter": {
    "geo_distance": {
      ...
    }
  },
  "version": true
}

If I remove one or the other in my example then the documents are found correctly. What could the cause of this be?


Solution

  • Each nested element is stored in its own internal document, so if you have several constraints on those elements, you need one nested query per constraint, as shown below:

       "query": {
        "bool": {
          "must": [
            {
              "nested": {
                "query": {
                  "match": {
                    "hotelAmenities.name": "laundry"
                  }
                },
                "path": "hotelAmenities",
                "ignore_unmapped": false,
                "score_mode": "max",
                "boost": 1
              }
            },
            {
              "nested": {
                "query": {
                  "match": {
                    "hotelAmenities.name": "pool"
                  }
                },
                "path": "hotelAmenities",
                "ignore_unmapped": false,
                "score_mode": "max",
                "boost": 1
              }
            }
          ],