Skip to content

Commit 8bfb0f3

Browse files
serialize suggestion responses as named writeables (#30284)
Suggestion responses were previously serialized as streamables which made writing suggesters in plugins with custom suggestion response types impossible. This commit makes them serialized as named writeables and provides a facility for registering a reader for suggestion responses when registering a suggester. This also makes Suggestion responses abstract, requiring a suggester implementation to provide its own types. Suggesters which do not need anything additional to what is defined in Suggest.Suggestion should provide a minimal subclass. The existing plugin suggester integration tests are removed and replaced with an equivalent implementation as an example plugin.
1 parent 0b7fb4e commit 8bfb0f3

File tree

30 files changed

+1269
-562
lines changed

30 files changed

+1269
-562
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,11 @@
163163
import org.elasticsearch.search.aggregations.pipeline.derivative.ParsedDerivative;
164164
import org.elasticsearch.search.suggest.Suggest;
165165
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
166+
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
166167
import org.elasticsearch.search.suggest.phrase.PhraseSuggestion;
168+
import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder;
167169
import org.elasticsearch.search.suggest.term.TermSuggestion;
170+
import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
168171

169172
import java.io.Closeable;
170173
import java.io.IOException;
@@ -1141,11 +1144,11 @@ static List<NamedXContentRegistry.Entry> getDefaultNamedXContents() {
11411144
List<NamedXContentRegistry.Entry> entries = map.entrySet().stream()
11421145
.map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue()))
11431146
.collect(Collectors.toList());
1144-
entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(TermSuggestion.NAME),
1147+
entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(TermSuggestionBuilder.SUGGESTION_NAME),
11451148
(parser, context) -> TermSuggestion.fromXContent(parser, (String)context)));
1146-
entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(PhraseSuggestion.NAME),
1149+
entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(PhraseSuggestionBuilder.SUGGESTION_NAME),
11471150
(parser, context) -> PhraseSuggestion.fromXContent(parser, (String)context)));
1148-
entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(CompletionSuggestion.NAME),
1151+
entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(CompletionSuggestionBuilder.SUGGESTION_NAME),
11491152
(parser, context) -> CompletionSuggestion.fromXContent(parser, (String)context)));
11501153
return entries;
11511154
}

docs/reference/release-notes/7.0.0-alpha1.asciidoc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,10 @@ Aggregations::
2121
* The Percentiles and PercentileRanks aggregations now return `null` in the REST response,
2222
instead of `NaN`. This makes it consistent with the rest of the aggregations. Note:
2323
this only applies to the REST response, the java objects continue to return `NaN` (also
24-
consistent with other aggregations)
24+
consistent with other aggregations)
25+
26+
Suggesters::
27+
* Plugins that register suggesters can now define their own types of suggestions and must
28+
explicitly indicate the type of suggestion that they produce. Existing plugins will
29+
require changes to their plugin registration. See the `custom-suggester` example
30+
plugin {pull}30284[#30284]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
apply plugin: 'elasticsearch.esplugin'
21+
22+
esplugin {
23+
name 'custom-suggester'
24+
description 'An example plugin showing how to write and register a custom suggester'
25+
classname 'org.elasticsearch.example.customsuggester.CustomSuggesterPlugin'
26+
}
27+
28+
integTestCluster {
29+
numNodes = 2
30+
}
31+
32+
// this plugin has no unit tests, only rest tests
33+
tasks.test.enabled = false
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.example.customsuggester;
21+
22+
import org.apache.lucene.search.IndexSearcher;
23+
import org.apache.lucene.util.CharsRefBuilder;
24+
import org.elasticsearch.common.text.Text;
25+
import org.elasticsearch.search.suggest.Suggest;
26+
import org.elasticsearch.search.suggest.Suggester;
27+
28+
import java.util.Locale;
29+
30+
public class CustomSuggester extends Suggester<CustomSuggestionContext> {
31+
32+
// This is a pretty dumb implementation which returns the original text + fieldName + custom config option + 12 or 123
33+
@Override
34+
public Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> innerExecute(
35+
String name,
36+
CustomSuggestionContext suggestion,
37+
IndexSearcher searcher,
38+
CharsRefBuilder spare) {
39+
40+
// Get the suggestion context
41+
String text = suggestion.getText().utf8ToString();
42+
43+
// create two suggestions with 12 and 123 appended
44+
CustomSuggestion response = new CustomSuggestion(name, suggestion.getSize(), "suggestion-dummy-value");
45+
46+
CustomSuggestion.Entry entry = new CustomSuggestion.Entry(new Text(text), 0, text.length(), "entry-dummy-value");
47+
48+
String firstOption =
49+
String.format(Locale.ROOT, "%s-%s-%s-%s", text, suggestion.getField(), suggestion.options.get("suffix"), "12");
50+
CustomSuggestion.Entry.Option option12 = new CustomSuggestion.Entry.Option(new Text(firstOption), 0.9f, "option-dummy-value-1");
51+
entry.addOption(option12);
52+
53+
String secondOption =
54+
String.format(Locale.ROOT, "%s-%s-%s-%s", text, suggestion.getField(), suggestion.options.get("suffix"), "123");
55+
CustomSuggestion.Entry.Option option123 = new CustomSuggestion.Entry.Option(new Text(secondOption), 0.8f, "option-dummy-value-2");
56+
entry.addOption(option123);
57+
58+
response.addTerm(entry);
59+
60+
return response;
61+
}
62+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.example.customsuggester;
21+
22+
import org.elasticsearch.plugins.Plugin;
23+
import org.elasticsearch.plugins.SearchPlugin;
24+
25+
import java.util.Collections;
26+
import java.util.List;
27+
28+
public class CustomSuggesterPlugin extends Plugin implements SearchPlugin {
29+
@Override
30+
public List<SearchPlugin.SuggesterSpec<?>> getSuggesters() {
31+
return Collections.singletonList(
32+
new SearchPlugin.SuggesterSpec<>(
33+
CustomSuggestionBuilder.SUGGESTION_NAME,
34+
CustomSuggestionBuilder::new,
35+
CustomSuggestionBuilder::fromXContent,
36+
CustomSuggestion::new
37+
)
38+
);
39+
}
40+
}

0 commit comments

Comments
 (0)