Search code examples
elasticsearchsearchmappingparent-child

Parent child relationship in ElasticSearch - search for a sentence in all the child docs combined


I am super new to Elastic Search. I have a use case which seems can be solved by the parent-child relationship. Parent doc contains the description of an NGO. Child doc contains various feedbacks sent to the NGO.

Parent Doc structure
{
    name
    address
    description
}

Child doc
{
    feedbackContent
}

Let's say, NGO-A 4 feedbacks (meaning 4 child documents)

  • best teachers

  • best facilities

  • good students

  • location is too far

Another NGO-B has 2 feedbacks (meaning 2 child documents)

  • good food quality

  • awesome location

The client should be able to look up the NGOs which has all the terms in the query string passed. Example - client searched for "best" AND "location".

Since best is present in child1 and child2 and location is present in child 4, NGO-A is a valid output. However, for NGO-B child2 contains one search term and the other search term is not present in any other child doc so NGO-B is not a valid result.

I read the doc - https://blog.mimacom.com/parent-child-elasticsearch/ which is quite good but unable to conclude if this can be done.

Examples I tried

PUT message_index
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0,
    "mapping.single_type": true
  },
  "mappings": {
    "doc": {
      "properties": {
        "ngo": {"type": "text"},
        "feedback": {"type": "text"},
        "ngo_relations": {
          "type": "join",
          "relations": {
            "ngo": "feedback"
          }
        }
      }
    }
  }
}

POST message_index/doc/_bulk
{"index": {"_id":1}}
{"name":"teach for india", "ngo_relations": {"name":"ngo"}}
{"index":{"_id":2}}
{"name":"hope for autism", "ngo_relations": {"name":"ngo"}}

PUT message_index/doc/3?routing=1
{"feedback":"best food","ngo_relations":{"name":"feedback", "parent":1}}

PUT message_index/doc/4?routing=1
{"feedback":"average location","ngo_relations":{"name":"feedback", "parent":1}}

PUT message_index/doc/5?routing=1
{"feedback":"awesome staff","ngo_relations":{"name":"feedback", "parent":1}}

PUT message_index/doc/6?routing=2
{"feedback":"best teachers","ngo_relations":{"name":"feedback", "parent":2}}

PUT message_index/doc/7?routing=2
{"feedback":"awesome overload","ngo_relations":{"name":"feedback", "parent":2}}

For best and location search, just teach for india NGO should be returned.

No hits:

GET message_index/_search
{
  "query": {
    "has_child": {
      "type": "feedback",
      "query": {
        "bool": {
          "must": {
            "term": {"feedback": "best"}
          },
          "must": {
            "term": {"feedback": "location"}
          }
        }
      }
    }
  }
}

Both the documents are returned

GET message_index/_search
{
  "query": {
    "has_child": {
      "type": "feedback",
      "query": {
        "bool": {
          "should": {
            "term": {"feedback": "best"}
          },
          "should": {
            "term": {"feedback": "location"}
          }
        }
      }
    }
  }
}

Solution

  • This can be done. You were close just a small mistake in the query.

    In your child query, you are performing a bool with two must/should. Therefore, your query is: Give me all documents such that they have a child such that the child has both (or 'one of the' in case of should) the terms "best" and "location".

    Whereas, what you want is: Give me all documents such that they have a child such that the child has the term "best" and also have a child such that the child has the term "location".

    Tweak your query as follows:

    GET message_index/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "has_child": {
                "type": "feedback",
                "query": {
                  "term": {
                    "feedback": "best"
                  }
                }
              }
            },
            {
              "has_child": {
                "type": "feedback",
                "query": {
                  "term": {
                    "feedback": "location"
                  }
                }
              }
            }
          ]
        }
      }
    }