Skip to content

Commit 52ba407

Browse files
authored
Expose sequence number and primary terms in search responses (#37639)
Users may require the sequence number and primary terms to perform optimistic concurrency control operations. Currently, you can get the sequence number via the `docvalues_fields` API but the primary term is not accessible because it is maintained by the `SeqNoFieldMapper` and the infrastructure can't find it. This commit adds a dedicated sub fetch phase to return both numbers that is connected to a new `seq_no_primary_term` parameter.
1 parent 534ba1d commit 52ba407

File tree

39 files changed

+603
-63
lines changed

39 files changed

+603
-63
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ The top_hits aggregation returns regular search hits, because of this many per h
2525
* <<search-request-script-fields,Script fields>>
2626
* <<search-request-docvalue-fields,Doc value fields>>
2727
* <<search-request-version,Include versions>>
28+
* <<search-request-seq-no-primary-term,Include Sequence Numbers and Primary Terms>>
2829

2930
==== Example
3031

docs/reference/docs/concurrency-control.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ returns:
8787

8888

8989
Note: The <<search-search,Search API>> can return the `_seq_no` and `_primary_term`
90-
for each search hit by requesting the `_seq_no` and `_primary_term` <<search-request-docvalue-fields,Doc Value Fields>>.
90+
for each search hit by setting <<search-request-seq-no-primary-term,`seq_no_primary_term` parameter>>.
9191

9292
The sequence number and the primary term uniquely identify a change. By noting down
9393
the sequence number and primary term returned, you can make sure to only change the

docs/reference/search/request-body.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ include::request/preference.asciidoc[]
213213

214214
include::request/explain.asciidoc[]
215215

216-
include::request/version.asciidoc[]
216+
include::request/version-and-seq-no.asciidoc[]
217217

218218
include::request/index-boost.asciidoc[]
219219

docs/reference/search/request/inner-hits.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Inner hits also supports the following per document features:
7676
* <<search-request-script-fields,Script fields>>
7777
* <<search-request-docvalue-fields,Doc value fields>>
7878
* <<search-request-version,Include versions>>
79+
* <<search-request-seq-no-primary-term,Include Sequence Numbers and Primary Terms>>
7980

8081
[[nested-inner-hits]]
8182
==== Nested inner hits
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[[search-request-seq-no-primary-term]]
2+
=== Sequence Numbers and Primary Term
3+
4+
Returns the sequence number and primary term of the last modification to each search hit.
5+
See <<optimistic-concurrency-control>> for more details.
6+
7+
[source,js]
8+
--------------------------------------------------
9+
GET /_search
10+
{
11+
"seq_no_primary_term": true,
12+
"query" : {
13+
"term" : { "user" : "kimchy" }
14+
}
15+
}
16+
--------------------------------------------------
17+
// CONSOLE
18+
19+
[[search-request-version]]
20+
=== Version
21+
22+
Returns a version for each search hit.
23+
24+
[source,js]
25+
--------------------------------------------------
26+
GET /_search
27+
{
28+
"version": true,
29+
"query" : {
30+
"term" : { "user" : "kimchy" }
31+
}
32+
}
33+
--------------------------------------------------
34+
// CONSOLE

docs/reference/search/request/version.asciidoc

Lines changed: 0 additions & 16 deletions
This file was deleted.

modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ public void testFromJson() throws IOException {
252252
" \"from\" : 0,\n" +
253253
" \"size\" : 100,\n" +
254254
" \"version\" : false,\n" +
255+
" \"seq_no_primary_term\" : false,\n" +
255256
" \"explain\" : false,\n" +
256257
" \"track_scores\" : false,\n" +
257258
" \"sort\" : [ {\n" +

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
5757
import static org.elasticsearch.index.query.QueryBuilders.nestedQuery;
5858
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
59+
import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_PRIMARY_TERM;
60+
import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
5961
import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery;
6062
import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery;
6163
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
@@ -66,6 +68,7 @@
6668
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId;
6769
import static org.hamcrest.Matchers.containsString;
6870
import static org.hamcrest.Matchers.equalTo;
71+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
6972
import static org.hamcrest.Matchers.notNullValue;
7073
import static org.hamcrest.Matchers.nullValue;
7174

@@ -133,9 +136,10 @@ public void testSimpleParentChild() throws Exception {
133136
assertThat(innerHits.getAt(1).getId(), equalTo("c2"));
134137
assertThat(innerHits.getAt(1).getType(), equalTo("doc"));
135138

139+
final boolean seqNoAndTerm = randomBoolean();
136140
response = client().prepareSearch("articles")
137141
.setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None)
138-
.innerHit(new InnerHitBuilder()))
142+
.innerHit(new InnerHitBuilder().setSeqNoAndPrimaryTerm(seqNoAndTerm)))
139143
.get();
140144
assertNoFailures(response);
141145
assertHitCount(response, 1);
@@ -152,6 +156,22 @@ public void testSimpleParentChild() throws Exception {
152156
assertThat(innerHits.getAt(2).getId(), equalTo("c6"));
153157
assertThat(innerHits.getAt(2).getType(), equalTo("doc"));
154158

159+
if (seqNoAndTerm) {
160+
assertThat(innerHits.getAt(0).getPrimaryTerm(), equalTo(1L));
161+
assertThat(innerHits.getAt(1).getPrimaryTerm(), equalTo(1L));
162+
assertThat(innerHits.getAt(2).getPrimaryTerm(), equalTo(1L));
163+
assertThat(innerHits.getAt(0).getSeqNo(), greaterThanOrEqualTo(0L));
164+
assertThat(innerHits.getAt(1).getSeqNo(), greaterThanOrEqualTo(0L));
165+
assertThat(innerHits.getAt(2).getSeqNo(), greaterThanOrEqualTo(0L));
166+
} else {
167+
assertThat(innerHits.getAt(0).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM));
168+
assertThat(innerHits.getAt(1).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM));
169+
assertThat(innerHits.getAt(2).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM));
170+
assertThat(innerHits.getAt(0).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO));
171+
assertThat(innerHits.getAt(1).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO));
172+
assertThat(innerHits.getAt(2).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO));
173+
}
174+
155175
response = client().prepareSearch("articles")
156176
.setQuery(
157177
hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(

modules/parent-join/src/test/resources/rest-api-spec/test/11_parent_child.yml

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,26 @@ setup:
1111
relations:
1212
parent: child
1313

14-
---
15-
"Parent/child inner hits":
16-
- do:
17-
index:
18-
index: test
19-
type: doc
20-
id: 1
21-
body: {"foo": "bar", "join_field": {"name" : "parent"} }
14+
- do:
15+
index:
16+
index: test
17+
type: doc
18+
id: 1
19+
body: {"foo": "bar", "join_field": {"name" : "parent"} }
2220

23-
- do:
24-
index:
25-
index: test
26-
type: doc
27-
id: 2
28-
routing: 1
29-
body: {"bar": "baz", "join_field": { "name" : "child", "parent": "1"} }
21+
- do:
22+
index:
23+
index: test
24+
type: doc
25+
id: 2
26+
routing: 1
27+
body: {"bar": "baz", "join_field": { "name" : "child", "parent": "1"} }
3028

31-
- do:
32-
indices.refresh: {}
29+
- do:
30+
indices.refresh: {}
3331

32+
---
33+
"Parent/child inner hits":
3434
- do:
3535
search:
3636
rest_total_hits_as_int: true
@@ -41,3 +41,24 @@ setup:
4141
- match: { hits.hits.0.inner_hits.child.hits.hits.0._index: "test"}
4242
- match: { hits.hits.0.inner_hits.child.hits.hits.0._id: "2" }
4343
- is_false: hits.hits.0.inner_hits.child.hits.hits.0._nested
44+
45+
---
46+
"Parent/child inner hits with seq no":
47+
- skip:
48+
version: " - 6.99.99"
49+
reason: support was added in 7.0
50+
51+
- do:
52+
search:
53+
rest_total_hits_as_int: true
54+
body: { "query" : { "has_child" :
55+
{ "type" : "child", "query" : { "match_all" : {} }, "inner_hits" : { "seq_no_primary_term": true} }
56+
} }
57+
- match: { hits.total: 1 }
58+
- match: { hits.hits.0._index: "test" }
59+
- match: { hits.hits.0._id: "1" }
60+
- match: { hits.hits.0.inner_hits.child.hits.hits.0._index: "test"}
61+
- match: { hits.hits.0.inner_hits.child.hits.hits.0._id: "2" }
62+
- is_false: hits.hits.0.inner_hits.child.hits.hits.0._nested
63+
- gte: { hits.hits.0.inner_hits.child.hits.hits.0._seq_no: 0 }
64+
- gte: { hits.hits.0.inner_hits.child.hits.hits.0._primary_term: 1 }

modules/reindex/src/test/java/org/elasticsearch/index/reindex/remote/RemoteRequestBuildersTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public void testInitialSearchParamsMisc() {
179179
fetchVersion = randomBoolean();
180180
searchRequest.source().version(fetchVersion);
181181
}
182-
182+
183183
Map<String, String> params = initialSearch(searchRequest, query, remoteVersion).getParameters();
184184

185185
if (scroll == null) {

0 commit comments

Comments
 (0)