Skip to content

Commit 41d0ff3

Browse files
authored
[Tests] Check output of SuggestionBuilder#build method (#25549)
This change adds a basic unit test for the SuggestionSearchContext that is created as output of SuggestionBuilder#build. The current test only adds checks for the common fields (like text, prefix, fieldName etc...). Relates to #17118
1 parent d9bc0f4 commit 41d0ff3

File tree

8 files changed

+123
-32
lines changed

8 files changed

+123
-32
lines changed

core/src/main/java/org/elasticsearch/index/mapper/MapperService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import com.carrotsearch.hppc.ObjectHashSet;
2323
import com.carrotsearch.hppc.cursors.ObjectCursor;
24+
2425
import org.apache.logging.log4j.message.ParameterizedMessage;
2526
import org.apache.lucene.analysis.Analyzer;
2627
import org.apache.lucene.analysis.DelegatingAnalyzerWrapper;
@@ -43,6 +44,7 @@
4344
import org.elasticsearch.index.IndexSettings;
4445
import org.elasticsearch.index.IndexSortConfig;
4546
import org.elasticsearch.index.analysis.IndexAnalyzers;
47+
import org.elasticsearch.index.analysis.NamedAnalyzer;
4648
import org.elasticsearch.index.mapper.Mapper.BuilderContext;
4749
import org.elasticsearch.index.query.QueryShardContext;
4850
import org.elasticsearch.index.similarity.SimilarityService;
@@ -187,6 +189,10 @@ public IndexAnalyzers getIndexAnalyzers() {
187189
return this.indexAnalyzers;
188190
}
189191

192+
public NamedAnalyzer getNamedAnalyzer(String analyzerName) {
193+
return this.indexAnalyzers.get(analyzerName);
194+
}
195+
190196
public DocumentMapperParser documentMapperParser() {
191197
return this.documentParser;
192198
}

