Skip to content

Commit a3f21f2

Browse files
authored
Emit deprecation warning when TermsLookup contains a type (#53731)
TermsLookup in master no longer accepts a type parameter. We should emit a deprecate warning in 7.x when a terms lookup requests includes type to prepare users for its removal. Relates to #41059
1 parent d486bde commit a3f21f2

File tree

7 files changed

+94
-75
lines changed

7 files changed

+94
-75
lines changed

rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/51_filter_with_types.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@ setup:
3535
---
3636
"Filter aggs with terms lookup and ensure it's cached":
3737
# Because the filter agg rewrites the terms lookup in the rewrite phase the request can be cached
38-
38+
- skip:
39+
features: allowed_warnings
3940
- do:
41+
allowed_warnings:
42+
- "Deprecated field [type] used, this field is unused and will be removed entirely"
4043
search:
4144
rest_total_hits_as_int: true
4245
size: 0

rest-api-spec/src/main/resources/rest-api-spec/test/search/150_rewrite_on_coordinator.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"Ensure that we fetch the document only once":
2+
- skip:
3+
features: allowed_warnings
24
- do:
35
indices.create:
46
index: search_index
@@ -31,11 +33,13 @@
3133
indices.refresh: {}
3234

3335
- do:
36+
allowed_warnings:
37+
- "Deprecated field [type] used, this field is unused and will be removed entirely"
3438
catch: /no such index/
3539
search:
3640
rest_total_hits_as_int: true
3741
index: "search_index"
38-
body: { "size" : 0, "query" : { "terms" : { "user" : { "index": "lookup_index", "type": "_doc", "id": "1", "path": "followers"} } } }
42+
body: { "size" : 0, "query" : { "terms" : { "user" : { "index": "lookup_index", "type" : "_doc", "id": "1", "path": "followers"} } } }
3943
- do:
4044
indices.create:
4145
index: lookup_index
@@ -55,10 +59,12 @@
5559
indices.refresh: {}
5660

5761
- do:
62+
allowed_warnings:
63+
- "Deprecated field [type] used, this field is unused and will be removed entirely"
5864
search:
5965
rest_total_hits_as_int: true
6066
index: "search_index"
61-
body: { "size" : 0, "query" : { "terms" : { "user" : { "index": "lookup_index", "type": "_doc", "id": "1", "path": "followers"} } } }
67+
body: { "size" : 0, "query" : { "terms" : { "user" : { "index": "lookup_index", "type" : "_doc", "id": "1", "path": "followers"} } } }
6268

6369
- match: { _shards.total: 5 }
6470
- match: { _shards.successful: 5 }

rest-api-spec/src/main/resources/rest-api-spec/test/search/171_terms_query_with_types.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- skip:
44
version: " - 6.99.99"
55
reason: index.max_terms_count setting has been added in 7.0.0
6+
features: allowed_warnings
67
- do:
78
indices.create:
89
include_type_name: true
@@ -46,6 +47,8 @@
4647
body: {"query" : {"terms" : {"user" : ["u1", "u2", "u3"]}}}
4748

4849
- do:
50+
allowed_warnings:
51+
- "Deprecated field [type] used, this field is unused and will be removed entirely"
4952
search:
5053
rest_total_hits_as_int: true
5154
index: test_index

server/src/main/java/org/elasticsearch/index/query/TermsQueryBuilder.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
package org.elasticsearch.index.query;
2121

22-
import org.apache.logging.log4j.LogManager;
2322
import org.apache.lucene.search.MatchAllDocsQuery;
2423
import org.apache.lucene.search.MatchNoDocsQuery;
2524
import org.apache.lucene.search.Query;
@@ -35,13 +34,12 @@
3534
import org.elasticsearch.common.io.stream.BytesStreamOutput;
3635
import org.elasticsearch.common.io.stream.StreamInput;
3736
import org.elasticsearch.common.io.stream.StreamOutput;
38-
import org.elasticsearch.common.logging.DeprecationLogger;
3937
import org.elasticsearch.common.xcontent.XContentBuilder;
4038
import org.elasticsearch.common.xcontent.XContentParser;
4139
import org.elasticsearch.common.xcontent.support.XContentMapValues;
4240
import org.elasticsearch.index.IndexSettings;
43-
import org.elasticsearch.index.mapper.MappedFieldType;
4441
import org.elasticsearch.index.mapper.ConstantFieldType;
42+
import org.elasticsearch.index.mapper.MappedFieldType;
4543
import org.elasticsearch.indices.TermsLookup;
4644

4745
import java.io.IOException;
@@ -64,11 +62,6 @@
6462
public class TermsQueryBuilder extends AbstractQueryBuilder<TermsQueryBuilder> {
6563
public static final String NAME = "terms";
6664

67-
private static final DeprecationLogger deprecationLogger = new DeprecationLogger(
68-
LogManager.getLogger(TermsQueryBuilder.class));
69-
static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Types are deprecated " +
70-
"in [terms] lookup queries.";
71-
7265
private final String fieldName;
7366
private final List<?> values;
7467
private final TermsLookup termsLookup;
@@ -402,15 +395,9 @@ public static TermsQueryBuilder fromXContent(XContentParser parser) throws IOExc
402395
"followed by array of terms or a document lookup specification");
403396
}
404397

