Skip to content

Commit dfa0823

Browse files
Felix Petersakonczak
authored andcommitted
DATAES-91 - completion suggester support
1 parent 0e37c5d commit dfa0823

File tree

13 files changed

+717
-14
lines changed

13 files changed

+717
-14
lines changed

pom.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@
133133
<groupId>org.codehaus.mojo</groupId>
134134
<artifactId>wagon-maven-plugin</artifactId>
135135
</plugin>
136-
<plugin>
137-
<groupId>org.asciidoctor</groupId>
138-
<artifactId>asciidoctor-maven-plugin</artifactId>
139-
</plugin>
136+
<plugin>
137+
<groupId>org.asciidoctor</groupId>
138+
<artifactId>asciidoctor-maven-plugin</artifactId>
139+
</plugin>
140140
</plugins>
141141
</build>
142142

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.annotations;
17+
18+
import java.lang.annotation.*;
19+
20+
/**
21+
* Based on the reference doc - http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters-completion.html
22+
*
23+
* @author Mewes Kochheim
24+
*/
25+
@Retention(RetentionPolicy.RUNTIME)
26+
@Target(ElementType.FIELD)
27+
@Documented
28+
public @interface CompletionField {
29+
30+
String searchAnalyzer() default "simple";
31+
32+
String indexAnalyzer() default "simple";
33+
34+
boolean payloads() default false;
35+
36+
boolean preserveSeparators() default true;
37+
38+
boolean preservePositionIncrements() default true;
39+
40+
int maxInputLength() default 50;
41+
}

src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
import org.elasticsearch.action.search.SearchRequestBuilder;
5252
import org.elasticsearch.action.search.SearchResponse;
5353
import org.elasticsearch.action.search.SearchType;
54+
import org.elasticsearch.action.suggest.SuggestRequest;
55+
import org.elasticsearch.action.suggest.SuggestRequestBuilder;
56+
import org.elasticsearch.action.suggest.SuggestResponse;
5457
import org.elasticsearch.action.update.UpdateRequestBuilder;
5558
import org.elasticsearch.action.update.UpdateResponse;
5659
import org.elasticsearch.client.Client;
@@ -60,6 +63,7 @@
6063
import org.elasticsearch.common.collect.ImmutableOpenMap;
6164
import org.elasticsearch.common.collect.MapBuilder;
6265
import org.elasticsearch.common.unit.TimeValue;
66+
import org.elasticsearch.common.xcontent.ToXContent;
6367
import org.elasticsearch.common.xcontent.XContentBuilder;
6468
import org.elasticsearch.index.query.FilterBuilder;
6569
import org.elasticsearch.index.query.QueryBuilder;
@@ -70,6 +74,7 @@
7074
import org.elasticsearch.search.highlight.HighlightBuilder;
7175
import org.elasticsearch.search.sort.SortBuilder;
7276
import org.elasticsearch.search.sort.SortOrder;
77+
import org.elasticsearch.search.suggest.SuggestBuilder;
7378
import org.slf4j.Logger;
7479
import org.slf4j.LoggerFactory;
7580
import org.springframework.beans.BeansException;
@@ -976,4 +981,14 @@ public static String readFileFromClasspath(String url) {
976981

977982
return stringBuilder.toString();
978983
}
984+
985+
public SuggestResponse suggest(SuggestBuilder.SuggestionBuilder<?> suggestion, String... indices) {
986+
SuggestRequestBuilder suggestRequestBuilder = client.prepareSuggest(indices);
987+
suggestRequestBuilder.addSuggestion(suggestion);
988+
return suggestRequestBuilder.execute().actionGet();
989+
}
990+
991+
public SuggestResponse suggest(SuggestBuilder.SuggestionBuilder<?> suggestion, Class clazz) {
992+
return suggest(suggestion, retrieveIndexNameFromPersistentEntity(clazz));
993+
}
979994
}

