Skip to content

Commit 7fefdac

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 fa6ba7d commit 7fefdac

File tree

15 files changed

+337
-99
lines changed

15 files changed

+337
-99
lines changed

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
@@ -65,7 +65,7 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
6565
@Override
6666
protected MatchQueryBuilder doCreateTestQueryBuilder() {
6767
String fieldName = randomFrom(STRING_FIELD_NAME, STRING_ALIAS_FIELD_NAME, BOOLEAN_FIELD_NAME, INT_FIELD_NAME,
68-
DOUBLE_FIELD_NAME, DATE_FIELD_NAME);
68+
DOUBLE_FIELD_NAME, DATE_FIELD_NAME, JSON_FIELD_NAME);
6969
Object value;
7070
if (isTextField(fieldName)) {
7171
int terms = randomIntBetween(0, 3);
@@ -153,7 +153,8 @@ protected void doAssertLuceneQuery(MatchQueryBuilder queryBuilder, Query query,
153153
MappedFieldType fieldType = context.fieldMapper(queryBuilder.fieldName());
154154
if (query instanceof TermQuery && fieldType != null) {
155155
String queryValue = queryBuilder.value().toString();
156-
if (queryBuilder.analyzer() == null || queryBuilder.analyzer().equals("simple")) {
156+
if (isTextField(queryBuilder.fieldName())
157+
&& (queryBuilder.analyzer() == null || queryBuilder.analyzer().equals("simple"))) {
157158
queryValue = queryValue.toLowerCase(Locale.ROOT);
158159
}
159160
Query expectedTermQuery = fieldType.termQuery(queryValue, context);
@@ -401,7 +402,7 @@ public void testMaxBooleanClause() {
401402
query.setAnalyzer(new MockGraphAnalyzer(createGiantGraphMultiTerms()));
402403
expectThrows(BooleanQuery.TooManyClauses.class, () -> query.parse(Type.PHRASE, STRING_FIELD_NAME, ""));
403404
}
404-
405+
405406
private static class MockGraphAnalyzer extends Analyzer {
406407

407408
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
@@ -65,7 +65,7 @@ public class MultiMatchQueryBuilderTests extends AbstractQueryTestCase<MultiMatc
6565
@Override
6666
protected MultiMatchQueryBuilder doCreateTestQueryBuilder() {
6767
String fieldName = randomFrom(STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME, BOOLEAN_FIELD_NAME, DATE_FIELD_NAME,
68-
MISSING_FIELD_NAME, MISSING_WILDCARD_FIELD_NAME);
68+
MISSING_FIELD_NAME, MISSING_WILDCARD_FIELD_NAME, JSON_FIELD_NAME);
6969

7070
final Object value;
7171
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"));

server/src/test/java/org/elasticsearch/search/query/ExistsIT.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
import static java.util.Collections.emptyMap;
4141
import static java.util.Collections.singletonMap;
42+
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
4243
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
4344
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
4445
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
@@ -231,4 +232,41 @@ public void testFieldAliasWithNoDocValues() throws Exception {
231232
assertSearchResponse(response);
232233
assertHitCount(response, 2);
233234
}
235+
236+
public void testJsonFields() throws Exception {
237+
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject()
238+
.startObject("type")
239+
.startObject("properties")
240+
.startObject("headers")
241+
.field("type", "json")
242+
.endObject()
243+
.endObject()
244+
.endObject()
245+
.endObject();
246+
assertAcked(prepareCreate("test").addMapping("type", mapping));
247+
248+
IndexRequestBuilder indexRequest = client().prepareIndex("test", "type", "1")
249+
.setSource(XContentFactory.jsonBuilder()
250+
.startObject()
251+
.startObject("headers")
252+
.field("content-type", "application/json")
253+
.endObject()
254+
.endObject());
255+
indexRandom(true, false, indexRequest);
256+
257+
SearchResponse searchResponse = client().prepareSearch()
258+
.setQuery(existsQuery("headers"))
259+
.get();
260+
assertHitCount(searchResponse, 1L);
261+
262+
searchResponse = client().prepareSearch()
263+
.setQuery(existsQuery("headers.content-type"))
264+
.get();
265+
assertHitCount(searchResponse, 1L);
266+
267+
searchResponse = client().prepareSearch()
268+
.setQuery(existsQuery("headers.nonexistent"))
269+
.get();
270+
assertHitCount(searchResponse, 0L);
271+
}
234272
}

0 commit comments

Comments
 (0)