405-
TermsQueryBuilder builder = new TermsQueryBuilder(fieldName, values, termsLookup)
398+
return new TermsQueryBuilder(fieldName, values, termsLookup)
406399
.boost(boost)
407400
.queryName(queryName);
408-
409-
if (builder.isTypeless() == false) {
410-
deprecationLogger.deprecatedAndMaybeLog("terms_lookup_with_types", TYPES_DEPRECATION_MESSAGE);
411-
}
412-
413-
return builder;
414401
}
415402

416403
static List<Object> parseValues(XContentParser parser) throws IOException {

server/src/main/java/org/elasticsearch/indices/TermsLookup.java

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121

2222
import org.elasticsearch.Version;
2323
import org.elasticsearch.common.Nullable;
24-
import org.elasticsearch.common.ParsingException;
24+
import org.elasticsearch.common.ParseField;
2525
import org.elasticsearch.common.io.stream.StreamInput;
2626
import org.elasticsearch.common.io.stream.StreamOutput;
2727
import org.elasticsearch.common.io.stream.Writeable;
28+
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
2829
import org.elasticsearch.common.xcontent.ToXContentFragment;
2930
import org.elasticsearch.common.xcontent.XContentBuilder;
3031
import org.elasticsearch.common.xcontent.XContentParser;
@@ -33,10 +34,14 @@
3334
import java.io.IOException;
3435
import java.util.Objects;
3536

37+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
38+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
39+
3640
/**
3741
* Encapsulates the parameters needed to fetch terms.
3842
*/
3943
public class TermsLookup implements Writeable, ToXContentFragment {
44+
4045
private final String index;
4146
private @Nullable String type;
4247
private final String id;
@@ -142,48 +147,24 @@ public TermsLookup routing(String routing) {
142147
return this;
143148
}
144149

150+
private static final ConstructingObjectParser<TermsLookup, Void> PARSER = new ConstructingObjectParser<>("terms_lookup",
151+
args -> {
152+
String index = (String) args[0];
153+
String type = (String) args[1];
154+
String id = (String) args[2];
155+
String path = (String) args[3];
156+
return new TermsLookup(index, type, id, path);
157+
});
158+
static {
159+
PARSER.declareString(constructorArg(), new ParseField("index"));
160+
PARSER.declareString(optionalConstructorArg(), new ParseField("type").withAllDeprecated());
161+
PARSER.declareString(constructorArg(), new ParseField("id"));
162+
PARSER.declareString(constructorArg(), new ParseField("path"));
163+
PARSER.declareString(TermsLookup::routing, new ParseField("routing"));
164+
}
165+
145166
public static TermsLookup parseTermsLookup(XContentParser parser) throws IOException {
146-
String index = null;
147-
String type = null;
148-
String id = null;
149-
String path = null;
150-
String routing = null;
151-
XContentParser.Token token;
152-
String currentFieldName = "";
153-
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
154-
if (token == XContentParser.Token.FIELD_NAME) {
155-
currentFieldName = parser.currentName();
156-
} else if (token.isValue()) {
157-
switch (currentFieldName) {
158-
case "index":
159-
index = parser.text();
160-
break;
161-
case "type":
162-
type = parser.text();
163-
break;
164-
case "id":
165-
id = parser.text();
166-
break;
167-
case "routing":
168-
routing = parser.textOrNull();
169-
break;
170-
case "path":
171-
path = parser.text();
172-
break;
173-
default:
174-
throw new ParsingException(parser.getTokenLocation(), "[" + TermsQueryBuilder.NAME +
175-
"] query does not support [" + currentFieldName + "] within lookup element");
176-
}
177-
} else {
178-
throw new ParsingException(parser.getTokenLocation(), "[" + TermsQueryBuilder.NAME + "] unknown token ["
179-
+ token + "] after [" + currentFieldName + "]");
180-
}
181-
}
182-
if (type == null) {
183-
return new TermsLookup(index, id, path).routing(routing);
184-
} else {
185-
return new TermsLookup(index, type, id, path).routing(routing);
186-
}
167+
return PARSER.parse(parser, null);
187168
}
188169

189170
@Override

server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
public class TermsQueryBuilderTests extends AbstractQueryTestCase<TermsQueryBuilder> {
5858
private List<Object> randomTerms;
5959
private String termsPath;
60+
private boolean maybeIncludeType = true;
6061

6162
@Before
6263
public void randomTerms() {
@@ -100,10 +101,10 @@ protected TermsQueryBuilder doCreateTestQueryBuilder() {
100101

101102
private TermsLookup randomTermsLookup() {
102103
// Randomly choose between a typeless terms lookup and one with an explicit type to make sure we are
104+
TermsLookup lookup = maybeIncludeType && randomBoolean()
105+
? new TermsLookup(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10), termsPath)
106+
: new TermsLookup(randomAlphaOfLength(10), randomAlphaOfLength(10), termsPath);
103107
// testing both cases.
104-
TermsLookup lookup = randomBoolean()
105-
? new TermsLookup(randomAlphaOfLength(10), randomAlphaOfLength(10), termsPath)
106-
: new TermsLookup(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10), termsPath);
107108
lookup.routing(randomBoolean() ? randomAlphaOfLength(10) : null);
108109
return lookup;
109110
}
@@ -149,7 +150,13 @@ protected void doAssertLuceneQuery(TermsQueryBuilder queryBuilder, Query query,
149150
}
150151
}
151152

152-
public void testEmtpyFieldName() {
153+
@Override
154+
public void testUnknownField() throws IOException {
155+
maybeIncludeType = false; // deprecation warnings will fail the parent test, so we disable types
156+
super.testUnknownField();
157+
}
158+
159+
public void testEmptyFieldName() {
153160
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new TermsQueryBuilder(null, "term"));
154161
assertEquals("field name cannot be null.", e.getMessage());
155162
e = expectThrows(IllegalArgumentException.class, () -> new TermsQueryBuilder("", "term"));
@@ -326,32 +333,36 @@ public void testTypeField() throws IOException {
326333
builder.doToQuery(createShardContext());
327334
assertWarnings(QueryShardContext.TYPES_DEPRECATION_MESSAGE);
328335
}
329-
336+
330337
public void testRewriteIndexQueryToMatchNone() throws IOException {
331338
TermsQueryBuilder query = new TermsQueryBuilder("_index", "does_not_exist", "also_does_not_exist");
332339
QueryShardContext queryShardContext = createShardContext();
333340
QueryBuilder rewritten = query.rewrite(queryShardContext);
334341
assertThat(rewritten, instanceOf(MatchNoneQueryBuilder.class));
335-
}
336-
342+
}
343+
337344
public void testRewriteIndexQueryToNotMatchNone() throws IOException {
338345
// At least one name is good
339346
TermsQueryBuilder query = new TermsQueryBuilder("_index", "does_not_exist", getIndex().getName());
340347
QueryShardContext queryShardContext = createShardContext();
341348
QueryBuilder rewritten = query.rewrite(queryShardContext);
342349
assertThat(rewritten, instanceOf(MatchAllQueryBuilder.class));
343-
}
344-
350+
}
351+
345352
@Override
346353
protected QueryBuilder parseQuery(XContentParser parser) throws IOException {
347-
QueryBuilder query = super.parseQuery(parser);
348-
assertThat(query, CoreMatchers.instanceOf(TermsQueryBuilder.class));
354+
try {
355+
QueryBuilder query = super.parseQuery(parser);
356+
assertThat(query, CoreMatchers.instanceOf(TermsQueryBuilder.class));
357+
358+
TermsQueryBuilder termsQuery = (TermsQueryBuilder) query;
359+
if (termsQuery.isTypeless() == false) {
360+
assertWarnings("Deprecated field [type] used, this field is unused and will be removed entirely");
361+
}
362+
return query;
363+
} finally {
349364

350-
TermsQueryBuilder termsQuery = (TermsQueryBuilder) query;
351-
if (termsQuery.isTypeless() == false) {
352-
assertWarnings(TermsQueryBuilder.TYPES_DEPRECATION_MESSAGE);
353365
}
354-
return query;
355366
}
356367

357368
}