src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.core.GenericCollectionTypeResolver;
3030
import org.springframework.data.annotation.Transient;
3131
import org.springframework.data.elasticsearch.annotations.*;
32+
import org.springframework.data.elasticsearch.core.completion.Completion;
3233
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
3334
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
3435
import org.springframework.data.mapping.model.SimpleTypeHolder;
@@ -55,7 +56,8 @@ class MappingBuilder {
5556

5657
public static final String INDEX_VALUE_NOT_ANALYZED = "not_analyzed";
5758
public static final String TYPE_VALUE_STRING = "string";
58-
public static final String TYPE_VALUE_GEO_POINT = "geo_point";
59+
public static final String TYPE_VALUE_GEO_POINT = "geo_point";
60+
public static final String TYPE_VALUE_COMPLETION = "completion";
5961

6062
private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = new SimpleTypeHolder();
6163

@@ -94,10 +96,11 @@ private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, bool
9496
continue;
9597
}
9698

97-
boolean isGeoField = isGeoField(field);
99+
boolean isGeoField = isGeoField(field);
100+
boolean isCompletionField = isCompletionField(field);
98101

99102
Field singleField = field.getAnnotation(Field.class);
100-
if (!isGeoField && isEntity(field) && isAnnotated(field)) {
103+
if (!isGeoField && !isCompletionField && isEntity(field) && isAnnotated(field)) {
101104
if (singleField == null) {
102105
continue;
103106
}
@@ -110,9 +113,13 @@ private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, bool
110113

111114
MultiField multiField = field.getAnnotation(MultiField.class);
112115

113-
if (isGeoField) {
114-
applyGeoPointFieldMapping(xContentBuilder, field);
115-
}
116+
if (isGeoField) {
117+
applyGeoPointFieldMapping(xContentBuilder, field);
118+
}
119+
120+
if (isCompletionField) {
121+
applyCompletionFieldMapping(xContentBuilder, field);
122+
}
116123

117124
if (isRootObject && singleField != null && isIdField(field, idFieldName)) {
118125
applyDefaultIdFieldMapping(xContentBuilder, field);
@@ -153,6 +160,12 @@ private static void applyGeoPointFieldMapping(XContentBuilder xContentBuilder, j
153160
.endObject();
154161
}
155162

163+
private static void applyCompletionFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException {
164+
xContentBuilder.startObject(field.getName());
165+
xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_COMPLETION)
166+
.endObject();
167+
}
168+
156169
private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field)
157170
throws IOException {
158171
xContentBuilder.startObject(field.getName())
@@ -304,7 +317,11 @@ private static boolean isNestedOrObjectField(java.lang.reflect.Field field) {
304317
return fieldAnnotation != null && (FieldType.Nested == fieldAnnotation.type() || FieldType.Object == fieldAnnotation.type());
305318
}
306319

307-
private static boolean isGeoField(java.lang.reflect.Field field) {
308-
return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null;
309-
}
310-
}
320+
private static boolean isGeoField(java.lang.reflect.Field field) {
321+
return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null;
322+
}
323+
324+
private static boolean isCompletionField(java.lang.reflect.Field field) {
325+
return field.getType() == Completion.class;
326+
}
327+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
diff a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java (rejected hunks)
2+
@@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
3+
import org.springframework.core.GenericCollectionTypeResolver;
4+
import org.springframework.data.annotation.Transient;
5+
import org.springframework.data.elasticsearch.annotations.*;
6+
+import org.springframework.data.elasticsearch.core.completion.Completion;
7+
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
8+
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
9+
import org.springframework.data.mapping.model.SimpleTypeHolder;
10+
@@ -55,7 +56,8 @@ class MappingBuilder {
11+
12+
public static final String INDEX_VALUE_NOT_ANALYZED = "not_analyzed";
13+
public static final String TYPE_VALUE_STRING = "string";
14+
- public static final String TYPE_VALUE_GEO_POINT = "geo_point";
15+
+ public static final String TYPE_VALUE_GEO_POINT = "geo_point";
16+
+ public static final String TYPE_VALUE_COMPLETION = "completion";
17+
18+
private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = new SimpleTypeHolder();
19+
20+
@@ -94,10 +96,11 @@ class MappingBuilder {
21+
continue;
22+
}
23+
24+
- boolean isGeoField = isGeoField(field);
25+
+ boolean isGeoField = isGeoField(field);
26+
+ boolean isCompletionField = isCompletionField(field);
27+
28+
Field singleField = field.getAnnotation(Field.class);
29+
- if (!isGeoField && isEntity(field) && isAnnotated(field)) {
30+
+ if (!isGeoField && !isCompletionField && isEntity(field) && isAnnotated(field)) {
31+
if (singleField == null) {
32+
continue;
33+
}
34+
@@ -110,9 +113,13 @@ class MappingBuilder {
35+
36+
MultiField multiField = field.getAnnotation(MultiField.class);
37+
38+
- if (isGeoField) {
39+
- applyGeoPointFieldMapping(xContentBuilder, field);
40+
- }
41+
+ if (isGeoField) {
42+
+ applyGeoPointFieldMapping(xContentBuilder, field);
43+
+ }
44+
+
45+
+ if (isCompletionField) {
46+
+ applyCompletionFieldMapping(xContentBuilder, field);
47+
+ }
48+
49+
if (isRootObject && singleField != null && isIdField(field, idFieldName)) {
50+
applyDefaultIdFieldMapping(xContentBuilder, field);
51+
@@ -153,6 +160,12 @@ class MappingBuilder {
52+
.endObject();
53+
}
54+
55+
+ private static void applyCompletionFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException {
56+
+ xContentBuilder.startObject(field.getName());
57+
+ xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_COMPLETION)
58+
+ .endObject();
59+
+ }
60+
+
61+
private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field)
62+
throws IOException {
63+
xContentBuilder.startObject(field.getName())
64+
@@ -304,7 +317,11 @@ class MappingBuilder {
65+
return fieldAnnotation != null && (FieldType.Nested == fieldAnnotation.type() || FieldType.Object == fieldAnnotation.type());
66+
}
67+
68+
- private static boolean isGeoField(java.lang.reflect.Field field) {
69+
- return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null;
70+
- }
71+
+ private static boolean isGeoField(java.lang.reflect.Field field) {
72+
+ return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null;
73+
+ }
74+
+
75+
+ private static boolean isCompletionField(java.lang.reflect.Field field) {
76+
+ return field.getType() == Completion.class;
77+
+ }
78+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.springframework.data.elasticsearch.core.completion;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
5+
/**
6+
* Based on the reference doc - http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters-completion.html
7+
*
8+
* @author Mewes Kochheim
9+
*/
10+
@JsonInclude(value = JsonInclude.Include.NON_NULL)
11+
public class Completion {
12+
13+
private String[] input;
14+
private String output;
15+
private Integer weight;
16+
private Object payload;
17+
18+
private Completion() {
19+
//required by mapper to instantiate object
20+
}
21+
22+
public Completion(String[] input) {
23+
this.input = input;
24+
}
25+
26+
public String[] getInput() {
27+
return input;
28+
}
29+
30+
public void setInput(String[] input) {
31+
this.input = input;
32+
}
33+
34+
public String getOutput() {
35+
return output;
36+
}
37+
38+
public void setOutput(String output) {
39+
this.output = output;
40+
}
41+
42+
public Object getPayload() {
43+
return payload;
44+
}
45+
46+
public void setPayload(Object payload) {
47+
this.payload = payload;
48+
}
49+
50+
public Integer getWeight() {
51+
return weight;
52+
}
53+
54+
public void setWeight(Integer weight) {
55+
this.weight = weight;
56+
}
57+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.springframework.data.elasticsearch.core.completion;
2+
3+
import org.springframework.data.annotation.Id;
4+
import org.springframework.data.elasticsearch.annotations.CompletionField;
5+
import org.springframework.data.elasticsearch.annotations.Document;
6+
7+
/**
8+
* @author Mewes Kochheim
9+
*/
10+
@Document(indexName = "test-completion-index", type = "annotated-completion-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
11+
public class AnnotatedCompletionEntity {
12+
13+
@Id
14+
private String id;
15+
private String name;
16+
17+
@CompletionField(payloads = true, maxInputLength = 100)
18+
private Completion suggest;
19+
20+
private AnnotatedCompletionEntity() {
21+
}
22+
23+
public AnnotatedCompletionEntity(String id) {
24+
this.id = id;
25+
}
26+
27+
public String getId() {
28+
return id;
29+
}
30+
31+
public void setId(String id) {
32+
this.id = id;
33+
}
34+
35+
public String getName() {
36+
return name;
37+
}
38+
39+
public void setName(String name) {
40+
this.name = name;
41+
}
42+
43+
public Completion getSuggest() {
44+
return suggest;
45+
}
46+
47+
public void setSuggest(Completion suggest) {
48+
this.suggest = suggest;
49+
}
50+
}

0 commit comments

Comments
 (0)