Skip to content

Commit 3f8cc89

Browse files
authored
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
1 parent b1b0f32 commit 3f8cc89

File tree

3 files changed

+724
-12
lines changed

3 files changed

+724
-12
lines changed
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
2+
---
3+
"Search by suggestion and by keyword sub-field should work":
4+
5+
- skip:
6+
version: " - 6.99.99"
7+
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
8+
9+
- do:
10+
indices.create:
11+
index: completion_with_sub_keyword
12+
body:
13+
mappings:
14+
test:
15+
"properties":
16+
"suggest_1":
17+
"type" : "completion"
18+
"fields":
19+
"text_raw":
20+
"type" : "keyword"
21+
22+
- do:
23+
index:
24+
index: completion_with_sub_keyword
25+
type: test
26+
id: 1
27+
body:
28+
suggest_1: "bar"
29+
30+
- do:
31+
index:
32+
index: completion_with_sub_keyword
33+
type: test
34+
id: 2
35+
body:
36+
suggest_1: "baz"
37+
38+
- do:
39+
indices.refresh: {}
40+
41+
- do:
42+
search:
43+
index: completion_with_sub_keyword
44+
body:
45+
suggest:
46+
result:
47+
text: "b"
48+
completion:
49+
field: suggest_1
50+
51+
- length: { suggest.result: 1 }
52+
- length: { suggest.result.0.options: 2 }
53+
54+
55+
- do:
56+
search:
57+
index: completion_with_sub_keyword
58+
body:
59+
query: { term: { suggest_1.text_raw: "bar" }}
60+
61+
- match: { hits.total: 1 }
62+
63+
64+
65+
---
66+
"Search by suggestion on sub field should work":
67+
68+
- skip:
69+
version: " - 6.99.99"
70+
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
71+
72+
- do:
73+
indices.create:
74+
index: completion_with_sub_completion
75+
body:
76+
mappings:
77+
test:
78+
"properties":
79+
"suggest_1":
80+
"type": "completion"
81+
"fields":
82+
"suggest_2":
83+
"type": "completion"
84+
85+
- do:
86+
index:
87+
index: completion_with_sub_completion
88+
type: test
89+
id: 1
90+
body:
91+
suggest_1: "bar"
92+
93+
- do:
94+
index:
95+
index: completion_with_sub_completion
96+
type: test
97+
id: 2
98+
body:
99+
suggest_1: "baz"
100+
101+
- do:
102+
indices.refresh: {}
103+
104+
- do:
105+
search:
106+
index: completion_with_sub_completion
107+
body:
108+
suggest:
109+
result:
110+
text: "b"
111+
completion:
112+
field: suggest_1.suggest_2
113+
114+
- length: { suggest.result: 1 }
115+
- length: { suggest.result.0.options: 2 }
116+
117+
---
118+
"Search by suggestion on sub field with context should work":
119+
120+
- skip:
121+
version: " - 6.99.99"
122+
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
123+
124+
- do:
125+
indices.create:
126+
index: completion_with_context
127+
body:
128+
mappings:
129+
test:
130+
"properties":
131+
"suggest_1":
132+
"type": "completion"
133+
"contexts":
134+
-
135+
"name": "color"
136+
"type": "category"
137+
"fields":
138+
"suggest_2":
139+
"type": "completion"
140+
"contexts":
141+
-
142+
"name": "color"
143+
"type": "category"
144+
145+
146+
- do:
147+
index:
148+
index: completion_with_context
149+
type: test
150+
id: 1
151+
body:
152+
suggest_1:
153+
input: "foo red"
154+
contexts:
155+
color: "red"
156+
157+
- do:
158+
index:
159+
index: completion_with_context
160+
type: test
161+
id: 2
162+
body:
163+
suggest_1:
164+
input: "foo blue"
165+
contexts:
166+
color: "blue"
167+
168+
- do:
169+
indices.refresh: {}
170+
171+
- do:
172+
search:
173+
index: completion_with_context
174+
body:
175+
suggest:
176+
result:
177+
prefix: "foo"
178+
completion:
179+
field: suggest_1.suggest_2
180+
contexts:
181+
color: "red"
182+
183+
- length: { suggest.result: 1 }
184+
- length: { suggest.result.0.options: 1 }
185+
- match: { suggest.result.0.options.0.text: "foo red" }
186+
187+
188+
---
189+
"Search by suggestion on sub field with weight should work":
190+
191+
- skip:
192+
version: " - 6.99.99"
193+
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
194+
195+
- do:
196+
indices.create:
197+
index: completion_with_weight
198+
body:
199+
mappings:
200+
test:
201+
"properties":
202+
"suggest_1":
203+
"type": "completion"
204+
"fields":
205+
"suggest_2":
206+
"type": "completion"
207+
208+
- do:
209+
index:
210+
index: completion_with_weight
211+
type: test
212+
id: 1
213+
body:
214+
suggest_1:
215+
input: "bar"
216+
weight: 2
217+
218+
- do:
219+
index:
220+
index: completion_with_weight
221+
type: test
222+
id: 2
223+
body:
224+
suggest_1:
225+
input: "baz"
226+
weight: 3
227+
228+
- do:
229+
indices.refresh: {}
230+
231+
- do:
232+
search:
233+
index: completion_with_weight
234+
body:
235+
suggest:
236+
result:
237+
text: "b"
238+
completion:
239+
field: suggest_1.suggest_2
240+
241+
- length: { suggest.result: 1 }
242+
- length: { suggest.result.0.options: 2 }
243+
- match: { suggest.result.0.options.0.text: "baz" }
244+
- match: { suggest.result.0.options.1.text: "bar" }
245+
246+
---
247+
"Search by suggestion on geofield-hash on sub field should work":
248+
249+
- skip:
250+
version: " - 6.99.99"
251+
reason: "Search by suggestion with multi-fields was introduced 7.0.0"
252+
253+
- do:
254+
indices.create:
255+
index: geofield_with_completion
256+
body:
257+
mappings:
258+
test:
259+
"properties":
260+
"geofield":
261+
"type": "geo_point"
262+
"fields":
263+
"suggest_1":
264+
"type": "completion"
265+
266+
- do:
267+
index:
268+
index: geofield_with_completion
269+
type: test
270+
id: 1
271+
body:
272+
geofield: "hgjhrwysvqw7"
273+
#41.12,-72.34,12
274+
275+
- do:
276+
index:
277+
index: geofield_with_completion
278+
type: test
279+
id: 1
280+
body:
281+
geofield: "hgm4psywmkn7"
282+
#41.12,-71.34,12
283+
284+
- do:
285+
indices.refresh: {}
286+
287+
- do:
288+
search:
289+
index: geofield_with_completion
290+
body:
291+
suggest:
292+
result:
293+
prefix: "hgm"
294+
completion:
295+
field: geofield.suggest_1
296+
297+
298+
- length: { suggest.result: 1 }
299+
- length: { suggest.result.0.options: 1 }

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

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,9 @@ public void parse(ParseContext context) throws IOException {
436436
Token token = parser.currentToken();
437437
Map<String, CompletionInputMetaData> inputMap = new HashMap<>(1);
438438

439-
// ignore null values
440-
if (token == Token.VALUE_NULL) {
439+
if (context.externalValueSet()) {
440+
inputMap = getInputMapFromExternalValue(context);
441+
} else if (token == Token.VALUE_NULL) { // ignore null values
441442
return;
442443
} else if (token == Token.START_ARRAY) {
443444
while ((token = parser.nextToken()) != Token.END_ARRAY) {
@@ -471,12 +472,33 @@ public void parse(ParseContext context) throws IOException {
471472
context.doc().add(new SuggestField(fieldType().name(), input, metaData.weight));
472473
}
473474
}
475+
474476
List<IndexableField> fields = new ArrayList<>(1);
475477
createFieldNamesField(context, fields);
476478
for (IndexableField field : fields) {
477479
context.doc().add(field);
478480
}
479-
multiFields.parse(this, context);
481+
482+
for (CompletionInputMetaData metaData: inputMap.values()) {
483+
ParseContext externalValueContext = context.createExternalValueContext(metaData);
484+
multiFields.parse(this, externalValueContext);
485+
}
486+
}
487+
488+
private Map<String, CompletionInputMetaData> getInputMapFromExternalValue(ParseContext context) {
489+
Map<String, CompletionInputMetaData> inputMap;
490+
if (isExternalValueOfClass(context, CompletionInputMetaData.class)) {
491+
CompletionInputMetaData inputAndMeta = (CompletionInputMetaData) context.externalValue();
492+
inputMap = Collections.singletonMap(inputAndMeta.input, inputAndMeta);
493+
} else {
494+
String fieldName = context.externalValue().toString();
495+
inputMap = Collections.singletonMap(fieldName, new CompletionInputMetaData(fieldName, Collections.emptyMap(), 1));
496+
}
497+
return inputMap;
498+
}
499+
500+
private boolean isExternalValueOfClass(ParseContext context, Class<?> clazz) {
501+
return context.externalValue().getClass().equals(clazz);
480502
}
481503

482504
/**
@@ -487,7 +509,7 @@ public void parse(ParseContext context) throws IOException {
487509
private void parse(ParseContext parseContext, Token token, XContentParser parser, Map<String, CompletionInputMetaData> inputMap) throws IOException {
488510
String currentFieldName = null;
489511
if (token == Token.VALUE_STRING) {
490-
inputMap.put(parser.text(), new CompletionInputMetaData(Collections.<String, Set<CharSequence>>emptyMap(), 1));
512+
inputMap.put(parser.text(), new CompletionInputMetaData(parser.text(), Collections.emptyMap(), 1));
491513
} else if (token == Token.START_OBJECT) {
492514
Set<String> inputs = new HashSet<>();
493515
int weight = 1;
@@ -561,7 +583,7 @@ private void parse(ParseContext parseContext, Token token, XContentParser parser
561583
}
562584
for (String input : inputs) {
563585
if (inputMap.containsKey(input) == false || inputMap.get(input).weight < weight) {
564-
inputMap.put(input, new CompletionInputMetaData(contextsMap, weight));
586+
inputMap.put(input, new CompletionInputMetaData(input, contextsMap, weight));
565587
}
566588
}
567589
} else {
@@ -570,13 +592,20 @@ private void parse(ParseContext parseContext, Token token, XContentParser parser
570592
}
571593

572594
static class CompletionInputMetaData {
595+
public final String input;
573596
public final Map<String, Set<CharSequence>> contexts;
574597
public final int weight;
575598

576-
CompletionInputMetaData(Map<String, Set<CharSequence>> contexts, int weight) {
599+
CompletionInputMetaData(String input, Map<String, Set<CharSequence>> contexts, int weight) {
600+
this.input = input;
577601
this.contexts = contexts;
578602
this.weight = weight;
579603
}
604+
605+
@Override
606+
public String toString() {
607+
return input;
608+
}
580609
}
581610

582611
@Override

0 commit comments

Comments
 (0)