server/src/test/java/org/elasticsearch/indices/TermsLookupTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.elasticsearch.Version;
2323
import org.elasticsearch.common.io.stream.BytesStreamOutput;
2424
import org.elasticsearch.common.io.stream.StreamInput;
25+
import org.elasticsearch.common.xcontent.XContentParser;
26+
import org.elasticsearch.common.xcontent.json.JsonXContent;
2527
import org.elasticsearch.test.ESTestCase;
2628

2729
import java.io.IOException;
@@ -105,6 +107,32 @@ public void testSerializationWithTypes() throws IOException {
105107
}
106108
}
107109

110+
public void testXContentParsingWithType() throws IOException {
111+
XContentParser parser = createParser(JsonXContent.jsonXContent,
112+
"{ \"index\" : \"index\", \"id\" : \"id\", \"type\" : \"type\", \"path\" : \"path\", \"routing\" : \"routing\" }");
113+
114+
TermsLookup tl = TermsLookup.parseTermsLookup(parser);
115+
assertEquals("index", tl.index());
116+
assertEquals("type", tl.type());
117+
assertEquals("id", tl.id());
118+
assertEquals("path", tl.path());
119+
assertEquals("routing", tl.routing());
120+
121+
assertWarnings("Deprecated field [type] used, this field is unused and will be removed entirely");
122+
}
123+
124+
public void testXContentParsing() throws IOException {
125+
XContentParser parser = createParser(JsonXContent.jsonXContent,
126+
"{ \"index\" : \"index\", \"id\" : \"id\", \"path\" : \"path\", \"routing\" : \"routing\" }");
127+
128+
TermsLookup tl = TermsLookup.parseTermsLookup(parser);
129+
assertEquals("index", tl.index());
130+
assertNull(tl.type());
131+
assertEquals("id", tl.id());
132+
assertEquals("path", tl.path());
133+
assertEquals("routing", tl.routing());
134+
}
135+
108136
public static TermsLookup randomTermsLookup() {
109137
return new TermsLookup(
110138
randomAlphaOfLength(10),

0 commit comments

Comments
 (0)