From dfa0823c9a45f10d8ce2d838da5c98458abfa0c9 Mon Sep 17 00:00:00 2001 From: Felix Peters Date: Tue, 11 Nov 2014 16:46:57 +0100 Subject: [PATCH] DATAES-91 - completion suggester support --- pom.xml | 8 +- .../annotations/CompletionField.java | 41 ++++++ .../core/ElasticsearchTemplate.java | 15 ++ .../elasticsearch/core/MappingBuilder.java | 37 +++-- .../core/MappingBuilder.java.rej | 78 +++++++++++ .../core/completion/Completion.java | 57 ++++++++ .../completion/AnnotatedCompletionEntity.java | 50 +++++++ .../AnnotatedCompletionEntityBuilder.java | 70 ++++++++++ .../completion/CompletionAnnotatedEntity.java | 52 +++++++ .../core/completion/CompletionEntity.java | 48 +++++++ .../CompletionEntityAnnotatedBuilder.java | 75 ++++++++++ .../completion/CompletionEntityBuilder.java | 68 +++++++++ .../ElasticsearchTemplateCompletionTests.java | 132 ++++++++++++++++++ 13 files changed, 717 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/CompletionField.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java.rej create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/completion/Completion.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/completion/AnnotatedCompletionEntity.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/completion/AnnotatedCompletionEntityBuilder.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionAnnotatedEntity.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntity.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntityAnnotatedBuilder.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntityBuilder.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/completion/ElasticsearchTemplateCompletionTests.java diff --git a/pom.xml b/pom.xml index 85090a338..5b728179a 100644 --- a/pom.xml +++ b/pom.xml @@ -133,10 +133,10 @@ org.codehaus.mojo wagon-maven-plugin - - org.asciidoctor - asciidoctor-maven-plugin - + + org.asciidoctor + asciidoctor-maven-plugin + diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/CompletionField.java b/src/main/java/org/springframework/data/elasticsearch/annotations/CompletionField.java new file mode 100644 index 000000000..ca2ca2f6e --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/CompletionField.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.elasticsearch.annotations; + +import java.lang.annotation.*; + +/** + * Based on the reference doc - http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters-completion.html + * + * @author Mewes Kochheim + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface CompletionField { + + String searchAnalyzer() default "simple"; + + String indexAnalyzer() default "simple"; + + boolean payloads() default false; + + boolean preserveSeparators() default true; + + boolean preservePositionIncrements() default true; + + int maxInputLength() default 50; +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index 0d6d91810..4638edaa5 100755 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -51,6 +51,9 @@ import org.elasticsearch.action.mlt.MoreLikeThisRequestBuilder; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; +import org.elasticsearch.action.suggest.SuggestRequest; +import org.elasticsearch.action.suggest.SuggestRequestBuilder; +import org.elasticsearch.action.suggest.SuggestResponse; import org.elasticsearch.action.update.UpdateRequestBuilder; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.Client; @@ -60,6 +63,7 @@ import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.QueryBuilder; @@ -70,6 +74,7 @@ import org.elasticsearch.search.facet.FacetBuilder; import org.elasticsearch.search.highlight.HighlightBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.search.suggest.SuggestBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; @@ -976,4 +981,14 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati return stringBuilder.toString(); } + + public SuggestResponse suggest(SuggestBuilder.SuggestionBuilder suggestion, String... indices) { + SuggestRequestBuilder suggestRequestBuilder = client.prepareSuggest(indices); + suggestRequestBuilder.addSuggestion(suggestion); + return suggestRequestBuilder.execute().actionGet(); + } + + public SuggestResponse suggest(SuggestBuilder.SuggestionBuilder suggestion, Class clazz) { + return suggest(suggestion, retrieveIndexNameFromPersistentEntity(clazz)); + } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java index ce83ee402..78f14187e 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.data.annotation.Transient; import org.springframework.data.elasticsearch.annotations.*; +import org.springframework.data.elasticsearch.core.completion.Completion; import org.springframework.data.elasticsearch.core.facet.FacetRequest; import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.data.mapping.model.SimpleTypeHolder; @@ -55,7 +56,8 @@ class MappingBuilder { public static final String INDEX_VALUE_NOT_ANALYZED = "not_analyzed"; public static final String TYPE_VALUE_STRING = "string"; - public static final String TYPE_VALUE_GEO_POINT = "geo_point"; + public static final String TYPE_VALUE_GEO_POINT = "geo_point"; + public static final String TYPE_VALUE_COMPLETION = "completion"; private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = new SimpleTypeHolder(); @@ -94,10 +96,11 @@ class MappingBuilder { continue; } - boolean isGeoField = isGeoField(field); + boolean isGeoField = isGeoField(field); + boolean isCompletionField = isCompletionField(field); Field singleField = field.getAnnotation(Field.class); - if (!isGeoField && isEntity(field) && isAnnotated(field)) { + if (!isGeoField && !isCompletionField && isEntity(field) && isAnnotated(field)) { if (singleField == null) { continue; } @@ -110,9 +113,13 @@ class MappingBuilder { MultiField multiField = field.getAnnotation(MultiField.class); - if (isGeoField) { - applyGeoPointFieldMapping(xContentBuilder, field); - } + if (isGeoField) { + applyGeoPointFieldMapping(xContentBuilder, field); + } + + if (isCompletionField) { + applyCompletionFieldMapping(xContentBuilder, field); + } if (isRootObject && singleField != null && isIdField(field, idFieldName)) { applyDefaultIdFieldMapping(xContentBuilder, field); @@ -153,6 +160,12 @@ class MappingBuilder { .endObject(); } + private static void applyCompletionFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException { + xContentBuilder.startObject(field.getName()); + xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_COMPLETION) + .endObject(); + } + private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException { xContentBuilder.startObject(field.getName()) @@ -304,7 +317,11 @@ class MappingBuilder { return fieldAnnotation != null && (FieldType.Nested == fieldAnnotation.type() || FieldType.Object == fieldAnnotation.type()); } - private static boolean isGeoField(java.lang.reflect.Field field) { - return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null; - } -} + private static boolean isGeoField(java.lang.reflect.Field field) { + return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null; + } + + private static boolean isCompletionField(java.lang.reflect.Field field) { + return field.getType() == Completion.class; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java.rej b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java.rej new file mode 100644 index 000000000..23019c89c --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java.rej @@ -0,0 +1,78 @@ +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) +@@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; + import org.springframework.core.GenericCollectionTypeResolver; + import org.springframework.data.annotation.Transient; + import org.springframework.data.elasticsearch.annotations.*; ++import org.springframework.data.elasticsearch.core.completion.Completion; + import org.springframework.data.elasticsearch.core.facet.FacetRequest; + import org.springframework.data.elasticsearch.core.geo.GeoPoint; + import org.springframework.data.mapping.model.SimpleTypeHolder; +@@ -55,7 +56,8 @@ class MappingBuilder { + + public static final String INDEX_VALUE_NOT_ANALYZED = "not_analyzed"; + public static final String TYPE_VALUE_STRING = "string"; +- public static final String TYPE_VALUE_GEO_POINT = "geo_point"; ++ public static final String TYPE_VALUE_GEO_POINT = "geo_point"; ++ public static final String TYPE_VALUE_COMPLETION = "completion"; + + private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = new SimpleTypeHolder(); + +@@ -94,10 +96,11 @@ class MappingBuilder { + continue; + } + +- boolean isGeoField = isGeoField(field); ++ boolean isGeoField = isGeoField(field); ++ boolean isCompletionField = isCompletionField(field); + + Field singleField = field.getAnnotation(Field.class); +- if (!isGeoField && isEntity(field) && isAnnotated(field)) { ++ if (!isGeoField && !isCompletionField && isEntity(field) && isAnnotated(field)) { + if (singleField == null) { + continue; + } +@@ -110,9 +113,13 @@ class MappingBuilder { + + MultiField multiField = field.getAnnotation(MultiField.class); + +- if (isGeoField) { +- applyGeoPointFieldMapping(xContentBuilder, field); +- } ++ if (isGeoField) { ++ applyGeoPointFieldMapping(xContentBuilder, field); ++ } ++ ++ if (isCompletionField) { ++ applyCompletionFieldMapping(xContentBuilder, field); ++ } + + if (isRootObject && singleField != null && isIdField(field, idFieldName)) { + applyDefaultIdFieldMapping(xContentBuilder, field); +@@ -153,6 +160,12 @@ class MappingBuilder { + .endObject(); + } + ++ private static void applyCompletionFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException { ++ xContentBuilder.startObject(field.getName()); ++ xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_COMPLETION) ++ .endObject(); ++ } ++ + private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) + throws IOException { + xContentBuilder.startObject(field.getName()) +@@ -304,7 +317,11 @@ class MappingBuilder { + return fieldAnnotation != null && (FieldType.Nested == fieldAnnotation.type() || FieldType.Object == fieldAnnotation.type()); + } + +- private static boolean isGeoField(java.lang.reflect.Field field) { +- return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null; +- } ++ private static boolean isGeoField(java.lang.reflect.Field field) { ++ return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null; ++ } ++ ++ private static boolean isCompletionField(java.lang.reflect.Field field) { ++ return field.getType() == Completion.class; ++ } + } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/completion/Completion.java b/src/main/java/org/springframework/data/elasticsearch/core/completion/Completion.java new file mode 100644 index 000000000..a1d86d17c --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/completion/Completion.java @@ -0,0 +1,57 @@ +package org.springframework.data.elasticsearch.core.completion; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Based on the reference doc - http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters-completion.html + * + * @author Mewes Kochheim + */ +@JsonInclude(value = JsonInclude.Include.NON_NULL) +public class Completion { + + private String[] input; + private String output; + private Integer weight; + private Object payload; + + private Completion() { + //required by mapper to instantiate object + } + + public Completion(String[] input) { + this.input = input; + } + + public String[] getInput() { + return input; + } + + public void setInput(String[] input) { + this.input = input; + } + + public String getOutput() { + return output; + } + + public void setOutput(String output) { + this.output = output; + } + + public Object getPayload() { + return payload; + } + + public void setPayload(Object payload) { + this.payload = payload; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/completion/AnnotatedCompletionEntity.java b/src/test/java/org/springframework/data/elasticsearch/core/completion/AnnotatedCompletionEntity.java new file mode 100644 index 000000000..4d6a9c38e --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/completion/AnnotatedCompletionEntity.java @@ -0,0 +1,50 @@ +package org.springframework.data.elasticsearch.core.completion; + +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.CompletionField; +import org.springframework.data.elasticsearch.annotations.Document; + +/** + * @author Mewes Kochheim + */ +@Document(indexName = "test-completion-index", type = "annotated-completion-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1") +public class AnnotatedCompletionEntity { + + @Id + private String id; + private String name; + + @CompletionField(payloads = true, maxInputLength = 100) + private Completion suggest; + + private AnnotatedCompletionEntity() { + } + + public AnnotatedCompletionEntity(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Completion getSuggest() { + return suggest; + } + + public void setSuggest(Completion suggest) { + this.suggest = suggest; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/completion/AnnotatedCompletionEntityBuilder.java b/src/test/java/org/springframework/data/elasticsearch/core/completion/AnnotatedCompletionEntityBuilder.java new file mode 100644 index 000000000..497504560 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/completion/AnnotatedCompletionEntityBuilder.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.elasticsearch.core.completion; + +import org.springframework.data.elasticsearch.core.query.IndexQuery; + +/** + * @author Franck Marchand + * @author Mohsin Husen + * @author Mewes Kochheim + */ +public class AnnotatedCompletionEntityBuilder { + + private CompletionEntity result; + + public AnnotatedCompletionEntityBuilder(String id) { + result = new CompletionEntity(id); + } + + public AnnotatedCompletionEntityBuilder name(String name) { + result.setName(name); + return this; + } + + public AnnotatedCompletionEntityBuilder suggest(String[] input) { + return suggest(input, null, null, null); + } + + public AnnotatedCompletionEntityBuilder suggest(String[] input, String output) { + return suggest(input, output, null, null); + } + + public AnnotatedCompletionEntityBuilder suggest(String[] input, String output, Object payload) { + return suggest(input, output, payload, null); + } + + public AnnotatedCompletionEntityBuilder suggest(String[] input, String output, Object payload, Integer weight) { + Completion suggest = new Completion(input); + suggest.setOutput(output); + suggest.setPayload(payload); + suggest.setWeight(weight); + + result.setSuggest(suggest); + return this; + } + + public CompletionEntity build() { + return result; + } + + public IndexQuery buildIndex() { + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(result.getId()); + indexQuery.setObject(result); + return indexQuery; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionAnnotatedEntity.java b/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionAnnotatedEntity.java new file mode 100644 index 000000000..de3b69ee7 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionAnnotatedEntity.java @@ -0,0 +1,52 @@ +package org.springframework.data.elasticsearch.core.completion; + +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.CompletionField; +import org.springframework.data.elasticsearch.annotations.Document; + +/** + * @author Franck Marchand + * @author Mohsin Husen + * @author Mewes Kochheim + */ +@Document(indexName = "test-completion-index", type = "completion-annotation-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1") +public class CompletionAnnotatedEntity { + + @Id + private String id; + private String name; + + @CompletionField(payloads = true) + private Completion suggest; + + private CompletionAnnotatedEntity() { + } + + public CompletionAnnotatedEntity(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Completion getSuggest() { + return suggest; + } + + public void setSuggest(Completion suggest) { + this.suggest = suggest; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntity.java b/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntity.java new file mode 100644 index 000000000..bc049b643 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntity.java @@ -0,0 +1,48 @@ +package org.springframework.data.elasticsearch.core.completion; + +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.Document; + +/** + * @author Mewes Kochheim + */ +@Document(indexName = "test-completion-index", type = "completion-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1") +public class CompletionEntity { + + @Id + private String id; + private String name; + + private Completion suggest; + + private CompletionEntity() { + } + + public CompletionEntity(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Completion getSuggest() { + return suggest; + } + + public void setSuggest(Completion suggest) { + this.suggest = suggest; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntityAnnotatedBuilder.java b/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntityAnnotatedBuilder.java new file mode 100644 index 000000000..7fa96cebc --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntityAnnotatedBuilder.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.elasticsearch.core.completion; + +import org.springframework.data.elasticsearch.core.query.IndexQuery; + +/** + * @author Franck Marchand + * @author Mohsin Husen + * @author Mewes Kochheim + */ +public class CompletionEntityAnnotatedBuilder { + + private CompletionAnnotatedEntity result; + + public CompletionEntityAnnotatedBuilder(String id) { + result = new CompletionAnnotatedEntity(id); + } + + public CompletionEntityAnnotatedBuilder name(String name) { + result.setName(name); + return this; + } + + public CompletionEntityAnnotatedBuilder suggest(String[] input) { + return suggest(input, null, null, null); + } + + public CompletionEntityAnnotatedBuilder suggest(String[] input, String output) { + return suggest(input, output, null, null); + } + + public CompletionEntityAnnotatedBuilder suggest(String[] input, String output, Object payload) { + return suggest(input, output, payload, null); + } + + public CompletionEntityAnnotatedBuilder suggest(String[] input, String output, Object payload, Integer weight) { + Completion suggest = new Completion(input); + if (output != null) { + suggest.setOutput(output); + } + if (payload != null) { + suggest.setPayload(payload); + } + if (weight != null) { + suggest.setWeight(weight); + } + result.setSuggest(suggest); + return this; + } + + public CompletionAnnotatedEntity build() { + return result; + } + + public IndexQuery buildIndex() { + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(result.getId()); + indexQuery.setObject(result); + return indexQuery; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntityBuilder.java b/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntityBuilder.java new file mode 100644 index 000000000..bebb52ed2 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/completion/CompletionEntityBuilder.java @@ -0,0 +1,68 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.elasticsearch.core.completion; + +import org.springframework.data.elasticsearch.core.query.IndexQuery; + +/** + * @author Mewes Kochheim + */ +public class CompletionEntityBuilder { + + private CompletionEntity result; + + public CompletionEntityBuilder(String id) { + result = new CompletionEntity(id); + } + + public CompletionEntityBuilder name(String name) { + result.setName(name); + return this; + } + + public CompletionEntityBuilder suggest(String[] input) { + return suggest(input, null, null, null); + } + + public CompletionEntityBuilder suggest(String[] input, String output) { + return suggest(input, output, null, null); + } + + public CompletionEntityBuilder suggest(String[] input, String output, Object payload) { + return suggest(input, output, payload, null); + } + + public CompletionEntityBuilder suggest(String[] input, String output, Object payload, Integer weight) { + Completion suggest = new Completion(input); + suggest.setOutput(output); + suggest.setPayload(payload); + suggest.setWeight(weight); + + result.setSuggest(suggest); + return this; + } + + public CompletionEntity build() { + return result; + } + + public IndexQuery buildIndex() { + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(result.getId()); + indexQuery.setObject(result); + return indexQuery; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/completion/ElasticsearchTemplateCompletionTests.java b/src/test/java/org/springframework/data/elasticsearch/core/completion/ElasticsearchTemplateCompletionTests.java new file mode 100644 index 000000000..eddd1d27a --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/completion/ElasticsearchTemplateCompletionTests.java @@ -0,0 +1,132 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.elasticsearch.core.completion; + +import org.elasticsearch.action.suggest.SuggestResponse; +import org.elasticsearch.search.suggest.completion.CompletionSuggestion; +import org.elasticsearch.search.suggest.completion.CompletionSuggestionFuzzyBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.query.IndexQuery; +import org.springframework.data.elasticsearch.entities.NonDocumentEntity; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; + +/** + * @author Rizwan Idrees + * @author Mohsin Husen + * @author Franck Marchand + * @author Artur Konczak + * @author Mewes Kochheim + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:elasticsearch-template-test.xml") +public class ElasticsearchTemplateCompletionTests { + + @Autowired + private ElasticsearchTemplate elasticsearchTemplate; + + private void loadCompletionObjectEntities() { + elasticsearchTemplate.deleteIndex(CompletionEntity.class); + elasticsearchTemplate.createIndex(CompletionEntity.class); + elasticsearchTemplate.refresh(CompletionEntity.class, true); + elasticsearchTemplate.putMapping(CompletionEntity.class); + + List indexQueries = new ArrayList(); + indexQueries.add(new CompletionEntityBuilder("1").name("Rizwan Idrees").suggest(new String[]{"Rizwan Idrees"}).buildIndex()); + indexQueries.add(new CompletionEntityBuilder("2").name("Franck Marchand").suggest(new String[]{"Franck", "Marchand"}).buildIndex()); + indexQueries.add(new CompletionEntityBuilder("3").name("Mohsin Husen").suggest(new String[]{"Mohsin", "Husen"}, "Mohsin Husen").buildIndex()); + indexQueries.add(new CompletionEntityBuilder("4").name("Artur Konczak").suggest(new String[]{"Artur", "Konczak"}, "Artur Konczak", null, 60).buildIndex()); + + elasticsearchTemplate.bulkIndex(indexQueries); + elasticsearchTemplate.refresh(CompletionEntity.class, true); + } + + private void loadAnnotatedCompletionObjectEntities() { + elasticsearchTemplate.deleteIndex(AnnotatedCompletionEntity.class); + elasticsearchTemplate.createIndex(AnnotatedCompletionEntity.class); + elasticsearchTemplate.refresh(AnnotatedCompletionEntity.class, true); + elasticsearchTemplate.putMapping(AnnotatedCompletionEntity.class); + + NonDocumentEntity nonDocumentEntity = new NonDocumentEntity(); + nonDocumentEntity.setSomeField1("foo"); + nonDocumentEntity.setSomeField2("bar"); + + List indexQueries = new ArrayList(); + indexQueries.add(new AnnotatedCompletionEntityBuilder("1").name("Franck Marchand").suggest(new String[]{"Franck", "Marchand"}).buildIndex()); + indexQueries.add(new AnnotatedCompletionEntityBuilder("2").name("Mohsin Husen").suggest(new String[]{"Mohsin", "Husen"}, "Mohsin Husen").buildIndex()); + indexQueries.add(new AnnotatedCompletionEntityBuilder("3").name("Rizwan Idrees").suggest(new String[]{"Rizwan", "Idrees"}, "Rizwan Idrees", "Payload test").buildIndex()); + indexQueries.add(new AnnotatedCompletionEntityBuilder("4").name("Artur Konczak").suggest(new String[]{"Artur", "Konczak"}, "Artur Konczak", nonDocumentEntity, 60).buildIndex()); + + elasticsearchTemplate.bulkIndex(indexQueries); + elasticsearchTemplate.refresh(AnnotatedCompletionEntity.class, true); + } + + @Test + public void shouldPutMappingForGivenEntity() throws Exception { + //given + Class entity = CompletionEntity.class; + elasticsearchTemplate.createIndex(entity); + + //when + assertThat(elasticsearchTemplate.putMapping(entity), is(true)); + } + + @Test + public void shouldFindSuggestionsForGivenCriteriaQueryUsingCompletionObjectEntity() { + //given + loadCompletionObjectEntities(); + CompletionSuggestionFuzzyBuilder completionSuggestionFuzzyBuilder = new CompletionSuggestionFuzzyBuilder("test-suggest") + .text("m") + .field("suggest"); + + //when + SuggestResponse suggestResponse = elasticsearchTemplate.suggest(completionSuggestionFuzzyBuilder, CompletionEntity.class); + CompletionSuggestion completionSuggestion = suggestResponse.getSuggest().getSuggestion("test-suggest"); + List options = completionSuggestion.getEntries().get(0).getOptions(); + + //then + assertThat(options.size(), is(2)); + assertEquals("Marchand", options.get(0).getText().string()); + } + + @Test + public void shouldFindSuggestionsForGivenCriteriaQueryUsingloadAnnotatedCompletionObjectEntity() { + //given + loadCompletionObjectEntities(); + CompletionSuggestionFuzzyBuilder completionSuggestionFuzzyBuilder = new CompletionSuggestionFuzzyBuilder("test-suggest") + .text("m") + .field("suggest"); + + //when + SuggestResponse suggestResponse = elasticsearchTemplate.suggest(completionSuggestionFuzzyBuilder, CompletionEntity.class); + CompletionSuggestion completionSuggestion = suggestResponse.getSuggest().getSuggestion("test-suggest"); + List options = completionSuggestion.getEntries().get(0).getOptions(); + + //then + assertThat(options.size(), is(2)); + assertEquals("Marchand", options.get(0).getText().string()); + } +}