Inner Hits Usage
editInner Hits Usage
editThe 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 Hits
editFluent DSL example
edits => s .Index(Index) .Query(q => q.HasChild<Prince>(hc => hc .Query(hcq => hcq.Match(m => m.Field(p => p.FullTextField).Query("default"))) .InnerHits(ih => ih .DocValueFields(f => f.Field(p => p.Name)) .Name("princes") .Highlight(h => h.Fields(f => f.Field(p => p.FullTextField))) .IgnoreUnmapped(false) .Version() ) ) || q.Nested(n => n .Path(p => p.Foes) .Query(nq => nq.MatchAll()) .InnerHits(i => i.Version()) ) ) .Version()
Object Initializer syntax example
editnew SearchRequest<King>(Index) { Query = new HasChildQuery { Type = typeof(Prince), Query = new MatchQuery { Field = Field<Prince>(p => p.FullTextField), Query = "default" }, InnerHits = new InnerHits { Name = "princes", DocValueFields = Field<Prince>(p => p.Name), Highlight = Highlight.Field(Field<Prince>(p => p.FullTextField)), IgnoreUnmapped = false, Version = true } } || new NestedQuery { Path = Field<King>(p => p.Foes), Query = new MatchAllQuery(), InnerHits = new InnerHits() { Version = true } }, Version = true }
Example json output.
{ "query": { "bool": { "should": [ { "has_child": { "type": "prince", "query": { "match": { "fullTextField": { "query": "default" } } }, "inner_hits": { "name": "princes", "docvalue_fields": [ "name" ], "highlight": { "fields": { "fullTextField": {} } }, "ignore_unmapped": false, "version": true } } }, { "nested": { "query": { "match_all": {} }, "path": "foes", "inner_hits": { "version": true } } } ] } }, "version": true }
Handling Responses
editresponse.Hits.Should().NotBeEmpty(); foreach (var hit in response.Hits) { hit.Id.Should().NotBeNullOrEmpty(); hit.Index.Should().NotBeNullOrEmpty(); hit.Version.Should().Be(1); var princes = hit.InnerHits["princes"].Documents<Prince>(); princes.Should().NotBeEmpty(); foreach (var princeHit in hit.InnerHits["princes"].Hits.Hits) { var highlights = princeHit.Highlight; highlights.Should().NotBeNull("princes should have highlights"); highlights.Should().ContainKey("fullTextField", "we are highlighting this field"); var hl = highlights["fullTextField"]; hl.Should() .NotBeEmpty("all docs have the same text so should all highlight") .And.Contain(s => s.Contains("<em>default</em>"), "default to be highlighted as its part of the query"); princeHit.Fields.Should().NotBeNull("all princes have a keyword name so fields should be returned"); var docValueName = princeHit.Fields.ValueOf<Prince, string>(p => p.Name); docValueName.Should().NotBeNullOrWhiteSpace("value of name on Fields"); princeHit.Version.Should().Be(1); } var foes = hit.InnerHits["foes"].Documents<King>(); foes.Should().NotBeEmpty(); }