Skip to content

Commit 4a19bdb

Browse files
authored
Support the 'fields' option in inner_hits and top_hits. (#62337)
This PR adds support for the 'fields' option in the following places: * Anytime `inner_hits` is used, for both fetching nested/ child docs and field collapsing * The `top_hits` aggregation Addresses #61949.
1 parent 3d5c13f commit 4a19bdb

File tree

15 files changed

+299
-83
lines changed

15 files changed

+299
-83
lines changed

docs/reference/aggregations/metrics/tophits-aggregation.asciidoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ The top_hits aggregation returns regular search hits, because of this many per h
2424
* <<highlighting,Highlighting>>
2525
* <<request-body-search-explain,Explain>>
2626
* <<named-queries,Named queries>>
27+
* <<search-fields-param,Search fields>>
2728
* <<source-filtering,Source filtering>>
2829
* <<stored-fields,Stored fields>>
2930
* <<script-fields,Script fields>>
@@ -41,7 +42,7 @@ by aggregations, use a <<query-dsl-function-score-query,`function_score`>> or
4142

4243
==== Example
4344

44-
In the following example we group the sales by type and per type we show the last sale.
45+
In the following example we group the sales by type and per type we show the last sale.
4546
For each sale only the date and price fields are being included in the source.
4647

4748
[source,console]

docs/reference/search/search-your-data/retrieve-inner-hits.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Inner hits also supports the following per document features:
7474

7575
* <<highlighting,Highlighting>>
7676
* <<request-body-search-explain,Explain>>
77+
* <<search-fields-param,Search fields>>
7778
* <<request-body-search-source-filtering,Source filtering>>
7879
* <<script-fields,Script fields>>
7980
* <<docvalue-fields,Doc value fields>>

modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public void testSimpleParentChild() throws Exception {
176176
.setQuery(
177177
hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(
178178
new InnerHitBuilder()
179-
.addDocValueField("message")
179+
.addFetchField("message")
180180
.setHighlightBuilder(new HighlightBuilder().field("message"))
181181
.setExplain(true).setSize(1)
182182
.addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5",
@@ -187,8 +187,18 @@ public void testSimpleParentChild() throws Exception {
187187
assertThat(innerHits.getHits().length, equalTo(1));
188188
assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("<em>fox</em> eat quick"));
189189
assertThat(innerHits.getAt(0).getExplanation().toString(), containsString("weight(message:fox"));
190-
assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat"));
190+
assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("fox eat quick"));
191191
assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5"));
192+
193+
response = client().prepareSearch("articles")
194+
.setQuery(
195+
hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(
196+
new InnerHitBuilder().addDocValueField("message").setSize(1)
197+
)).get();
198+
assertNoFailures(response);
199+
innerHits = response.getHits().getAt(0).getInnerHits().get("comment");
200+
assertThat(innerHits.getHits().length, equalTo(1));
201+
assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat"));
192202
}
193203

194204
public void testRandomParentChild() throws Exception {

rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,49 +6,50 @@ setup:
66
mappings:
77
properties:
88
numeric_group: { type: integer }
9+
tag: { type: keyword }
910

1011
- do:
1112
index:
1213
index: test
1314
id: 1
1415
version_type: external
1516
version: 11
16-
body: { numeric_group: 1, sort: 10 }
17+
body: { numeric_group: 1, tag: A, sort: 10 }
1718
- do:
1819
index:
1920
index: test
2021
id: 2
2122
version_type: external
2223
version: 22
23-
body: { numeric_group: 1, sort: 6 }
24+
body: { numeric_group: 1, tag: B, sort: 6 }
2425
- do:
2526
index:
2627
index: test
2728
id: 3
2829
version_type: external
2930
version: 33
30-
body: { numeric_group: 1, sort: 24 }
31+
body: { numeric_group: 1, tag: A, sort: 24 }
3132
- do:
3233
index:
3334
index: test
3435
id: 4
3536
version_type: external
3637
version: 44
37-
body: { numeric_group: 25, sort: 10 }
38+
body: { numeric_group: 25, tag: B, sort: 10 }
3839
- do:
3940
index:
4041
index: test
4142
id: 5
4243
version_type: external
4344
version: 55
44-
body: { numeric_group: 25, sort: 5 }
45+
body: { numeric_group: 25, tag: A, sort: 5 }
4546
- do:
4647
index:
4748
index: test
4849
id: 6
4950
version_type: external
5051
version: 66
51-
body: { numeric_group: 3, sort: 36 }
52+
body: { numeric_group: 3, tag: B, sort: 36 }
5253
- do:
5354
indices.refresh:
5455
index: test
@@ -161,6 +162,40 @@ setup:
161162
- match: { hits.hits.2.inner_hits.sub_hits.hits.hits.0._id: "5" }
162163
- match: { hits.hits.2.inner_hits.sub_hits.hits.hits.1._id: "4" }
163164

165+
---
166+
"field collapsing, inner_hits, and fields":
167+
- skip:
168+
version: " - 7.9.99"
169+
reason: the fields option was added in 7.10
170+
- do:
171+
search:
172+
rest_total_hits_as_int: true
173+
index: test
174+
body:
175+
collapse:
176+
field: numeric_group
177+
inner_hits:
178+
name: sub_hits
179+
size: 2
180+
sort: [{ sort: asc }]
181+
fields: ["tag"]
182+
sort: [{ sort: desc }]
183+
184+
- length: { hits.hits: 3 }
185+
- length: { hits.hits.0.inner_hits.sub_hits.hits.hits: 1 }
186+
- match: { hits.hits.0.inner_hits.sub_hits.hits.hits.0._id: "6" }
187+
- match: { hits.hits.0.inner_hits.sub_hits.hits.hits.0.fields.tag: ["B"]}
188+
- length: { hits.hits.1.inner_hits.sub_hits.hits.hits: 2 }
189+
- match: { hits.hits.1.inner_hits.sub_hits.hits.hits.0._id: "2" }
190+
- match: { hits.hits.1.inner_hits.sub_hits.hits.hits.0.fields.tag: ["B"]}
191+
- match: { hits.hits.1.inner_hits.sub_hits.hits.hits.1._id: "1" }
192+
- match: { hits.hits.1.inner_hits.sub_hits.hits.hits.1.fields.tag: ["A"]}
193+
- length: { hits.hits.2.inner_hits.sub_hits.hits.hits: 2 }
194+
- match: { hits.hits.2.inner_hits.sub_hits.hits.hits.0._id: "5" }
195+
- match: { hits.hits.2.inner_hits.sub_hits.hits.hits.0.fields.tag: ["A"]}
196+
- match: { hits.hits.2.inner_hits.sub_hits.hits.hits.1._id: "4" }
197+
- match: { hits.hits.2.inner_hits.sub_hits.hits.hits.1.fields.tag: ["B"]}
198+
164199
---
165200
"field collapsing, inner_hits and maxConcurrentGroupRequests":
166201

server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ public void setupSuiteScopeCluster() throws Exception {
166166
.field(SORT_FIELD, i + 1)
167167
.field("text", "some text to entertain")
168168
.field("field1", 5)
169+
.field("field2", 2.71)
169170
.endObject()));
170171
}
171172

@@ -315,7 +316,7 @@ public void testBasics() throws Exception {
315316
assertThat((Long) hits.getAt(1).getSortValues()[0], equalTo(higestSortValue - 1));
316317
assertThat((Long) hits.getAt(2).getSortValues()[0], equalTo(higestSortValue - 2));
317318

318-
assertThat(hits.getAt(0).getSourceAsMap().size(), equalTo(4));
319+
assertThat(hits.getAt(0).getSourceAsMap().size(), equalTo(5));
319320
}
320321
}
321322

