Completion types with multi-fields support (#34081)
Mappings with completion type and multi-fields, were not able to index array or object format on completion fields. Only string format was supported. This is fixed by providing multiField parser with externalValueContext with already parsed object closes #15115
This commit is contained in:
parent
b1b0f3276b
commit
3f8cc89c9f
|
@ -0,0 +1,299 @@
|
|||
|
||||
---
|
||||
"Search by suggestion and by keyword sub-field should work":
|
||||
|
||||
- skip:
|
||||
version: " - 6.99.99"
|
||||
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
|
||||
|
||||
- do:
|
||||
indices.create:
|
||||
index: completion_with_sub_keyword
|
||||
body:
|
||||
mappings:
|
||||
test:
|
||||
"properties":
|
||||
"suggest_1":
|
||||
"type" : "completion"
|
||||
"fields":
|
||||
"text_raw":
|
||||
"type" : "keyword"
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: completion_with_sub_keyword
|
||||
type: test
|
||||
id: 1
|
||||
body:
|
||||
suggest_1: "bar"
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: completion_with_sub_keyword
|
||||
type: test
|
||||
id: 2
|
||||
body:
|
||||
suggest_1: "baz"
|
||||
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
- do:
|
||||
search:
|
||||
index: completion_with_sub_keyword
|
||||
body:
|
||||
suggest:
|
||||
result:
|
||||
text: "b"
|
||||
completion:
|
||||
field: suggest_1
|
||||
|
||||
- length: { suggest.result: 1 }
|
||||
- length: { suggest.result.0.options: 2 }
|
||||
|
||||
|
||||
- do:
|
||||
search:
|
||||
index: completion_with_sub_keyword
|
||||
body:
|
||||
query: { term: { suggest_1.text_raw: "bar" }}
|
||||
|
||||
- match: { hits.total: 1 }
|
||||
|
||||
|
||||
|
||||
---
|
||||
"Search by suggestion on sub field should work":
|
||||
|
||||
- skip:
|
||||
version: " - 6.99.99"
|
||||
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
|
||||
|
||||
- do:
|
||||
indices.create:
|
||||
index: completion_with_sub_completion
|
||||
body:
|
||||
mappings:
|
||||
test:
|
||||
"properties":
|
||||
"suggest_1":
|
||||
"type": "completion"
|
||||
"fields":
|
||||
"suggest_2":
|
||||
"type": "completion"
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: completion_with_sub_completion
|
||||
type: test
|
||||
id: 1
|
||||
body:
|
||||
suggest_1: "bar"
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: completion_with_sub_completion
|
||||
type: test
|
||||
id: 2
|
||||
body:
|
||||
suggest_1: "baz"
|
||||
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
- do:
|
||||
search:
|
||||
index: completion_with_sub_completion
|
||||
body:
|
||||
suggest:
|
||||
result:
|
||||
text: "b"
|
||||
completion:
|
||||
field: suggest_1.suggest_2
|
||||
|
||||
- length: { suggest.result: 1 }
|
||||
- length: { suggest.result.0.options: 2 }
|
||||
|
||||
---
|
||||
"Search by suggestion on sub field with context should work":
|
||||
|
||||
- skip:
|
||||
version: " - 6.99.99"
|
||||
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
|
||||
|
||||
- do:
|
||||
indices.create:
|
||||
index: completion_with_context
|
||||
body:
|
||||
mappings:
|
||||
test:
|
||||
"properties":
|
||||
"suggest_1":
|
||||
"type": "completion"
|
||||
"contexts":
|
||||
-
|
||||
"name": "color"
|
||||
"type": "category"
|
||||
"fields":
|
||||
"suggest_2":
|
||||
"type": "completion"
|
||||
"contexts":
|
||||
-
|
||||
"name": "color"
|
||||
"type": "category"
|
||||
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: completion_with_context
|
||||
type: test
|
||||
id: 1
|
||||
body:
|
||||
suggest_1:
|
||||
input: "foo red"
|
||||
contexts:
|
||||
color: "red"
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: completion_with_context
|
||||
type: test
|
||||
id: 2
|
||||
body:
|
||||
suggest_1:
|
||||
input: "foo blue"
|
||||
contexts:
|
||||
color: "blue"
|
||||
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
- do:
|
||||
search:
|
||||
index: completion_with_context
|
||||
body:
|
||||
suggest:
|
||||
result:
|
||||
prefix: "foo"
|
||||
completion:
|
||||
field: suggest_1.suggest_2
|
||||
contexts:
|
||||
color: "red"
|
||||
|
||||
- length: { suggest.result: 1 }
|
||||
- length: { suggest.result.0.options: 1 }
|
||||
- match: { suggest.result.0.options.0.text: "foo red" }
|
||||
|
||||
|
||||
---
|
||||
"Search by suggestion on sub field with weight should work":
|
||||
|
||||
- skip:
|
||||
version: " - 6.99.99"
|
||||
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
|
||||
|
||||
- do:
|
||||
indices.create:
|
||||
index: completion_with_weight
|
||||
body:
|
||||
mappings:
|
||||
test:
|
||||
"properties":
|
||||
"suggest_1":
|
||||
"type": "completion"
|
||||
"fields":
|
||||
"suggest_2":
|
||||
"type": "completion"
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: completion_with_weight
|
||||
type: test
|
||||
id: 1
|
||||
body:
|
||||
suggest_1:
|
||||
input: "bar"
|
||||
weight: 2
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: completion_with_weight
|
||||
type: test
|
||||
id: 2
|
||||
body:
|
||||
suggest_1:
|
||||
input: "baz"
|
||||
weight: 3
|
||||
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
- do:
|
||||
search:
|
||||
index: completion_with_weight
|
||||
body:
|
||||
suggest:
|
||||
result:
|
||||
text: "b"
|
||||
completion:
|
||||
field: suggest_1.suggest_2
|
||||
|
||||
- length: { suggest.result: 1 }
|
||||
- length: { suggest.result.0.options: 2 }
|
||||
- match: { suggest.result.0.options.0.text: "baz" }
|
||||
- match: { suggest.result.0.options.1.text: "bar" }
|
||||
|
||||
---
|
||||
"Search by suggestion on geofield-hash on sub field should work":
|
||||
|
||||
- skip:
|
||||
version: " - 6.99.99"
|
||||
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
|
||||
|
||||
- do:
|
||||
indices.create:
|
||||
index: geofield_with_completion
|
||||
body:
|
||||
mappings:
|
||||
test:
|
||||
"properties":
|
||||
"geofield":
|
||||
"type": "geo_point"
|
||||
"fields":
|
||||
"suggest_1":
|
||||
"type": "completion"
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: geofield_with_completion
|
||||
type: test
|
||||
id: 1
|
||||
body:
|
||||
geofield: "hgjhrwysvqw7"
|
||||
#41.12,-72.34,12
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: geofield_with_completion
|
||||
type: test
|
||||
id: 1
|
||||
body:
|
||||
geofield: "hgm4psywmkn7"
|
||||
#41.12,-71.34,12
|
||||
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
- do:
|
||||
search:
|
||||
index: geofield_with_completion
|
||||
body:
|
||||
suggest:
|
||||
result:
|
||||
prefix: "hgm"
|
||||
completion:
|
||||
field: geofield.suggest_1
|
||||
|
||||
|
||||
- length: { suggest.result: 1 }
|
||||
- length: { suggest.result.0.options: 1 }
|
|
@ -436,8 +436,9 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
|
|||
Token token = parser.currentToken();
|
||||
Map<String, CompletionInputMetaData> inputMap = new HashMap<>(1);
|
||||
|
||||
// ignore null values
|
||||
if (token == Token.VALUE_NULL) {
|
||||
if (context.externalValueSet()) {
|
||||
inputMap = getInputMapFromExternalValue(context);
|
||||
} else if (token == Token.VALUE_NULL) { // ignore null values
|
||||
return;
|
||||
} else if (token == Token.START_ARRAY) {
|
||||
while ((token = parser.nextToken()) != Token.END_ARRAY) {
|
||||
|
@ -471,12 +472,33 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
|
|||
context.doc().add(new SuggestField(fieldType().name(), input, metaData.weight));
|
||||
}
|
||||
}
|
||||
|
||||
List<IndexableField> fields = new ArrayList<>(1);
|
||||
createFieldNamesField(context, fields);
|
||||
for (IndexableField field : fields) {
|
||||
context.doc().add(field);
|
||||
}
|
||||
multiFields.parse(this, context);
|
||||
|
||||
for (CompletionInputMetaData metaData: inputMap.values()) {
|
||||
ParseContext externalValueContext = context.createExternalValueContext(metaData);
|
||||
multiFields.parse(this, externalValueContext);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, CompletionInputMetaData> getInputMapFromExternalValue(ParseContext context) {
|
||||
Map<String, CompletionInputMetaData> inputMap;
|
||||
if (isExternalValueOfClass(context, CompletionInputMetaData.class)) {
|
||||
CompletionInputMetaData inputAndMeta = (CompletionInputMetaData) context.externalValue();
|
||||
inputMap = Collections.singletonMap(inputAndMeta.input, inputAndMeta);
|
||||
} else {
|
||||
String fieldName = context.externalValue().toString();
|
||||
inputMap = Collections.singletonMap(fieldName, new CompletionInputMetaData(fieldName, Collections.emptyMap(), 1));
|
||||
}
|
||||
return inputMap;
|
||||
}
|
||||
|
||||
private boolean isExternalValueOfClass(ParseContext context, Class<?> clazz) {
|
||||
return context.externalValue().getClass().equals(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -487,7 +509,7 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
|
|||
private void parse(ParseContext parseContext, Token token, XContentParser parser, Map<String, CompletionInputMetaData> inputMap) throws IOException {
|
||||
String currentFieldName = null;
|
||||
if (token == Token.VALUE_STRING) {
|
||||
inputMap.put(parser.text(), new CompletionInputMetaData(Collections.<String, Set<CharSequence>>emptyMap(), 1));
|
||||
inputMap.put(parser.text(), new CompletionInputMetaData(parser.text(), Collections.emptyMap(), 1));
|
||||
} else if (token == Token.START_OBJECT) {
|
||||
Set<String> inputs = new HashSet<>();
|
||||
int weight = 1;
|
||||
|
@ -561,7 +583,7 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
|
|||
}
|
||||
for (String input : inputs) {
|
||||
if (inputMap.containsKey(input) == false || inputMap.get(input).weight < weight) {
|
||||
inputMap.put(input, new CompletionInputMetaData(contextsMap, weight));
|
||||
inputMap.put(input, new CompletionInputMetaData(input, contextsMap, weight));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -570,13 +592,20 @@ public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapp
|
|||
}
|
||||
|
||||
static class CompletionInputMetaData {
|
||||
public final String input;
|
||||
public final Map<String, Set<CharSequence>> contexts;
|
||||
public final int weight;
|
||||
|
||||
CompletionInputMetaData(Map<String, Set<CharSequence>> contexts, int weight) {
|
||||
CompletionInputMetaData(String input, Map<String, Set<CharSequence>> contexts, int weight) {
|
||||
this.input = input;
|
||||
this.contexts = contexts;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
*/
|
||||
package org.elasticsearch.index.mapper;
|
||||
|
||||
import org.apache.lucene.document.SortedSetDocValuesField;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.suggest.document.CompletionAnalyzer;
|
||||
import org.apache.lucene.search.suggest.document.ContextSuggestField;
|
||||
import org.apache.lucene.search.suggest.document.FuzzyCompletionQuery;
|
||||
import org.apache.lucene.search.suggest.document.PrefixCompletionQuery;
|
||||
import org.apache.lucene.search.suggest.document.RegexCompletionQuery;
|
||||
|
@ -42,11 +44,18 @@ import org.elasticsearch.common.xcontent.json.JsonXContent;
|
|||
import org.elasticsearch.index.IndexService;
|
||||
import org.elasticsearch.index.analysis.NamedAnalyzer;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
import org.hamcrest.FeatureMatcher;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hamcrest.core.CombinableMatcher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
|
||||
import static org.hamcrest.Matchers.arrayWithSize;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
@ -182,6 +191,328 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
|
|||
assertEquals("failed to parse [completion]: expected text or object, but got VALUE_NUMBER", e.getCause().getMessage());
|
||||
}
|
||||
|
||||
public void testKeywordWithSubCompletionAndContext() throws Exception {
|
||||
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties")
|
||||
.startObject("keywordfield")
|
||||
.field("type", "keyword")
|
||||
.startObject("fields")
|
||||
.startObject("subsuggest")
|
||||
.field("type", "completion")
|
||||
.startArray("contexts")
|
||||
.startObject()
|
||||
.field("name","place_type")
|
||||
.field("type","category")
|
||||
.field("path","cat")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject().endObject()
|
||||
.endObject().endObject()
|
||||
);
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
|
||||
|
||||
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
|
||||
.bytes(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.array("keywordfield", "key1", "key2", "key3")
|
||||
.endObject()),
|
||||
XContentType.JSON));
|
||||
|
||||
ParseContext.Document indexableFields = parsedDocument.rootDoc();
|
||||
|
||||
assertThat(indexableFields.getFields("keywordfield"), arrayContainingInAnyOrder(
|
||||
keywordField("key1"),
|
||||
sortedSetDocValuesField("key1"),
|
||||
keywordField("key2"),
|
||||
sortedSetDocValuesField("key2"),
|
||||
keywordField("key3"),
|
||||
sortedSetDocValuesField("key3")
|
||||
));
|
||||
assertThat(indexableFields.getFields("keywordfield.subsuggest"), arrayContainingInAnyOrder(
|
||||
contextSuggestField("key1"),
|
||||
contextSuggestField("key2"),
|
||||
contextSuggestField("key3")
|
||||
));
|
||||
}
|
||||
|
||||
public void testCompletionWithContextAndSubCompletion() throws Exception {
|
||||
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties")
|
||||
.startObject("suggest")
|
||||
.field("type", "completion")
|
||||
.startArray("contexts")
|
||||
.startObject()
|
||||
.field("name","place_type")
|
||||
.field("type","category")
|
||||
.field("path","cat")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.startObject("fields")
|
||||
.startObject("subsuggest")
|
||||
.field("type", "completion")
|
||||
.startArray("contexts")
|
||||
.startObject()
|
||||
.field("name","place_type")
|
||||
.field("type","category")
|
||||
.field("path","cat")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject().endObject()
|
||||
.endObject().endObject()
|
||||
);
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
|
||||
|
||||
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
|
||||
.bytes(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.startObject("suggest")
|
||||
.array("input","timmy","starbucks")
|
||||
.startObject("contexts")
|
||||
.array("place_type","cafe","food")
|
||||
.endObject()
|
||||
.field("weight", 3)
|
||||
.endObject()
|
||||
.endObject()),
|
||||
XContentType.JSON));
|
||||
|
||||
ParseContext.Document indexableFields = parsedDocument.rootDoc();
|
||||
assertThat(indexableFields.getFields("suggest"), arrayContainingInAnyOrder(
|
||||
contextSuggestField("timmy"),
|
||||
contextSuggestField("starbucks")
|
||||
));
|
||||
assertThat(indexableFields.getFields("suggest.subsuggest"), arrayContainingInAnyOrder(
|
||||
contextSuggestField("timmy"),
|
||||
contextSuggestField("starbucks")
|
||||
));
|
||||
//unable to assert about context, covered in a REST test
|
||||
}
|
||||
|
||||
public void testCompletionWithContextAndSubCompletionIndexByPath() throws Exception {
|
||||
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties")
|
||||
.startObject("suggest")
|
||||
.field("type", "completion")
|
||||
.startArray("contexts")
|
||||
.startObject()
|
||||
.field("name","place_type")
|
||||
.field("type","category")
|
||||
.field("path","cat")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.startObject("fields")
|
||||
.startObject("subsuggest")
|
||||
.field("type", "completion")
|
||||
.startArray("contexts")
|
||||
.startObject()
|
||||
.field("name","place_type")
|
||||
.field("type","category")
|
||||
.field("path","cat")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject().endObject()
|
||||
.endObject().endObject()
|
||||
);
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
|
||||
|
||||
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
|
||||
.bytes(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.array("suggest", "timmy","starbucks")
|
||||
.array("cat","cafe","food")
|
||||
.endObject()),
|
||||
XContentType.JSON));
|
||||
|
||||
ParseContext.Document indexableFields = parsedDocument.rootDoc();
|
||||
assertThat(indexableFields.getFields("suggest"), arrayContainingInAnyOrder(
|
||||
contextSuggestField("timmy"),
|
||||
contextSuggestField("starbucks")
|
||||
));
|
||||
assertThat(indexableFields.getFields("suggest.subsuggest"), arrayContainingInAnyOrder(
|
||||
contextSuggestField("timmy"),
|
||||
contextSuggestField("starbucks")
|
||||
));
|
||||
//unable to assert about context, covered in a REST test
|
||||
}
|
||||
|
||||
|
||||
public void testKeywordWithSubCompletionAndStringInsert() throws Exception {
|
||||
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("geofield")
|
||||
.field("type", "geo_point")
|
||||
.startObject("fields")
|
||||
.startObject("analyzed")
|
||||
.field("type", "completion")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject().endObject()
|
||||
.endObject().endObject()
|
||||
);
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
|
||||
|
||||
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
|
||||
.bytes(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.field("geofield", "drm3btev3e86")//"41.12,-71.34"
|
||||
.endObject()),
|
||||
XContentType.JSON));
|
||||
|
||||
ParseContext.Document indexableFields = parsedDocument.rootDoc();
|
||||
assertThat(indexableFields.getFields("geofield"), arrayWithSize(2));
|
||||
assertThat(indexableFields.getFields("geofield.analyzed"), arrayContainingInAnyOrder(
|
||||
suggestField("drm3btev3e86")
|
||||
));
|
||||
//unable to assert about geofield content, covered in a REST test
|
||||
}
|
||||
|
||||
public void testCompletionTypeWithSubCompletionFieldAndStringInsert() throws Exception {
|
||||
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("suggest")
|
||||
.field("type", "completion")
|
||||
.startObject("fields")
|
||||
.startObject("subsuggest")
|
||||
.field("type", "completion")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject().endObject()
|
||||
.endObject().endObject()
|
||||
);
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
|
||||
|
||||
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
|
||||
.bytes(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.field("suggest", "suggestion")
|
||||
.endObject()),
|
||||
XContentType.JSON));
|
||||
|
||||
ParseContext.Document indexableFields = parsedDocument.rootDoc();
|
||||
assertThat(indexableFields.getFields("suggest"), arrayContainingInAnyOrder(
|
||||
suggestField("suggestion")
|
||||
));
|
||||
assertThat(indexableFields.getFields("suggest.subsuggest"), arrayContainingInAnyOrder(
|
||||
suggestField("suggestion")
|
||||
));
|
||||
}
|
||||
|
||||
public void testCompletionTypeWithSubCompletionFieldAndObjectInsert() throws Exception {
|
||||
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("completion")
|
||||
.field("type", "completion")
|
||||
.startObject("fields")
|
||||
.startObject("analyzed")
|
||||
.field("type", "completion")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject().endObject()
|
||||
.endObject().endObject()
|
||||
);
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
|
||||
|
||||
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
|
||||
.bytes(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.startObject("completion")
|
||||
.array("input","New York", "NY")
|
||||
.field("weight",34)
|
||||
.endObject()
|
||||
.endObject()),
|
||||
XContentType.JSON));
|
||||
|
||||
ParseContext.Document indexableFields = parsedDocument.rootDoc();
|
||||
assertThat(indexableFields.getFields("completion"), arrayContainingInAnyOrder(
|
||||
suggestField("New York"),
|
||||
suggestField("NY")
|
||||
));
|
||||
assertThat(indexableFields.getFields("completion.analyzed"), arrayContainingInAnyOrder(
|
||||
suggestField("New York"),
|
||||
suggestField("NY")
|
||||
));
|
||||
//unable to assert about weight, covered in a REST test
|
||||
}
|
||||
|
||||
public void testCompletionTypeWithSubKeywordFieldAndObjectInsert() throws Exception {
|
||||
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("completion")
|
||||
.field("type", "completion")
|
||||
.startObject("fields")
|
||||
.startObject("analyzed")
|
||||
.field("type", "keyword")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject().endObject()
|
||||
.endObject().endObject()
|
||||
);
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
|
||||
|
||||
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
|
||||
.bytes(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.startObject("completion")
|
||||
.array("input","New York", "NY")
|
||||
.field("weight",34)
|
||||
.endObject()
|
||||
.endObject()),
|
||||
XContentType.JSON));
|
||||
|
||||
ParseContext.Document indexableFields = parsedDocument.rootDoc();
|
||||
assertThat(indexableFields.getFields("completion"), arrayContainingInAnyOrder(
|
||||
suggestField("New York"),
|
||||
suggestField("NY")
|
||||
));
|
||||
assertThat(indexableFields.getFields("completion.analyzed"), arrayContainingInAnyOrder(
|
||||
keywordField("New York"),
|
||||
sortedSetDocValuesField("New York"),
|
||||
keywordField("NY"),
|
||||
sortedSetDocValuesField("NY")
|
||||
));
|
||||
//unable to assert about weight, covered in a REST test
|
||||
}
|
||||
|
||||
public void testCompletionTypeWithSubKeywordFieldAndStringInsert() throws Exception {
|
||||
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("completion")
|
||||
.field("type", "completion")
|
||||
.startObject("fields")
|
||||
.startObject("analyzed")
|
||||
.field("type", "keyword")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject().endObject()
|
||||
.endObject().endObject()
|
||||
);
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse("type1", new CompressedXContent(mapping));
|
||||
|
||||
ParsedDocument parsedDocument = defaultMapper.parse(SourceToParse.source("test", "type1", "1", BytesReference
|
||||
.bytes(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.field("completion", "suggestion")
|
||||
.endObject()),
|
||||
XContentType.JSON));
|
||||
|
||||
ParseContext.Document indexableFields = parsedDocument.rootDoc();
|
||||
assertThat(indexableFields.getFields("completion"), arrayContainingInAnyOrder(
|
||||
suggestField("suggestion")
|
||||
));
|
||||
assertThat(indexableFields.getFields("completion.analyzed"), arrayContainingInAnyOrder(
|
||||
keywordField("suggestion"),
|
||||
sortedSetDocValuesField("suggestion")
|
||||
));
|
||||
}
|
||||
|
||||
public void testParsingMultiValued() throws Exception {
|
||||
String mapping = Strings.toString(jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("completion")
|
||||
|
@ -199,7 +530,10 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
|
|||
.endObject()),
|
||||
XContentType.JSON));
|
||||
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
|
||||
assertSuggestFields(fields, 2);
|
||||
assertThat(fields, arrayContainingInAnyOrder(
|
||||
suggestField("suggestion1"),
|
||||
suggestField("suggestion2")
|
||||
));
|
||||
}
|
||||
|
||||
public void testParsingWithWeight() throws Exception {
|
||||
|
@ -222,7 +556,9 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
|
|||
.endObject()),
|
||||
XContentType.JSON));
|
||||
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
|
||||
assertSuggestFields(fields, 1);
|
||||
assertThat(fields, arrayContainingInAnyOrder(
|
||||
suggestField("suggestion")
|
||||
));
|
||||
}
|
||||
|
||||
public void testParsingMultiValueWithWeight() throws Exception {
|
||||
|
@ -245,7 +581,11 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
|
|||
.endObject()),
|
||||
XContentType.JSON));
|
||||
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
|
||||
assertSuggestFields(fields, 3);
|
||||
assertThat(fields, arrayContainingInAnyOrder(
|
||||
suggestField("suggestion1"),
|
||||
suggestField("suggestion2"),
|
||||
suggestField("suggestion3")
|
||||
));
|
||||
}
|
||||
|
||||
public void testParsingWithGeoFieldAlias() throws Exception {
|
||||
|
@ -318,7 +658,11 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
|
|||
.endObject()),
|
||||
XContentType.JSON));
|
||||
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
|
||||
assertSuggestFields(fields, 3);
|
||||
assertThat(fields, arrayContainingInAnyOrder(
|
||||
suggestField("suggestion1"),
|
||||
suggestField("suggestion2"),
|
||||
suggestField("suggestion3")
|
||||
));
|
||||
}
|
||||
|
||||
public void testParsingMixed() throws Exception {
|
||||
|
@ -351,7 +695,14 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
|
|||
.endObject()),
|
||||
XContentType.JSON));
|
||||
IndexableField[] fields = parsedDocument.rootDoc().getFields(fieldMapper.name());
|
||||
assertSuggestFields(fields, 6);
|
||||
assertThat(fields, arrayContainingInAnyOrder(
|
||||
suggestField("suggestion1"),
|
||||
suggestField("suggestion2"),
|
||||
suggestField("suggestion3"),
|
||||
suggestField("suggestion4"),
|
||||
suggestField("suggestion5"),
|
||||
suggestField("suggestion6")
|
||||
));
|
||||
}
|
||||
|
||||
public void testNonContextEnabledParsingWithContexts() throws Exception {
|
||||
|
@ -508,9 +859,13 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
|
|||
}
|
||||
|
||||
private static void assertSuggestFields(IndexableField[] fields, int expected) {
|
||||
assertFieldsOfType(fields, SuggestField.class, expected);
|
||||
}
|
||||
|
||||
private static void assertFieldsOfType(IndexableField[] fields, Class<?> clazz, int expected) {
|
||||
int actualFieldCount = 0;
|
||||
for (IndexableField field : fields) {
|
||||
if (field instanceof SuggestField) {
|
||||
if (clazz.isInstance(field)) {
|
||||
actualFieldCount++;
|
||||
}
|
||||
}
|
||||
|
@ -529,4 +884,33 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase {
|
|||
);
|
||||
assertThat(e.getMessage(), containsString("name cannot be empty string"));
|
||||
}
|
||||
|
||||
private Matcher<IndexableField> suggestField(String value) {
|
||||
return Matchers.allOf(hasProperty(IndexableField::stringValue, equalTo(value)),
|
||||
Matchers.instanceOf(SuggestField.class));
|
||||
}
|
||||
|
||||
private Matcher<IndexableField> contextSuggestField(String value) {
|
||||
return Matchers.allOf(hasProperty(IndexableField::stringValue, equalTo(value)),
|
||||
Matchers.instanceOf(ContextSuggestField.class));
|
||||
}
|
||||
|
||||
private CombinableMatcher<IndexableField> sortedSetDocValuesField(String value) {
|
||||
return Matchers.both(hasProperty(IndexableField::binaryValue, equalTo(new BytesRef(value))))
|
||||
.and(Matchers.instanceOf(SortedSetDocValuesField.class));
|
||||
}
|
||||
|
||||
private CombinableMatcher<IndexableField> keywordField(String value) {
|
||||
return Matchers.both(hasProperty(IndexableField::binaryValue, equalTo(new BytesRef(value))))
|
||||
.and(hasProperty(IndexableField::fieldType, Matchers.instanceOf(KeywordFieldMapper.KeywordFieldType.class)));
|
||||
}
|
||||
|
||||
private <T, V> Matcher<T> hasProperty(Function<? super T, ? extends V> property, Matcher<V> valueMatcher) {
|
||||
return new FeatureMatcher<T, V>(valueMatcher, "object with", property.toString()) {
|
||||
@Override
|
||||
protected V featureValueOf(T actual) {
|
||||
return property.apply(actual);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue