Skip to content

Commit 214b8d6

Browse files
authored
Fix '_terms_enum' on docvalue-only keywords fields (#94719) (#94735)
Running a "_terms_enum" request on a keyword field with "index: false" setting can currently fail with an exception. This change adds missing testing around the problematic case and fixes it by implementing missing methods in SortedSetDocValuesTerms. Closes #94673
1 parent 64129e6 commit 214b8d6

File tree

3 files changed

+122
-4
lines changed

3 files changed

+122
-4
lines changed

docs/changelog/94719.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 94719
2+
summary: Fix '_terms_enum' on docvalue-only keywords fields
3+
area: Search
4+
type: bug
5+
issues:
6+
- 94673

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -662,22 +662,22 @@ public int getDocCount() throws IOException {
662662

663663
@Override
664664
public boolean hasFreqs() {
665-
throw new UnsupportedOperationException();
665+
return false;
666666
}
667667

668668
@Override
669669
public boolean hasOffsets() {
670-
throw new UnsupportedOperationException();
670+
return false;
671671
}
672672

673673
@Override
674674
public boolean hasPositions() {
675-
throw new UnsupportedOperationException();
675+
return false;
676676
}
677677

678678
@Override
679679
public boolean hasPayloads() {
680-
throw new UnsupportedOperationException();
680+
return false;
681681
}
682682

683683
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.core.termsenum;
9+
10+
import org.elasticsearch.action.support.WriteRequest;
11+
import org.elasticsearch.common.settings.Settings;
12+
import org.elasticsearch.plugins.Plugin;
13+
import org.elasticsearch.test.ESSingleNodeTestCase;
14+
import org.elasticsearch.test.InternalSettingsPlugin;
15+
import org.elasticsearch.xcontent.XContentFactory;
16+
import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin;
17+
import org.elasticsearch.xpack.core.XPackSettings;
18+
import org.elasticsearch.xpack.core.termsenum.action.TermsEnumAction;
19+
import org.elasticsearch.xpack.core.termsenum.action.TermsEnumRequest;
20+
import org.elasticsearch.xpack.core.termsenum.action.TermsEnumResponse;
21+
22+
import java.io.IOException;
23+
import java.util.Collection;
24+
import java.util.List;
25+
26+
import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
27+
import static org.hamcrest.Matchers.contains;
28+
29+
public class TermsEnumTests extends ESSingleNodeTestCase {
30+
31+
@Override
32+
protected Collection<Class<? extends Plugin>> getPlugins() {
33+
return List.of(LocalStateCompositeXPackPlugin.class, InternalSettingsPlugin.class);
34+
}
35+
36+
@Override
37+
protected Settings nodeSettings() {
38+
return Settings.builder().put(XPackSettings.SECURITY_ENABLED.getKey(), "false").build();
39+
}
40+
41+
public void testTermsEnumKeywordIndexed() throws Exception {
42+
checkTermsEnumKeywords("true");
43+
}
44+
45+
public void testTermsEnumKeywordDocValuesOnly() throws Exception {
46+
checkTermsEnumKeywords("false");
47+
}
48+
49+
private void checkTermsEnumKeywords(String indexed) throws Exception {
50+
String indexName = "test_" + indexed;
51+
// only shard is preferred to increase likelihood of having more than one segment, which goes through a different code path
52+
int numshards = randomBoolean() ? 1 : randomIntBetween(1, 5);
53+
createIndex(indexName, Settings.builder().put("index.merge.enabled", false).put("number_of_shards", numshards).build());
54+
55+
client().admin()
56+
.indices()
57+
.preparePutMapping(indexName)
58+
.setSource(
59+
XContentFactory.jsonBuilder()
60+
.startObject()
61+
.startObject("_doc")
62+
.startObject("properties")
63+
.startObject("keyword")
64+
.field("type", "keyword")
65+
.field("index", indexed)
66+
.endObject()
67+
.endObject()
68+
.endObject()
69+
.endObject()
70+
)
71+
.get();
72+
ensureGreen();
73+
74+
indexAndRefresh(indexName, "1", "keyword", "Apple");
75+
indexAndRefresh(indexName, "2", "keyword", "Able");
76+
indexAndRefresh(indexName, "3", "keyword", "Banana");
77+
indexAndRefresh(indexName, "4", "keyword", "Blue");
78+
indexAndRefresh(indexName, "5", "keyword", "Cheddar");
79+
{
80+
TermsEnumResponse response = client().execute(TermsEnumAction.INSTANCE, new TermsEnumRequest(indexName).field("keyword")).get();
81+
List<String> terms = response.getTerms();
82+
assertEquals(5, terms.size());
83+
assertThat(terms, contains("Able", "Apple", "Banana", "Blue", "Cheddar"));
84+
}
85+
{
86+
TermsEnumResponse response = client().execute(
87+
TermsEnumAction.INSTANCE,
88+
new TermsEnumRequest(indexName).field("keyword").string("A")
89+
).get();
90+
List<String> terms = response.getTerms();
91+
assertEquals(2, terms.size());
92+
assertThat(terms, contains("Able", "Apple"));
93+
}
94+
{
95+
TermsEnumResponse response = client().execute(
96+
TermsEnumAction.INSTANCE,
97+
new TermsEnumRequest(indexName).field("keyword").string("B").searchAfter("Banana")
98+
).get();
99+
List<String> terms = response.getTerms();
100+
assertEquals(1, terms.size());
101+
assertThat(terms, contains("Blue"));
102+
}
103+
}
104+
105+
private void indexAndRefresh(String indexName, String id, String field, String value) throws IOException {
106+
client().prepareIndex(indexName)
107+
.setId(id)
108+
.setSource(jsonBuilder().startObject().field(field, value).endObject())
109+
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
110+
.get();
111+
}
112+
}

0 commit comments

Comments
 (0)