Skip to content

Commit 67b5a83

Browse files
authored
Ensure that _exists queries on keyword fields use norms when they're available. (#33006)
1 parent 767c695 commit 67b5a83

File tree

4 files changed

+45
-15
lines changed

4 files changed

+45
-15
lines changed

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.lucene.index.IndexableField;
2929
import org.apache.lucene.index.Term;
3030
import org.apache.lucene.search.DocValuesFieldExistsQuery;
31+
import org.apache.lucene.search.NormsFieldExistsQuery;
3132
import org.apache.lucene.search.Query;
3233
import org.apache.lucene.search.TermQuery;
3334
import org.apache.lucene.util.BytesRef;
@@ -166,7 +167,7 @@ public Mapper.Builder<?,?> parse(String name, Map<String, Object> node, ParserCo
166167
builder.ignoreAbove(XContentMapValues.nodeIntegerValue(propNode, -1));
167168
iterator.remove();
168169
} else if (propName.equals("norms")) {
169-
builder.omitNorms(XContentMapValues.nodeBooleanValue(propNode, "norms") == false);
170+
TypeParsers.parseNorms(builder, name, propNode);
170171
iterator.remove();
171172
} else if (propName.equals("eager_global_ordinals")) {
172173
builder.eagerGlobalOrdinals(XContentMapValues.nodeBooleanValue(propNode, "eager_global_ordinals"));
@@ -256,8 +257,10 @@ public void setSplitQueriesOnWhitespace(boolean splitQueriesOnWhitespace) {
256257
public Query existsQuery(QueryShardContext context) {
257258
if (hasDocValues()) {
258259
return new DocValuesFieldExistsQuery(name());
259-
} else {
260+
} else if (omitNorms()) {
260261
return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name()));
262+
} else {
263+
return new NormsFieldExistsQuery(name());
261264
}
262265
}
263266

@@ -366,17 +369,19 @@ protected void parseCreateField(ParseContext context, List<IndexableField> field
366369

367370
// convert to utf8 only once before feeding postings/dv/stored fields
368371
final BytesRef binaryValue = new BytesRef(value);
369-
if (fieldType().indexOptions() != IndexOptions.NONE || fieldType().stored()) {
372+
if (fieldType().indexOptions() != IndexOptions.NONE || fieldType().stored()) {
370373
Field field = new Field(fieldType().name(), binaryValue, fieldType());
371374
fields.add(field);
375+
376+
if (fieldType().hasDocValues() == false && fieldType().omitNorms()) {
377+
createFieldNamesField(context, fields);
378+
}
372379
}
380+
373381
if (fieldType().hasDocValues()) {
374382
fields.add(new SortedSetDocValuesField(fieldType().name(), binaryValue));
375-
} else if (fieldType().stored() || fieldType().indexOptions() != IndexOptions.NONE) {
376-
createFieldNamesField(context, fields);
377383
}
378384
}
379-
380385
@Override
381386
protected String contentType() {
382387
return CONTENT_TYPE;

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,7 @@ private static void parseAnalyzersAndTermVectors(FieldMapper.Builder builder, St
122122
}
123123
}
124124

125-
public static void parseNorms(FieldMapper.Builder builder, String fieldName, Object propNode,
126-
Mapper.TypeParser.ParserContext parserContext) {
125+
public static void parseNorms(FieldMapper.Builder builder, String fieldName, Object propNode) {
127126
builder.omitNorms(XContentMapValues.nodeBooleanValue(propNode, fieldName + ".norms") == false);
128127
}
129128

@@ -140,7 +139,7 @@ public static void parseTextField(FieldMapper.Builder builder, String name, Map<
140139
final String propName = entry.getKey();
141140
final Object propNode = entry.getValue();
142141
if ("norms".equals(propName)) {
143-
parseNorms(builder, name, propNode, parserContext);
142+
parseNorms(builder, name, propNode);
144143
iterator.remove();
145144
}
146145
}

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,11 +321,16 @@ public void testBoost() throws IOException {
321321

322322
public void testEnableNorms() throws IOException {
323323
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
324-
.startObject("properties").startObject("field").field("type", "keyword").field("norms", true).endObject().endObject()
325-
.endObject().endObject());
324+
.startObject("properties")
325+
.startObject("field")
326+
.field("type", "keyword")
327+
.field("doc_values", false)
328+
.field("norms", true)
329+
.endObject()
330+
.endObject()
331+
.endObject().endObject());
326332

327333
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
328-
329334
assertEquals(mapping, mapper.mappingSource().toString());
330335

331336
ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", BytesReference
@@ -336,8 +341,11 @@ public void testEnableNorms() throws IOException {
336341
XContentType.JSON));
337342

338343
IndexableField[] fields = doc.rootDoc().getFields("field");
339-
assertEquals(2, fields.length);
344+
assertEquals(1, fields.length);
340345
assertFalse(fields[0].fieldType().omitNorms());
346+
347+
IndexableField[] fieldNamesFields = doc.rootDoc().getFields(FieldNamesFieldMapper.NAME);
348+
assertEquals(0, fieldNamesFields.length);
341349
}
342350

343351
public void testNormalizer() throws IOException {

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
package org.elasticsearch.index.mapper;
2020

2121
import com.carrotsearch.randomizedtesting.generators.RandomStrings;
22-
2322
import org.apache.lucene.analysis.Analyzer;
2423
import org.apache.lucene.analysis.LowerCaseFilter;
2524
import org.apache.lucene.analysis.TokenFilter;
@@ -28,9 +27,11 @@
2827
import org.apache.lucene.analysis.core.WhitespaceTokenizer;
2928
import org.apache.lucene.index.IndexOptions;
3029
import org.apache.lucene.index.Term;
31-
import org.apache.lucene.search.TermInSetQuery;
30+
import org.apache.lucene.search.DocValuesFieldExistsQuery;
3231
import org.apache.lucene.search.FuzzyQuery;
32+
import org.apache.lucene.search.NormsFieldExistsQuery;
3333
import org.apache.lucene.search.RegexpQuery;
34+
import org.apache.lucene.search.TermInSetQuery;
3435
import org.apache.lucene.search.TermQuery;
3536
import org.apache.lucene.util.BytesRef;
3637
import org.elasticsearch.common.lucene.Lucene;
@@ -132,6 +133,23 @@ public void testTermsQuery() {
132133
assertEquals("Cannot search on field [field] since it is not indexed.", e.getMessage());
133134
}
134135

136+
public void testExistsQuery() {
137+
MappedFieldType ft = createDefaultFieldType();
138+
ft.setName("field");
139+
140+
ft.setHasDocValues(true);
141+
ft.setOmitNorms(true);
142+
assertEquals(new DocValuesFieldExistsQuery("field"), ft.existsQuery(null));
143+
144+
ft.setHasDocValues(false);
145+
ft.setOmitNorms(false);
146+
assertEquals(new NormsFieldExistsQuery("field"), ft.existsQuery(null));
147+
148+
ft.setHasDocValues(false);
149+
ft.setOmitNorms(true);
150+
assertEquals(new TermQuery(new Term(FieldNamesFieldMapper.NAME, "field")), ft.existsQuery(null));
151+
}
152+
135153
public void testRegexpQuery() {
136154
MappedFieldType ft = createDefaultFieldType();
137155
ft.setName("field");

0 commit comments

Comments
 (0)