0

I'm very new to elastic search, how do I write a query which search for a keyword (ie. test keyword) in all fields in the document, and one more keyword which search in a specific field.

this can be done using query_string but we can't do search in nested fields with nested field specified, So i'm using LUQUM to convert lucene query to Elasticsearch DSL.

Below is the sample usecase:

I have a mapping:

"mappings": {
    "properties": {
      "grocery_name":{
        "type": "text"
       },
      "items": {
        "type": "nested",
        "properties": {
          "name": {
            "type": "text"
          },
          "stock": {
            "type": "integer"
          },
          "category": {
            "type": "text"
          }
        }
      }
    }
  }
}

and the data looks like below

{
  "grocery_name": "Elastic Eats",
  "items": [
    {
      "name": "Red banana",
      "stock": "12",
      "category": "fruit"
    },
    {
      "name": "Cavendish banana",
      "stock": "10",
      "category": "fruit"
    },
    {
      "name": "peach",
      "stock": "10",
      "category": "fruit"
    },
    {
      "name": "carrot",
      "stock": "9",
      "category": "vegetable"
    },
    {
      "name": "broccoli",
      "stock": "5",
      "category": "vegetable"
    }
  ]
}

How can I query to get all items where the item name matches banana from grocery_name: Elastic Eats ?

tried with * and _all, it didn't work.

example query:

{
   "query": {
        "bool": {
            "must": [
                {
                    "match_phrase": {
                        "grocery_name": {
                            "query": "Elastic Eats"
                        }
                    }
                },
                {
                    "match": {
                        "*": {
                            "query": "banana",
                            "zero_terms_query": "all"
                        }
                    }
                }
            ]
        }
    }
}

I'm sure I'm missing something obvious, but I have read the manual and I'm getting no joy at all.

UPDATE: enabling include_in_parent for nested object works for below query, but it will internally duplicates data which will definitely impact on memory.

{
  "query": {
    "bool": {
      "must": [
        {
          "match_phrase": {
            "grocery_name": {
              "query": "Elastic Eats"
            }
          }
        },
        {
          "multi_match": {
              "query": "banana"
          }
        }
      ]
    }
  }
}

Is there any other way to do this?

Code Wizard
  • 46
  • 2
  • 7
  • did you get a chance to go through my answer, looking forward to get feedback from you – ESCoder Oct 09 '20 at 05:37
  • please don't forget to upvote and accept my answer, as it helped you resolve your issue +1 for your question – ESCoder Oct 09 '20 at 08:02
  • thank u for accepting my answer, can you please upvote my answer as well – ESCoder Oct 09 '20 at 08:30
  • Sorry!! my bad, i removed the answer i had posted, it was wrong and it doesn't work, it was working because when i was testing that i was adding data in template and searching in different index, any other answers are welcome. – Code Wizard Oct 09 '20 at 12:47
  • did you get a chance to go through my answer, looking forward to get feedback from you – ESCoder Oct 10 '20 at 01:51

1 Answers1

1

You need to use a nested match query with inner_hits resulting in an inner nested query to automatically match the relevant nesting level, rather than root

Search Query

 {
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "grocery_name": "elastic"
          }
        },
        {
          "nested": {
            "path": "items",
            "query": {
              "bool": {
                "must": [
                  {
                    "match": {
                      "items.name": "banana"
                    }
                  }
                ]
              }
            },
            "inner_hits": {}
          }
        }
      ]
    }
  }
}

Search Result:

 "inner_hits": {
          "items": {
            "hits": {
              "total": {
                "value": 2,
                "relation": "eq"
              },
              "max_score": 0.744874,
              "hits": [
                {
                  "_index": "stof_64273970",
                  "_type": "_doc",
                  "_id": "1",
                  "_nested": {
                    "field": "items",
                    "offset": 0
                  },
                  "_score": 0.744874,
                  "_source": {
                    "name": "Red banana",
                    "stock": "12",
                    "category": "fruit"
                  }
                },
                {
                  "_index": "stof_64273970",
                  "_type": "_doc",
                  "_id": "1",
                  "_nested": {
                    "field": "items",
                    "offset": 1
                  },
                  "_score": 0.744874,
                  "_source": {
                    "name": "Cavendish banana",
                    "stock": "10",
                    "category": "fruit"
                  }
                }
              ]
            }

Update 1:

On the basis of your comments, you can use multi match query, for your use case

If no fields are provided, the multi_match query defaults to the index.query.default_field index settings, which in turn defaults to *.

(*) extracts all fields in the mapping that are eligible to term queries and filters the metadata fields. All extracted fields are then combined to build a query.

Search Query:

    {
      "query": {
        "bool": {
          "filter": [
            {
              "term": {
                "grocery_name": "elastic"
              }
            },
            {
              "nested": {
                "path": "items",
                "query": {
                  "bool": {
                    "must": [
                      {
                        "multi_match": {
                          "query": "banana"    <-- note this
                        }
                      }
                    ]
                  }
                },
                "inner_hits": {}
              }
            }
          ]
        }
      }
    }

Update 2:

You need to use a combination of multiple bool queries like this:

{
  "query": {
    "bool": {
      "must": [
        {
          "match_phrase": {
            "grocery_name": {
              "query": "Elastic Eats"
            }
          }
        },
        {
          "bool": {
            "should": [
              {
                "bool": {
                  "must": [
                    {
                      "multi_match": {
                        "query": "banana"
                      }
                    }
                  ]
                }
              },
              {
                "bool": {
                  "must": [
                    {
                      "nested": {
                        "path": "items",
                        "query": {
                          "bool": {
                            "must": [
                              {
                                "multi_match": {
                                  "query": "banana"
                                }
                              }
                            ]
                          }
                        },
                        "inner_hits": {}
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  }
}
ESCoder
  • 15,431
  • 2
  • 19
  • 42
  • Thanks for the response, nested query match does work only if i give the field name, my use case is different i.e, when there is no field name specified **"items.name"** – Code Wizard Oct 09 '20 at 06:40
  • @CodeWizard please go through my updated answer, and let me know if this resolved your issue ? – ESCoder Oct 09 '20 at 06:47
  • how would i know that i have to search in nested item field, if i have another field which also contains banana in its value, i want to search banana in entire document. thanks for the suggestion of multi match i found the solution for my usecase i will update it. – Code Wizard Oct 09 '20 at 07:21
  • @Code Wizard I saw that you unaccepted my answer Please go through my updated answer, and let me know if this resolved your issue – ESCoder Oct 09 '20 at 14:59
  • Sorry for that, but in your answer i see that we have to give path for nested fields but if i create multiple nested types and some of its own types, upload a document, and attempt to query the document and all its nested documents, but fail. Is there some way to do this without duplicating the query for each nested document, or is that actually a performant way to do this? Thanks. – Code Wizard Oct 10 '20 at 04:34
  • @CodeWizard thanks for your reply . But each time when you want to query on different nested field documents, then you have to specify its nested path, and then only you can query on that. All these queries have to be wrapped inside multiple bool queries (as shown in my updated query). I have tried locally also with my updated query (where `banana` was present in some other field also, apart from the nested field, and it is giving me appropriate results. According to me, I don't think, that there is any other way to achieve your use case – ESCoder Oct 10 '20 at 04:39
  • @CodeWizard its been a long time, it would be great if you can accept and upvote my answer if it helped you resolve your issue – ESCoder Nov 06 '20 at 02:27
  • @CodeWizard thank u for accepting my answer, can you please upvote my answer as well :) – ESCoder Nov 10 '20 at 06:17