Inner Hits Usageedit

The parent/child and nested features allow the return of documents that have matches in a different scope. In the parent/child case, parent document are returned based on matches in child documents or child document are returned based on matches in parent documents. In the nested case, documents are returned based on matches in nested inner objects.

In both cases, the actual matches in the different scopes that caused a document to be returned is hidden. In many cases, it’s very useful to know which inner nested objects (in the case of nested) or children/parent documents (in the case of parent/child) caused certain information to be returned. The inner hits feature can be used for this. This feature returns per search hit in the search response additional nested hits that caused a search hit to match in a different scope.

Inner hits can be used by defining an inner_hits definition on a nested, has_child or has_parent query and filter.

See the Elasticsearch documentation on Inner hits for more detail.

Query Inner Hitsedit

Fluent DSL exampleedit

s => s
.Index(Index)
.Query(q =>
    q.HasChild<Prince>(hc => hc
        .Query(hcq => hcq.MatchAll())
        .InnerHits(ih => ih.Name("princes"))
    ) || q.Nested(n => n
        .Path(p => p.Foes)
        .Query(nq => nq.MatchAll())
        .InnerHits()
    )
)

Object Initializer syntax exampleedit

new SearchRequest<King>(Index, typeof(King))
{
    Query = new HasChildQuery
    {
        Type = typeof(Prince),
        Query = new MatchAllQuery(),
        InnerHits = new InnerHits { Name = "princes" }
    } || new NestedQuery
    {
        Path = Field<King>(p => p.Foes),
        Query = new MatchAllQuery(),
        InnerHits = new InnerHits()
    }
}

Example json output. 

{
  "query": {
    "bool": {
      "should": [
        {
          "has_child": {
            "type": "prince",
            "query": {
              "match_all": {}
            },
            "inner_hits": {
              "name": "princes"
            }
          }
        },
        {
          "nested": {
            "query": {
              "match_all": {}
            },
            "path": "foes",
            "inner_hits": {}
          }
        }
      ]
    }
  }
}

Handling Responsesedit

response.Hits.Should().NotBeEmpty();
foreach (var hit in response.Hits)
{
    var princes = hit.InnerHits["princes"].Documents<Prince>();
    princes.Should().NotBeEmpty();

    var foes = hit.InnerHits["foes"].Documents<King>();
    foes.Should().NotBeEmpty();
};