Skip to content

Commit 4d4f860

Browse files
committed
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 1dde748 commit 4d4f860

File tree

40 files changed

+611
-71
lines changed

40 files changed

+611
-71
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
@@ -205,7 +205,7 @@ include::request/preference.asciidoc[]
205205

206206
include::request/explain.asciidoc[]
207207

208-
include::request/version.asciidoc[]
208+
include::request/version-and-seq-no.asciidoc[]
209209

210210
include::request/index-boost.asciidoc[]
211211

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
@@ -256,6 +256,7 @@ public void testFromJson() throws IOException {
256256
" \"from\" : 0,\n" +
257257
" \"size\" : 100,\n" +
258258
" \"version\" : false,\n" +
259+
" \"seq_no_primary_term\" : false,\n" +
259260
" \"explain\" : false,\n" +
260261
" \"track_scores\" : false,\n" +
261262
" \"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

@@ -140,9 +143,10 @@ public void testSimpleParentChild() throws Exception {
140143
assertThat(innerHits.getAt(1).getId(), equalTo("c2"));
141144
assertThat(innerHits.getAt(1).getType(), equalTo(legacy() ? "comment" : "doc"));
142145

146+
final boolean seqNoAndTerm = randomBoolean();
143147
response = client().prepareSearch("articles")
144148
.setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None)
145-
.innerHit(new InnerHitBuilder()))
149+
.innerHit(new InnerHitBuilder().setSeqNoAndPrimaryTerm(seqNoAndTerm)))
146150
.get();
147151
assertNoFailures(response);
148152
assertHitCount(response, 1);
@@ -159,6 +163,22 @@ public void testSimpleParentChild() throws Exception {
159163
assertThat(innerHits.getAt(2).getId(), equalTo("c6"));
160164
assertThat(innerHits.getAt(2).getType(), equalTo(legacy() ? "comment" : "doc"));
161165

166+
if (seqNoAndTerm) {
167+
assertThat(innerHits.getAt(0).getPrimaryTerm(), equalTo(1L));
168+
assertThat(innerHits.getAt(1).getPrimaryTerm(), equalTo(1L));
169+
assertThat(innerHits.getAt(2).getPrimaryTerm(), equalTo(1L));
170+
assertThat(innerHits.getAt(0).getSeqNo(), greaterThanOrEqualTo(0L));
171+
assertThat(innerHits.getAt(1).getSeqNo(), greaterThanOrEqualTo(0L));
172+
assertThat(innerHits.getAt(2).getSeqNo(), greaterThanOrEqualTo(0L));
173+
} else {
174+
assertThat(innerHits.getAt(0).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM));
175+
assertThat(innerHits.getAt(1).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM));
176+
assertThat(innerHits.getAt(2).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM));
177+
assertThat(innerHits.getAt(0).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO));
178+
assertThat(innerHits.getAt(1).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO));
179+
assertThat(innerHits.getAt(2).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO));
180+
}
181+
162182
response = client().prepareSearch("articles")
163183
.setQuery(
164184
hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package org.elasticsearch.join.query;
2121

2222
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
23-
2423
import org.apache.lucene.search.BooleanClause;
2524
import org.apache.lucene.search.BooleanQuery;
2625
import org.apache.lucene.search.ConstantScoreQuery;
@@ -240,6 +239,7 @@ public void testFromJson() throws IOException {
240239
" \"from\" : 0,\n" +
241240
" \"size\" : 100,\n" +
242241
" \"version\" : false,\n" +
242+
" \"seq_no_primary_term\" : false,\n" +
243243
" \"explain\" : false,\n" +
244244
" \"track_scores\" : false,\n" +
245245
" \"sort\" : [ {\n" +
Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
setup:
2+
- skip:
3+
version: " - 5.5.99"
4+
reason: parent-join was added in 5.6.
25
- do:
36
indices.create:
47
index: test
@@ -11,30 +14,30 @@ setup:
1114
relations:
1215
parent: child
1316

17+
- do:
18+
index:
19+
index: test
20+
type: doc
21+
id: 1
22+
body: {"foo": "bar", "join_field": {"name" : "parent"} }
23+
24+
- do:
25+
index:
26+
index: test
27+
type: doc
28+
id: 2
29+
routing: 1
30+
body: {"bar": "baz", "join_field": { "name" : "child", "parent": "1"} }
31+
32+
- do:
33+
indices.refresh: {}
34+
1435
---
1536
"Parent/child inner hits":
1637
- skip:
1738
version: " - 5.5.99"
1839
reason: parent-join was added in 5.6.
1940

20-
- do:
21-
index:
22-
index: test
23-
type: doc
24-
id: 1
25-
body: {"foo": "bar", "join_field": {"name" : "parent"} }
26-
27-
- do:
28-
index:
29-
index: test
30-
type: doc
31-
id: 2
32-
routing: 1
33-
body: {"bar": "baz", "join_field": { "name" : "child", "parent": "1"} }
34-
35-
- do:
36-
indices.refresh: {}
37-
3841
- do:
3942
search:
4043
body: { "query" : { "has_child" : { "type" : "child", "query" : { "match_all" : {} }, "inner_hits" : {} } } }
@@ -44,3 +47,24 @@ setup:
4447
- match: { hits.hits.0.inner_hits.child.hits.hits.0._index: "test"}
4548
- match: { hits.hits.0.inner_hits.child.hits.hits.0._id: "2" }
4649
- is_false: hits.hits.0.inner_hits.child.hits.hits.0._nested
50+
51+
---
52+
"Parent/child inner hits with seq no":
53+
- skip:
54+
version: " - 6.6.99"
55+
reason: support was added in 6.7
56+
57+
- do:
58+
search:
59+
rest_total_hits_as_int: true
60+
body: { "query" : { "has_child" :
61+
{ "type" : "child", "query" : { "match_all" : {} }, "inner_hits" : { "seq_no_primary_term": true} }
62+
} }
63+
- match: { hits.total: 1 }
64+
- match: { hits.hits.0._index: "test" }
65+
- match: { hits.hits.0._id: "1" }
66+
- match: { hits.hits.0.inner_hits.child.hits.hits.0._index: "test"}
67+
- match: { hits.hits.0.inner_hits.child.hits.hits.0._id: "2" }
68+
- is_false: hits.hits.0.inner_hits.child.hits.hits.0._nested
69+
- gte: { hits.hits.0.inner_hits.child.hits.hits.0._seq_no: 0 }
70+
- gte: { hits.hits.0.inner_hits.child.hits.hits.0._primary_term: 1 }

0 commit comments

Comments
 (0)