core/src/main/java/org/elasticsearch/search/suggest/SuggestionBuilder.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,7 @@ static SuggestionBuilder<?> fromXContent(XContentParser parser) throws IOExcepti
298298
* Transfers the text, prefix, regex, analyzer, field, size and shard size settings from the
299299
* original {@link SuggestionBuilder} to the target {@link SuggestionContext}
300300
*/
301-
protected void populateCommonFields(MapperService mapperService,
302-
SuggestionSearchContext.SuggestionContext suggestionContext) throws IOException {
301+
protected void populateCommonFields(MapperService mapperService, SuggestionSearchContext.SuggestionContext suggestionContext) {
303302

304303
Objects.requireNonNull(field, "field must not be null");
305304

@@ -314,7 +313,7 @@ protected void populateCommonFields(MapperService mapperService,
314313
suggestionContext.setAnalyzer(fieldType.searchAnalyzer());
315314
}
316315
} else {
317-
Analyzer luceneAnalyzer = mapperService.getIndexAnalyzers().get(analyzer);
316+
Analyzer luceneAnalyzer = mapperService.getNamedAnalyzer(analyzer);
318317
if (luceneAnalyzer == null) {
319318
throw new IllegalArgumentException("analyzer [" + analyzer + "] doesn't exists");
320319
}

core/src/main/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGeneratorBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,13 +412,13 @@ public PhraseSuggestionContext.DirectCandidateGenerator build(MapperService mapp
412412
generator.setField(this.field);
413413
transferIfNotNull(this.size, generator::size);
414414
if (this.preFilter != null) {
415-
generator.preFilter(mapperService.getIndexAnalyzers().get(this.preFilter));
415+
generator.preFilter(mapperService.getNamedAnalyzer(this.preFilter));
416416
if (generator.preFilter() == null) {
417417
throw new IllegalArgumentException("Analyzer [" + this.preFilter + "] doesn't exists");
418418
}
419419
}
420420
if (this.postFilter != null) {
421-
generator.postFilter(mapperService.getIndexAnalyzers().get(this.postFilter));
421+
generator.postFilter(mapperService.getNamedAnalyzer(this.postFilter));
422422
if (generator.postFilter() == null) {
423423
throw new IllegalArgumentException("Analyzer [" + this.postFilter + "] doesn't exists");
424424
}

core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilder.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@
3737
import org.elasticsearch.index.analysis.TokenFilterFactory;
3838
import org.elasticsearch.index.mapper.MapperService;
3939
import org.elasticsearch.index.query.QueryShardContext;
40-
import org.elasticsearch.script.ExecutableScript;
4140
import org.elasticsearch.script.Script;
42-
import org.elasticsearch.script.ScriptContext;
4341
import org.elasticsearch.script.ScriptType;
4442
import org.elasticsearch.script.TemplateScript;
4543
import org.elasticsearch.search.suggest.SuggestionBuilder;
@@ -55,7 +53,6 @@
5553
import java.util.Map.Entry;
5654
import java.util.Objects;
5755
import java.util.Set;
58-
import java.util.function.Function;
5956

6057
/**
6158
* Defines the actual suggest command for phrase suggestions ( <tt>phrase</tt>).

core/src/test/java/org/elasticsearch/search/suggest/AbstractSuggestionBuilderTestCase.java

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

2020
package org.elasticsearch.search.suggest;
2121

22+
import org.apache.lucene.analysis.core.SimpleAnalyzer;
23+
import org.elasticsearch.Version;
24+
import org.elasticsearch.cluster.metadata.IndexMetaData;
2225
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
2326
import org.elasticsearch.common.io.stream.Writeable;
2427
import org.elasticsearch.common.settings.Settings;
@@ -28,15 +31,32 @@
2831
import org.elasticsearch.common.xcontent.XContentFactory;
2932
import org.elasticsearch.common.xcontent.XContentParser;
3033
import org.elasticsearch.common.xcontent.XContentType;
34+
import org.elasticsearch.index.Index;
35+
import org.elasticsearch.index.IndexSettings;
36+
import org.elasticsearch.index.analysis.AnalyzerScope;
37+
import org.elasticsearch.index.analysis.NamedAnalyzer;
38+
import org.elasticsearch.index.mapper.MappedFieldType;
39+
import org.elasticsearch.index.mapper.MapperService;
40+
import org.elasticsearch.index.query.QueryShardContext;
41+
import org.elasticsearch.mock.orig.Mockito;
42+
import org.elasticsearch.script.Script;
43+
import org.elasticsearch.script.ScriptService;
44+
import org.elasticsearch.script.TemplateScript;
3145
import org.elasticsearch.search.SearchModule;
46+
import org.elasticsearch.search.suggest.SuggestionSearchContext.SuggestionContext;
3247
import org.elasticsearch.test.ESTestCase;
48+
import org.elasticsearch.test.IndexSettingsModule;
3349
import org.junit.AfterClass;
3450
import org.junit.BeforeClass;
3551

3652
import java.io.IOException;
3753

3854
import static java.util.Collections.emptyList;
55+
import static org.elasticsearch.common.lucene.BytesRefs.toBytesRef;
3956
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
57+
import static org.mockito.Matchers.any;
58+
import static org.mockito.Mockito.mock;
59+
import static org.mockito.Mockito.when;
4060

4161
public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBuilder<SB>> extends ESTestCase {
4262

@@ -48,7 +68,7 @@ public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBui
4868
* setup for the whole base test class
4969
*/
5070
@BeforeClass
51-
public static void init() throws IOException {
71+
public static void init() {
5272
SearchModule searchModule = new SearchModule(Settings.EMPTY, false, emptyList());
5373
namedWriteableRegistry = new NamedWriteableRegistry(searchModule.getNamedWriteables());
5474
xContentRegistry = new NamedXContentRegistry(searchModule.getNamedXContents());
@@ -98,7 +118,7 @@ public static void setCommonPropertiesOnRandomBuilder(SuggestionBuilder<?> rando
98118
/**
99119
* Test equality and hashCode properties
100120
*/
101-
public void testEqualsAndHashcode() throws IOException {
121+
public void testEqualsAndHashcode() {
102122
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
103123
checkEqualsAndHashCode(randomTestBuilder(), this::copy, this::mutate);
104124
}
@@ -131,6 +151,62 @@ public void testFromXContent() throws IOException {
131151
}
132152
}
133153

154+
public void testBuild() throws IOException {
155+
Settings indexSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
156+
IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(new Index(randomAlphaOfLengthBetween(1, 10), "_na_"),
157+
indexSettings);
158+
MapperService mapperService = mock(MapperService.class);
159+
ScriptService scriptService = mock(ScriptService.class);
160+
MappedFieldType fieldType = mockFieldType();
161+
boolean fieldTypeSearchAnalyzerSet = randomBoolean();
162+
if (fieldTypeSearchAnalyzerSet) {
163+
NamedAnalyzer searchAnalyzer = new NamedAnalyzer("fieldSearchAnalyzer", AnalyzerScope.INDEX, new SimpleAnalyzer());
164+
if (Mockito.mockingDetails(fieldType).isMock()) {
165+
when(fieldType.searchAnalyzer()).thenReturn(searchAnalyzer);
166+
} else {
167+
fieldType.setSearchAnalyzer(searchAnalyzer);
168+
}
169+
} else {
170+
when(mapperService.searchAnalyzer())
171+
.thenReturn(new NamedAnalyzer("mapperServiceSearchAnalyzer", AnalyzerScope.INDEX, new SimpleAnalyzer()));
172+
}
173+
when(mapperService.fullName(any(String.class))).thenReturn(fieldType);
174+
when(mapperService.getNamedAnalyzer(any(String.class))).then(
175+
invocation -> new NamedAnalyzer((String) invocation.getArguments()[0], AnalyzerScope.INDEX, new SimpleAnalyzer()));
176+
when(scriptService.compile(any(Script.class), any())).thenReturn(mock(TemplateScript.Factory.class));
177+
QueryShardContext mockShardContext = new QueryShardContext(0, idxSettings, null, null, mapperService, null, scriptService,
178+
xContentRegistry(), null, null, System::currentTimeMillis);
179+
180+
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
181+
SB suggestionBuilder = randomTestBuilder();
182+
SuggestionContext suggestionContext = suggestionBuilder.build(mockShardContext);
183+
assertEquals(toBytesRef(suggestionBuilder.text()), suggestionContext.getText());
184+
if (suggestionBuilder.text() != null && suggestionBuilder.prefix() == null) {
185+
assertEquals(toBytesRef(suggestionBuilder.text()), suggestionContext.getPrefix());
186+
} else {
187+
assertEquals(toBytesRef(suggestionBuilder.prefix()), suggestionContext.getPrefix());
188+
}
189+
assertEquals(toBytesRef(suggestionBuilder.regex()), suggestionContext.getRegex());
190+
assertEquals(suggestionBuilder.field(), suggestionContext.getField());
191+
int expectedSize = suggestionBuilder.size() != null ? suggestionBuilder.size : 5;
192+
assertEquals(expectedSize, suggestionContext.getSize());
193+
Integer expectedShardSize = suggestionBuilder.shardSize != null ? suggestionBuilder.shardSize : Math.max(expectedSize, 5);
194+
assertEquals(expectedShardSize, suggestionContext.getShardSize());
195+
assertSame(mockShardContext, suggestionContext.getShardContext());
196+
if (suggestionBuilder.analyzer() != null) {
197+
assertEquals(suggestionBuilder.analyzer(), ((NamedAnalyzer) suggestionContext.getAnalyzer()).name());
198+
} else if (fieldTypeSearchAnalyzerSet){
199+
assertEquals("fieldSearchAnalyzer", ((NamedAnalyzer) suggestionContext.getAnalyzer()).name());
200+
} else {
201+
assertEquals("mapperServiceSearchAnalyzer", ((NamedAnalyzer) suggestionContext.getAnalyzer()).name());
202+
}
203+
}
204+
}
205+
206+
protected MappedFieldType mockFieldType() {
207+
return mock(MappedFieldType.class);
208+
}
209+
134210
/**
135211
* Subclasses can override this method and return a set of fields which should be protected from
136212
* recursive random shuffling in the {@link #testFromXContent()} test case

core/src/test/java/org/elasticsearch/search/suggest/completion/CompletionSuggesterBuilderTests.java

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,38 +22,49 @@
2222
import org.elasticsearch.common.bytes.BytesReference;
2323
import org.elasticsearch.common.unit.Fuzziness;
2424
import org.elasticsearch.common.xcontent.ToXContent;
25+
import org.elasticsearch.index.mapper.CompletionFieldMapper.CompletionFieldType;
26+
import org.elasticsearch.index.mapper.MappedFieldType;
2527
import org.elasticsearch.search.suggest.AbstractSuggestionBuilderTestCase;
2628
import org.elasticsearch.search.suggest.completion.context.CategoryQueryContext;
29+
import org.elasticsearch.search.suggest.completion.context.ContextBuilder;
30+
import org.elasticsearch.search.suggest.completion.context.ContextMapping;
31+
import org.elasticsearch.search.suggest.completion.context.ContextMappings;
2732
import org.elasticsearch.search.suggest.completion.context.GeoQueryContext;
2833

2934
import java.io.IOException;
3035
import java.util.ArrayList;
36+
import java.util.Arrays;
3137
import java.util.Collections;
3238
import java.util.HashMap;
3339
import java.util.List;
3440
import java.util.Map;
3541

3642
public class CompletionSuggesterBuilderTests extends AbstractSuggestionBuilderTestCase<CompletionSuggestionBuilder> {
3743

38-
private static final String[] SHUFFLE_PROTECTED_FIELDS = new String[] {CompletionSuggestionBuilder.CONTEXTS_FIELD.getPreferredName()};
44+
private static final String[] SHUFFLE_PROTECTED_FIELDS = new String[] { CompletionSuggestionBuilder.CONTEXTS_FIELD.getPreferredName() };
45+
private static final Map<String, List<? extends ToXContent>> contextMap = new HashMap<>();
46+
private static String categoryContextName;
47+
private static String geoQueryContextName;
48+
private static List<ContextMapping> contextMappings;
3949

4050
@Override
4151
protected CompletionSuggestionBuilder randomSuggestionBuilder() {
4252
return randomCompletionSuggestionBuilder();
4353
}
4454

4555
public static CompletionSuggestionBuilder randomCompletionSuggestionBuilder() {
46-
return randomSuggestionBuilderWithContextInfo().builder;
47-
}
48-
49-
private static class BuilderAndInfo {
50-
CompletionSuggestionBuilder builder;
51-
List<String> catContexts = new ArrayList<>();
52-
List<String> geoContexts = new ArrayList<>();
53-
}
54-
55-
private static BuilderAndInfo randomSuggestionBuilderWithContextInfo() {
56-
final BuilderAndInfo builderAndInfo = new BuilderAndInfo();
56+
// lazy initialization of context names and mappings, cannot be done in init method because other test
57+
// also create random CompletionSuggestionBuilder instances
58+
if (categoryContextName == null) {
59+
categoryContextName = randomAlphaOfLength(10);
60+
}
61+
if (geoQueryContextName == null) {
62+
geoQueryContextName = randomAlphaOfLength(10);
63+
}
64+
if (contextMappings == null) {
65+
contextMappings = Arrays.asList(new ContextMapping[] { ContextBuilder.category(categoryContextName).build(),
66+
ContextBuilder.geo(geoQueryContextName).build() });
67+
}
5768
CompletionSuggestionBuilder testBuilder = new CompletionSuggestionBuilder(randomAlphaOfLengthBetween(2, 20));
5869
setCommonPropertiesOnRandomBuilder(testBuilder);
5970
switch (randomIntBetween(0, 3)) {
@@ -77,23 +88,18 @@ private static BuilderAndInfo randomSuggestionBuilderWithContextInfo() {
7788
for (int i = 0; i < numContext; i++) {
7889
contexts.add(CategoryQueryContextTests.randomCategoryQueryContext());
7990
}
80-
String name = randomAlphaOfLength(10);
81-
contextMap.put(name, contexts);
82-
builderAndInfo.catContexts.add(name);
91+
contextMap.put(categoryContextName, contexts);
8392
}
8493
if (randomBoolean()) {
8594
int numContext = randomIntBetween(1, 5);
8695
List<GeoQueryContext> contexts = new ArrayList<>(numContext);
8796
for (int i = 0; i < numContext; i++) {
8897
contexts.add(GeoQueryContextTests.randomGeoQueryContext());
8998
}
90-
String name = randomAlphaOfLength(10);
91-
contextMap.put(name, contexts);
92-
builderAndInfo.geoContexts.add(name);
99+
contextMap.put(geoQueryContextName, contexts);
93100
}
94101
testBuilder.contexts(contextMap);
95-
builderAndInfo.builder = testBuilder;
96-
return builderAndInfo;
102+
return testBuilder;
97103
}
98104

99105
/**
@@ -137,4 +143,11 @@ protected void mutateSpecificParameters(CompletionSuggestionBuilder builder) thr
137143
throw new IllegalStateException("should not through");
138144
}
139145
}
146+
147+
@Override
148+
protected MappedFieldType mockFieldType() {
149+
CompletionFieldType completionFieldType = new CompletionFieldType();
150+
completionFieldType.setContextMappings(new ContextMappings(contextMappings));
151+
return completionFieldType;
152+
}
140153
}

core/src/test/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilderTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ protected void mutateSpecificParameters(PhraseSuggestionBuilder builder) throws
146146
}
147147
}
148148

149-
public void testInvalidParameters() throws IOException {
149+
public void testInvalidParameters() {
150150
// test missing field name
151151
Exception e = expectThrows(NullPointerException.class, () -> new PhraseSuggestionBuilder((String) null));
152152
assertEquals("suggestion requires a field name", e.getMessage());

core/src/test/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilderTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ protected void mutateSpecificParameters(TermSuggestionBuilder builder) throws IO
142142
}
143143
}
144144

145-
public void testInvalidParameters() throws IOException {
145+
public void testInvalidParameters() {
146146
// test missing field name
147147
Exception e = expectThrows(NullPointerException.class, () -> new TermSuggestionBuilder((String) null));
148148
assertEquals("suggestion requires a field name", e.getMessage());

0 commit comments

Comments
 (0)