Chris Koiak
Chris Koiak

Reputation: 1059

Return parent data with child document from Elasticsearch

Is is possible to return parent data with a search for child documents within an Elasticsearch query?

I have two document types, e.g. Book and Chapter, that are related as Parent/Child (not nested).

I want to run a search on the child document and return the child document, with some of the fields from the parent document. I'm trying to avoid executing a separate query on the parent.

Update

The only way possible I can find is to use the has_child query and then a series of aggregations to drill back to the children and apply the query/filter again. However, this seems overly complicated and inefficient.

GET index/_search
{
  "size": 10,
  "query": {
    "has_child": {
      "type": "chapter",
  "query": {
    "term": {
      "field": "value"
        }
      }
    }
  },
  "aggs": {
"name1": {
  "terms": {
    "size": 50,
    "field": "id"
  },
  "aggs": {
    "name2": {
      "top_hits": {
        "size": 50
      }
    },
    "name3": {
      "children": {
        "type": "type2"
      },
      "aggs": {
        "docFilter": {
          "filter": {
            "query": {
              "match": {
                "_all": "value"
              }
            }
          },
          "aggs": {
            "docs": {
              "top_hits": {
                "size": 50
              }
            }
          }
        }
      }
    }
  }
}
  }
}

Upvotes: 6

Views: 11061

Answers (3)

Luke
Luke

Reputation: 3101

This can be now be done with ElasticSearch. Just use 'has_parent' in the search query:

'has_parent': {
    'parent_type': 'book',
    'query': {
        'match_all': {}
    },
    'inner_hits': {}
}

The results will appear in the inner_hits of the response.

Upvotes: 3

Dani
Dani

Reputation: 4111

As Dan Tuffery say in his comment, currently, this can be achieve with Inner Hits, in Java you can understand it more easy with the next snippet of code.

SearchResponse searchResponse = this.transportClient.prepareSearch("your_index")
                .setTypes("your_type")
                .setQuery(QueryBuilders.filteredQuery(
                                null,
                                FilterBuilders.hasParentFilter(
                                        "parent_type_name",
                                        FilterBuilders.termFilter("foo", "foo"))
                                .innerHit(new QueryInnerHitBuilder()))
                )
                .execute().actionGet();

List<YourObject> list = new ArrayList<>();

for (SearchHit searchHit : searchHits.getHits()) {

    YourObject yourObject = this.objectMapper.readValue(searchHit.getSourceAsString(), YourObject.class);
    yourObject.setYourParentObject(this.objectMapper.readValue(searchHit.getInnerHits().get("parent_type_name").getAt(0).getSourceAsString(), YourParentObject.class));

    list.add(yourObject);
}

Upvotes: 0

Dan Tuffery
Dan Tuffery

Reputation: 5924

It is possible do a has_child query to return the parent docs with a top hits aggregation to return the child docs, but it is a bit cumbersome.

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-metrics-top-hits-aggregation.html

The Inner Hits feature that is due to be released in 1.5.0 will do what you want.

http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.x/search-request-inner-hits.html

You could build the source from master and try it out.

Upvotes: 1

Related Questions