Skip to content

Commit fd73dcc

Browse files
committed
Add tests for the supported query types. (#35319)
* Add tests for the supported query types. * Disallow unbounded range queries on keyed JSON fields. * Make sure MappedFieldType#hasDocValues always returns false.
1 parent 8975d1c commit fd73dcc

File tree

17 files changed

+349
-116
lines changed

17 files changed

+349
-116
lines changed

rest-api-spec/src/main/resources/rest-api-spec/test/search/160_exists_query.yml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,15 +1340,13 @@ setup:
13401340
index: json_test
13411341
body:
13421342
mappings:
1343-
_doc:
1344-
dynamic: false
1345-
properties:
1346-
json:
1347-
type: json
1343+
dynamic: false
1344+
properties:
1345+
json:
1346+
type: json
13481347
- do:
13491348
index:
13501349
index: json_test
1351-
type: _doc
13521350
id: 1
13531351
body:
13541352
json:
@@ -1363,7 +1361,7 @@ setup:
13631361
exists:
13641362
field: json
13651363

1366-
- match: { hits.total: 1 }
1364+
- match: { hits.total.value: 1 }
13671365

13681366
- do:
13691367
search:
@@ -1373,7 +1371,7 @@ setup:
13731371
exists:
13741372
field: json.key
13751373

1376-
- match: { hits.total: 1 }
1374+
- match: { hits.total.value: 1 }
13771375

13781376
- do:
13791377
search:
@@ -1383,4 +1381,4 @@ setup:
13831381
exists:
13841382
field: json.nonexistent_key
13851383

1386-
- match: { hits.total: 0 }
1384+
- match: { hits.total.value: 0 }

rest-api-spec/src/main/resources/rest-api-spec/test/search/60_query_string.yml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,13 @@
7373
index: test
7474
body:
7575
mappings:
76-
_doc:
77-
properties:
78-
headers:
79-
type: json
76+
properties:
77+
headers:
78+
type: json
8079

8180
- do:
8281
index:
8382
index: test
84-
type: _doc
8583
id: 1
8684
body:
8785
headers:
@@ -92,7 +90,6 @@
9290
- do:
9391
index:
9492
index: test
95-
type: _doc
9693
id: 2
9794
body:
9895
headers:
@@ -108,7 +105,7 @@
108105
query_string:
109106
query: "headers:text\\/plain"
110107

111-
- match: { hits.total: 1 }
108+
- match: { hits.total.value: 1 }
112109
- length: { hits.hits: 1 }
113110
- match: { hits.hits.0._id: "2" }
114111

@@ -120,6 +117,6 @@
120117
query_string:
121118
query: "application\\/javascript AND headers.origin:elastic.co"
122119

123-
- match: { hits.total: 1 }
120+
- match: { hits.total.value: 1 }
124121
- length: { hits.hits: 1 }
125122
- match: { hits.hits.0._id: "1" }

server/src/main/java/org/elasticsearch/index/mapper/JsonFieldMapper.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.lucene.search.Query;
3030
import org.apache.lucene.search.TermQuery;
3131
import org.apache.lucene.util.BytesRef;
32+
import org.elasticsearch.Version;
3233
import org.elasticsearch.common.bytes.BytesReference;
3334
import org.elasticsearch.common.lucene.Lucene;
3435
import org.elasticsearch.common.settings.Settings;
@@ -165,6 +166,11 @@ public Builder copyTo(CopyTo copyTo) {
165166
throw new UnsupportedOperationException("[copy_to] is not supported for [" + CONTENT_TYPE + "] fields.");
166167
}
167168

169+
@Override
170+
protected boolean defaultDocValues(Version indexCreated) {
171+
return false;
172+
}
173+
168174
@Override
169175
public JsonFieldMapper build(BuilderContext context) {
170176
setupFieldType(context);
@@ -276,6 +282,25 @@ public Query existsQuery(QueryShardContext context) {
276282
return new PrefixQuery(term);
277283
}
278284

285+
@Override
286+
public Query rangeQuery(Object lowerTerm,
287+
Object upperTerm,
288+
boolean includeLower,
289+
boolean includeUpper,
290+
QueryShardContext context) {
291+
292+
// We require range queries to specify both bounds because an unbounded query could incorrectly match
293+
// values from other keys. For example, a query on the 'first' key with only a lower bound would become
294+
// ("first\0value", null), which would also match the value "second\0value" belonging to the key 'second'.
295+
if (lowerTerm == null || upperTerm == null) {
296+
throw new IllegalArgumentException("[range] queries on keyed [" + CONTENT_TYPE +
297+
"] fields must include both an upper and a lower bound.");
298+
}
299+
300+
return super.rangeQuery(lowerTerm, upperTerm,
301+
includeLower, includeUpper, context);
302+
}
303+
279304
@Override
280305
public Query fuzzyQuery(Object value, Fuzziness fuzziness, int prefixLength, int maxExpansions,
281306
boolean transpositions) {

server/src/test/java/org/elasticsearch/index/mapper/JsonFieldMapperTests.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.elasticsearch.common.xcontent.XContentType;
3131
import org.elasticsearch.common.xcontent.json.JsonXContent;
3232
import org.elasticsearch.index.IndexService;
33+
import org.elasticsearch.index.mapper.JsonFieldMapper.KeyedJsonFieldType;
3334
import org.elasticsearch.index.mapper.JsonFieldMapper.RootJsonFieldType;
3435
import org.elasticsearch.plugins.Plugin;
3536
import org.elasticsearch.test.ESSingleNodeTestCase;
@@ -42,7 +43,6 @@
4243

4344
import static org.apache.lucene.analysis.BaseTokenStreamTestCase.assertTokenStreamContents;
4445
import static org.hamcrest.Matchers.equalTo;
45-
import static org.hamcrest.Matchers.instanceOf;
4646

4747
public class JsonFieldMapperTests extends ESSingleNodeTestCase {
4848
private IndexService indexService;
@@ -417,7 +417,8 @@ public void testNullValues() throws Exception {
417417
assertEquals(new BytesRef("key\0placeholder"), prefixedOtherFields[0].binaryValue());
418418
}
419419

420-
public void testSplitQueriesOnWhitespace() throws IOException {
420+
public void testSplitQueriesOnWhitespace() throws IOException {
421+
MapperService mapperService = indexService.mapperService();
421422
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject()
422423
.startObject("type")
423424
.startObject("properties")
@@ -427,13 +428,16 @@ public void testSplitQueriesOnWhitespace() throws IOException {
427428
.endObject()
428429
.endObject()
429430
.endObject().endObject());
430-
indexService.mapperService().merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE);
431+
mapperService.merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE);
431432

432-
MappedFieldType fieldType = indexService.mapperService().fullName("field");
433-
assertThat(fieldType, instanceOf(RootJsonFieldType.class));
433+
RootJsonFieldType rootFieldType = (RootJsonFieldType) mapperService.fullName("field");
434+
assertThat(rootFieldType.searchAnalyzer(), equalTo(JsonFieldMapper.WHITESPACE_ANALYZER));
435+
assertTokenStreamContents(rootFieldType.searchAnalyzer().analyzer().tokenStream("", "Hello World"),
436+
new String[] {"Hello", "World"});
434437

435-
RootJsonFieldType ft = (RootJsonFieldType) fieldType;
436-
assertThat(ft.searchAnalyzer(), equalTo(JsonFieldMapper.WHITESPACE_ANALYZER));
437-
assertTokenStreamContents(ft.searchAnalyzer().analyzer().tokenStream("", "Hello World"), new String[] {"Hello", "World"});
438+
KeyedJsonFieldType keyedFieldType = (KeyedJsonFieldType) mapperService.fullName("field.key");
439+
assertThat(keyedFieldType.searchAnalyzer(), equalTo(JsonFieldMapper.WHITESPACE_ANALYZER));
440+
assertTokenStreamContents(keyedFieldType.searchAnalyzer().analyzer().tokenStream("", "Hello World"),
441+
new String[] {"Hello", "World"});
438442
}
439443
}

server/src/test/java/org/elasticsearch/index/mapper/KeyedJsonFieldTypeTests.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,20 @@
2020

2121
import org.apache.lucene.index.IndexOptions;
2222
import org.apache.lucene.index.Term;
23+
import org.apache.lucene.search.MultiTermQuery;
2324
import org.apache.lucene.search.PrefixQuery;
2425
import org.apache.lucene.search.Query;
26+
import org.apache.lucene.search.TermInSetQuery;
2527
import org.apache.lucene.search.TermQuery;
28+
import org.apache.lucene.search.TermRangeQuery;
2629
import org.apache.lucene.util.BytesRef;
2730
import org.elasticsearch.common.unit.Fuzziness;
2831
import org.elasticsearch.index.mapper.JsonFieldMapper.KeyedJsonFieldType;
2932
import org.junit.Before;
3033

34+
import java.util.ArrayList;
35+
import java.util.List;
36+
3137
public class KeyedJsonFieldTypeTests extends FieldTypeTestCase {
3238

3339
@Before
@@ -73,6 +79,22 @@ public void testTermQuery() {
7379
assertEquals("Cannot search on field [field] since it is not indexed.", e.getMessage());
7480
}
7581

82+
public void testTermsQuery() {
83+
KeyedJsonFieldType ft = createDefaultFieldType();
84+
ft.setName("field");
85+
86+
Query expected = new TermInSetQuery("field",
87+
new BytesRef("key\0value1"),
88+
new BytesRef("key\0value2"));
89+
90+
List<String> terms = new ArrayList<>();
91+
terms.add("value1");
92+
terms.add("value2");
93+
Query actual = ft.termsQuery(terms, null);
94+
95+
assertEquals(expected, actual);
96+
}
97+
7698
public void testExistsQuery() {
7799
KeyedJsonFieldType ft = createDefaultFieldType();
78100
ft.setName("field");
@@ -81,6 +103,14 @@ public void testExistsQuery() {
81103
assertEquals(expected, ft.existsQuery(null));
82104
}
83105

106+
public void testPrefixQuery() {
107+
KeyedJsonFieldType ft = createDefaultFieldType();
108+
ft.setName("field");
109+
110+
Query expected = new PrefixQuery(new Term("field", "key\0val"));
111+
assertEquals(expected, ft.prefixQuery("val", MultiTermQuery.CONSTANT_SCORE_REWRITE, null));
112+
}
113+
84114
public void testFuzzyQuery() {
85115
KeyedJsonFieldType ft = createDefaultFieldType();
86116
ft.setName("field");
@@ -90,6 +120,31 @@ public void testFuzzyQuery() {
90120
assertEquals("[fuzzy] queries are not currently supported on [json] fields.", e.getMessage());
91121
}
92122

123+
public void testRangeQuery() {
124+
KeyedJsonFieldType ft = createDefaultFieldType();
125+
ft.setName("field");
126+
127+
TermRangeQuery expected = new TermRangeQuery("field",
128+
new BytesRef("key\0lower"),
129+
new BytesRef("key\0upper"), false, false);
130+
assertEquals(expected, ft.rangeQuery("lower", "upper", false, false, null));
131+
132+
expected = new TermRangeQuery("field",
133+
new BytesRef("key\0lower"),
134+
new BytesRef("key\0upper"), true, true);
135+
assertEquals(expected, ft.rangeQuery("lower", "upper", true, true, null));
136+
137+
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () ->
138+
ft.rangeQuery("lower", null, false, false, null));
139+
assertEquals("[range] queries on keyed [json] fields must include both an upper and a lower bound.",
140+
e.getMessage());
141+
142+
e = expectThrows(IllegalArgumentException.class, () ->
143+
ft.rangeQuery(null, "upper", false, false, null));
144+
assertEquals("[range] queries on keyed [json] fields must include both an upper and a lower bound.",
145+
e.getMessage());
146+
}
147+
93148
public void testRegexpQuery() {
94149
KeyedJsonFieldType ft = createDefaultFieldType();
95150
ft.setName("field");

server/src/test/java/org/elasticsearch/index/mapper/RootJsonFieldTypeTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.lucene.index.Term;
2323
import org.apache.lucene.search.Query;
2424
import org.apache.lucene.search.TermQuery;
25+
import org.apache.lucene.search.TermRangeQuery;
2526
import org.apache.lucene.util.BytesRef;
2627
import org.elasticsearch.common.unit.Fuzziness;
2728
import org.elasticsearch.index.mapper.JsonFieldMapper.RootJsonFieldType;
@@ -83,6 +84,21 @@ public void testFuzzyQuery() {
8384
assertEquals("[fuzzy] queries are not currently supported on [json] fields.", e.getMessage());
8485
}
8586

87+
public void testRangeQuery() {
88+
RootJsonFieldType ft = createDefaultFieldType();
89+
ft.setName("field");
90+
91+
TermRangeQuery expected = new TermRangeQuery("field",
92+
new BytesRef("lower"),
93+
new BytesRef("upper"), false, false);
94+
assertEquals(expected, ft.rangeQuery("lower", "upper", false, false, null));
95+
96+
expected = new TermRangeQuery("field",
97+
new BytesRef("lower"),
98+
new BytesRef("upper"), true, true);
99+
assertEquals(expected, ft.rangeQuery("lower", "upper", true, true, null));
100+
}
101+
86102
public void testRegexpQuery() {
87103
RootJsonFieldType ft = createDefaultFieldType();
88104
ft.setName("field");

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
6767
@Override
6868
protected MatchQueryBuilder doCreateTestQueryBuilder() {
6969
String fieldName = randomFrom(STRING_FIELD_NAME, STRING_ALIAS_FIELD_NAME, BOOLEAN_FIELD_NAME, INT_FIELD_NAME,
70-
DOUBLE_FIELD_NAME, DATE_FIELD_NAME);
70+
DOUBLE_FIELD_NAME, DATE_FIELD_NAME, JSON_FIELD_NAME);
7171
Object value;
7272
if (isTextField(fieldName)) {
7373
int terms = randomIntBetween(0, 3);
@@ -155,7 +155,8 @@ protected void doAssertLuceneQuery(MatchQueryBuilder queryBuilder, Query query,
155155
MappedFieldType fieldType = context.fieldMapper(queryBuilder.fieldName());
156156
if (query instanceof TermQuery && fieldType != null) {
157157
String queryValue = queryBuilder.value().toString();
158-
if (queryBuilder.analyzer() == null || queryBuilder.analyzer().equals("simple")) {
158+
if (isTextField(queryBuilder.fieldName())
159+
&& (queryBuilder.analyzer() == null || queryBuilder.analyzer().equals("simple"))) {
159160
queryValue = queryValue.toLowerCase(Locale.ROOT);
160161
}
161162
Query expectedTermQuery = fieldType.termQuery(queryValue, context);
@@ -473,7 +474,7 @@ public void testMaxBooleanClause() {
473474
query.setAnalyzer(new MockGraphAnalyzer(createGiantGraphMultiTerms()));
474475
expectThrows(BooleanQuery.TooManyClauses.class, () -> query.parse(Type.PHRASE, STRING_FIELD_NAME, ""));
475476
}
476-
477+
477478
private static class MockGraphAnalyzer extends Analyzer {
478479

479480
CannedBinaryTokenStream tokenStream;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class MultiMatchQueryBuilderTests extends AbstractQueryTestCase<MultiMatc
6767
@Override
6868
protected MultiMatchQueryBuilder doCreateTestQueryBuilder() {
6969
String fieldName = randomFrom(STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME, BOOLEAN_FIELD_NAME, DATE_FIELD_NAME,
70-
MISSING_FIELD_NAME, MISSING_WILDCARD_FIELD_NAME);
70+
MISSING_FIELD_NAME, MISSING_WILDCARD_FIELD_NAME, JSON_FIELD_NAME);
7171

7272
final Object value;
7373
if (fieldName.equals(STRING_FIELD_NAME)) {

server/src/test/java/org/elasticsearch/index/search/MatchPhraseQueryIT.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.elasticsearch.action.search.SearchResponse;
2525
import org.elasticsearch.common.settings.Settings;
2626
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
27-
import org.elasticsearch.index.query.QueryBuilders;
2827
import org.elasticsearch.index.search.MatchQuery.ZeroTermsQuery;
2928
import org.elasticsearch.test.ESIntegTestCase;
3029
import org.junit.Before;
@@ -33,6 +32,7 @@
3332
import java.util.List;
3433
import java.util.concurrent.ExecutionException;
3534

35+
import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery;
3636
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
3737
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
3838

@@ -55,7 +55,7 @@ public void testZeroTermsQuery() throws ExecutionException, InterruptedException
5555
List<IndexRequestBuilder> indexRequests = getIndexRequests();
5656
indexRandom(true, false, indexRequests);
5757

58-
MatchPhraseQueryBuilder baseQuery = QueryBuilders.matchPhraseQuery("name", "the who")
58+
MatchPhraseQueryBuilder baseQuery = matchPhraseQuery("name", "the who")
5959
.analyzer("standard_stopwords");
6060

6161
MatchPhraseQueryBuilder matchNoneQuery = baseQuery.zeroTermsQuery(ZeroTermsQuery.NONE);
@@ -67,7 +67,6 @@ public void testZeroTermsQuery() throws ExecutionException, InterruptedException
6767
assertHitCount(matchAllResponse, 2L);
6868
}
6969

70-
7170
private List<IndexRequestBuilder> getIndexRequests() {
7271
List<IndexRequestBuilder> requests = new ArrayList<>();
7372
requests.add(client().prepareIndex(INDEX, "band").setSource("name", "the beatles"));

0 commit comments

Comments
 (0)