@@ -402,7 +403,7 @@ public void testBreadthFirstWithScoreNeeded() throws Exception {
402403
assertThat(hits.getTotalHits().value, equalTo(10L));
403404
assertThat(hits.getHits().length, equalTo(3));
404405

405-
assertThat(hits.getAt(0).getSourceAsMap().size(), equalTo(4));
406+
assertThat(hits.getAt(0).getSourceAsMap().size(), equalTo(5));
406407
}
407408
}
408409

@@ -433,7 +434,7 @@ public void testBreadthFirstWithAggOrderAndScoreNeeded() throws Exception {
433434
assertThat(hits.getTotalHits().value, equalTo(10L));
434435
assertThat(hits.getHits().length, equalTo(3));
435436

436-
assertThat(hits.getAt(0).getSourceAsMap().size(), equalTo(4));
437+
assertThat(hits.getAt(0).getSourceAsMap().size(), equalTo(5));
437438
id--;
438439
}
439440
}
@@ -597,6 +598,7 @@ public void testFetchFeatures() {
597598
.explain(true)
598599
.storedField("text")
599600
.docValueField("field1")
601+
.fetchField("field2")
600602
.scriptField("script",
601603
new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", Collections.emptyMap()))
602604
.fetchSource("text", null)
@@ -639,13 +641,16 @@ public void testFetchFeatures() {
639641

640642
assertThat(hit.getMatchedQueries()[0], equalTo("test"));
641643

642-
DocumentField field = hit.field("field1");
643-
assertThat(field.getValue().toString(), equalTo("5"));
644+
DocumentField field1 = hit.field("field1");
645+
assertThat(field1.getValue(), equalTo(5L));
646+
647+
DocumentField field2 = hit.field("field2");
648+
assertThat(field2.getValue(), equalTo(2.71f));
644649

645650
assertThat(hit.getSourceAsMap().get("text").toString(), equalTo("some text to entertain"));
646651

647-
field = hit.field("script");
648-
assertThat(field.getValue().toString(), equalTo("5"));
652+
field2 = hit.field("script");
653+
assertThat(field2.getValue().toString(), equalTo("5"));
649654

650655
assertThat(hit.getSourceAsMap().size(), equalTo(1));
651656
assertThat(hit.getSourceAsMap().get("text").toString(), equalTo("some text to entertain"));

server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public void testSimpleNested() throws Exception {
165165
.setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit(
166166
new InnerHitBuilder().setHighlightBuilder(new HighlightBuilder().field("comments.message"))
167167
.setExplain(true)
168-
.addDocValueField("comments.mes*")
168+
.addFetchField("comments.mes*")
169169
.addScriptField("script",
170170
new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", Collections.emptyMap()))
171171
.setSize(1))).get();
@@ -176,8 +176,18 @@ public void testSimpleNested() throws Exception {
176176
assertThat(innerHits.getAt(0).getHighlightFields().get("comments.message").getFragments()[0].string(),
177177
equalTo("<em>fox</em> eat quick"));
178178
assertThat(innerHits.getAt(0).getExplanation().toString(), containsString("weight(comments.message:fox in"));
179-
assertThat(innerHits.getAt(0).getFields().get("comments.message").getValue().toString(), equalTo("eat"));
179+
assertThat(innerHits.getAt(0).getFields().get("comments.message").getValue().toString(), equalTo("fox eat quick"));
180180
assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5"));
181+
182+
response = client().prepareSearch("articles")
183+
.setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit(
184+
new InnerHitBuilder()
185+
.addDocValueField("comments.mes*")
186+
.setSize(1))).get();
187+
assertNoFailures(response);
188+
innerHits = response.getHits().getAt(0).getInnerHits().get("comments");
189+
assertThat(innerHits.getHits().length, equalTo(1));
190+
assertThat(innerHits.getAt(0).getFields().get("comments.message").getValue().toString(), equalTo("eat"));
181191
}
182192

183193
public void testRandomNested() throws Exception {

server/src/main/java/org/elasticsearch/action/search/ExpandSearchPhase.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ private SearchSourceBuilder buildExpandSearchSourceBuilder(InnerHitBuilder optio
136136
options.getFetchSourceContext().excludes());
137137
}
138138
}
139+
if (options.getFetchFields() != null) {
140+
options.getFetchFields().forEach(ff -> groupSource.fetchField(ff.field, ff.format));
141+
}
139142
if (options.getDocValueFields() != null) {
140143
options.getDocValueFields().forEach(ff -> groupSource.docValueField(ff.field, ff.format));
141144
}

0 commit comments

Comments
 (0)