From 8862e01c74c2d3d6fb6bb6ab55db55c8c15a28c8 Mon Sep 17 00:00:00 2001 From: Artur Konczak Date: Tue, 21 May 2013 08:23:25 +0100 Subject: [PATCH] added annotation for multfield --- .../data/elasticsearch/annotations/Field.java | 8 +-- .../elasticsearch/annotations/FieldIndex.java | 7 ++ .../elasticsearch/annotations/FieldType.java | 7 ++ .../elasticsearch/annotations/MultiField.java | 19 ++++++ .../annotations/NestedField.java | 26 +++++++ .../elasticsearch/core/MappingBuilder.java | 67 ++++++++++++------- .../elasticsearch/core/facet/FacetType.java | 2 +- .../core/query/TermFacetRequest.java | 27 ++------ .../core/query/TermFacetRequestBuilder.java | 9 +-- .../data/elasticsearch/Article.java | 18 +++-- .../elasticsearch/SampleMappingEntity.java | 60 +++++++++-------- .../core/ElasticsearchTemplateFacetTests.java | 18 ++--- 12 files changed, 168 insertions(+), 100 deletions(-) create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/FieldIndex.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/MultiField.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/NestedField.java diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java b/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java index 79887edcc..d52675067 100644 --- a/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java @@ -28,9 +28,9 @@ import java.lang.annotation.*; @Documented public @interface Field { - String type() default ""; + FieldType type() default FieldType.Auto; - String index() default ""; + FieldIndex index() default FieldIndex.analyzed; boolean store() default false; @@ -38,8 +38,4 @@ public @interface Field { String indexAnalyzer() default ""; - boolean facetable() default false; - - boolean sortable() default false; - } diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/FieldIndex.java b/src/main/java/org/springframework/data/elasticsearch/annotations/FieldIndex.java new file mode 100644 index 000000000..6031c5922 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/FieldIndex.java @@ -0,0 +1,7 @@ +package org.springframework.data.elasticsearch.annotations; + +/** + */ +public enum FieldIndex { + not_analyzed, analyzed +} diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java b/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java new file mode 100644 index 000000000..4073d78bb --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java @@ -0,0 +1,7 @@ +package org.springframework.data.elasticsearch.annotations; + +/** + */ +public enum FieldType { + String, Integer, Long, Date, Object, Auto +} diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/MultiField.java b/src/main/java/org/springframework/data/elasticsearch/annotations/MultiField.java new file mode 100644 index 000000000..fa3ccff0d --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/MultiField.java @@ -0,0 +1,19 @@ +package org.springframework.data.elasticsearch.annotations; + +import java.lang.annotation.*; + +/** + * @author Rizwan Idrees + * @author Mohsin Husen + * @author Artur Konczak + * @author Jonathan Yan + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface MultiField { + + public Field mainField(); + + public NestedField[] otherFields() default {}; +} diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/NestedField.java b/src/main/java/org/springframework/data/elasticsearch/annotations/NestedField.java new file mode 100644 index 000000000..9cd36b06a --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/NestedField.java @@ -0,0 +1,26 @@ +package org.springframework.data.elasticsearch.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface NestedField { + + String dotSuffix(); + + FieldType type(); + + FieldIndex index() default FieldIndex.analyzed; + + boolean store() default false; + + String searchAnalyzer() default ""; + + String indexAnalyzer() default ""; +} 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 eaa0c3164..362386352 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -16,7 +16,7 @@ package org.springframework.data.elasticsearch.core; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.*; import org.springframework.data.elasticsearch.core.query.FacetRequest; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.ClassTypeInformation; @@ -71,15 +71,14 @@ class MappingBuilder { if (isEntity(field)) { mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName()); } - Field fieldAnnotation = field.getAnnotation(Field.class); - if (isRootObject && fieldAnnotation != null && isIdField(field, idFieldName)) { + Field singleField = field.getAnnotation(Field.class); + MultiField multiField = field.getAnnotation(MultiField.class); + if (isRootObject && singleField != null && isIdField(field, idFieldName)) { applyDefaultIdFieldMapping(xContentBuilder, field); - } else if (fieldAnnotation != null) { - if ((fieldAnnotation.sortable() || fieldAnnotation.facetable()) && TYPE_VALUE_STRING.equals(fieldAnnotation.type())) { - addMultiFieldMapping(xContentBuilder, field, fieldAnnotation); - } else { - addSimpleFieldMapping(xContentBuilder, field, fieldAnnotation); - } + } else if (multiField != null) { + addMultiFieldMapping(xContentBuilder, field, multiField); + } else if (singleField != null) { + addSingleFieldMapping(xContentBuilder, field, singleField); } } @@ -105,15 +104,15 @@ class MappingBuilder { * @param fieldAnnotation * @throws IOException */ - private static void addSimpleFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field, + private static void addSingleFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field, Field fieldAnnotation) throws IOException { xContentBuilder.startObject(field.getName()); xContentBuilder.field(FIELD_STORE, fieldAnnotation.store()); - if (isNotBlank(fieldAnnotation.type())) { - xContentBuilder.field(FIELD_TYPE, fieldAnnotation.type()); + if (FieldType.Auto != fieldAnnotation.type()) { + xContentBuilder.field(FIELD_TYPE, fieldAnnotation.type().name().toLowerCase()); } - if (isNotBlank(fieldAnnotation.index())) { - xContentBuilder.field(FIELD_INDEX, fieldAnnotation.index()); + if (FieldIndex.not_analyzed == fieldAnnotation.index()) { + xContentBuilder.field(FIELD_INDEX, fieldAnnotation.index().name().toLowerCase()); } if (isNotBlank(fieldAnnotation.searchAnalyzer())) { xContentBuilder.field(FIELD_SEARCH_ANALYZER, fieldAnnotation.searchAnalyzer()); @@ -124,6 +123,33 @@ class MappingBuilder { xContentBuilder.endObject(); } + /** + * Apply mapping for a single nested @Field annotation + * + * @param builder + * @param field + * @param annotation + * @throws IOException + */ + private static void addNestedFieldMapping(XContentBuilder builder, java.lang.reflect.Field field, + NestedField annotation) throws IOException { + builder.startObject(field.getName() + "." + annotation.dotSuffix()); + builder.field(FIELD_STORE, annotation.store()); + if (FieldType.Auto != annotation.type()) { + builder.field(FIELD_TYPE, annotation.type().name().toLowerCase()); + } + if (FieldIndex.not_analyzed == annotation.index()) { + builder.field(FIELD_INDEX, annotation.index().name().toLowerCase()); + } + if (isNotBlank(annotation.searchAnalyzer())) { + builder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer()); + } + if (isNotBlank(annotation.indexAnalyzer())) { + builder.field(FIELD_INDEX_ANALYZER, annotation.indexAnalyzer()); + } + builder.endObject(); + } + /** * Multi field mappings for string type fields, support for sorts and facets * @@ -133,19 +159,14 @@ class MappingBuilder { * @throws IOException */ private static void addMultiFieldMapping(XContentBuilder builder, java.lang.reflect.Field field, - Field annotation) throws IOException { + MultiField annotation) throws IOException { builder.startObject(field.getName()); builder.field(FIELD_TYPE, "multi_field"); builder.startObject("fields"); //add standard field - addSimpleFieldMapping(builder, field, annotation); - //facet field - untouched, not analise, stored - if (annotation.facetable()) { - addFacetMapping(builder, field, annotation); - } - //sort field - lowercase, not analise, stored - if (annotation.sortable()) { - addSortMapping(builder, field, annotation); + addSingleFieldMapping(builder, field, annotation.mainField()); + for (NestedField nestedField : annotation.otherFields()) { + addNestedFieldMapping(builder, field, nestedField); } builder.endObject(); builder.endObject(); diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetType.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetType.java index e3c8b6642..a0d344944 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetType.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetType.java @@ -2,6 +2,6 @@ package org.springframework.data.elasticsearch.core.facet; public enum FacetType { - term, renage, histogram + term, range, histogram } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequest.java b/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequest.java index c19d7b67c..eb936a455 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequest.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequest.java @@ -19,8 +19,7 @@ import java.util.List; */ public class TermFacetRequest extends AbstractFacetRequest { - private String[] stringFields; - private String[] numberFields; + private String[] fields; private int size = 10; private TermsFacet.ComparatorType order; @@ -28,12 +27,8 @@ public class TermFacetRequest extends AbstractFacetRequest { super(name); } - public void setStringFields(String... fields) { - this.stringFields = fields; - } - - public void setNumberFields(String... fields) { - this.numberFields = fields; + public void setFields(String... fields) { + this.fields = fields; } public void setSize(int size) { @@ -57,24 +52,10 @@ public class TermFacetRequest extends AbstractFacetRequest { order = TermsFacet.ComparatorType.COUNT; } - private List convertStringFieldsToFullNames() { - List result = new ArrayList(); - if (ArrayUtils.isNotEmpty(stringFields)) { - for (String stringField : stringFields) { - result.add(stringField + "." + FIELD_UNTOUCHED); - } - } - if (ArrayUtils.isNotEmpty(numberFields)) { - Collections.addAll(result, numberFields); - } - return result; - } - @Override public FacetBuilder getFacet() { - List fields = convertStringFieldsToFullNames(); Assert.notEmpty(fields, "Please select at last one field !!!"); - TermsFacetBuilder builder = FacetBuilders.termsFacet(getName()).fields(fields.toArray(new String[fields.size()])).size(size); + TermsFacetBuilder builder = FacetBuilders.termsFacet(getName()).fields(fields).size(size); if (order != null) { builder.order(order); } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequestBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequestBuilder.java index 19b0cbbbe..a4dcbe7df 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequestBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequestBuilder.java @@ -13,13 +13,8 @@ public class TermFacetRequestBuilder { result = new TermFacetRequest(name); } - public TermFacetRequestBuilder withStringFields(String... fields) { - result.setStringFields(fields); - return this; - } - - public TermFacetRequestBuilder withNumberFields(String... fields) { - result.setNumberFields(fields); + public TermFacetRequestBuilder withFields(String... fields) { + result.setFields(fields); return this; } diff --git a/src/test/java/org/springframework/data/elasticsearch/Article.java b/src/test/java/org/springframework/data/elasticsearch/Article.java index f7b933d97..c0f217372 100644 --- a/src/test/java/org/springframework/data/elasticsearch/Article.java +++ b/src/test/java/org/springframework/data/elasticsearch/Article.java @@ -1,12 +1,16 @@ package org.springframework.data.elasticsearch; import org.springframework.data.annotation.Id; -import org.springframework.data.elasticsearch.annotations.Document; -import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.*; import java.util.ArrayList; import java.util.List; +import static org.springframework.data.elasticsearch.annotations.FieldIndex.analyzed; +import static org.springframework.data.elasticsearch.annotations.FieldIndex.not_analyzed; +import static org.springframework.data.elasticsearch.annotations.FieldType.Integer; +import static org.springframework.data.elasticsearch.annotations.FieldType.String; + /** * Simple type to test facets */ @@ -18,10 +22,16 @@ public class Article { private String title; - @Field(type = "string", facetable = true) + @MultiField( + mainField = @Field(type = String, index = analyzed), + otherFields = { + @NestedField(dotSuffix = "untouched", type = String, store = true, index = not_analyzed), + @NestedField(dotSuffix = "sort", type = String, store = true, indexAnalyzer = "keyword") + } + ) private List authors = new ArrayList(); - @Field(type = "integer", facetable = true) + @Field(type = Integer, store = true) private List publishedYears = new ArrayList(); public Article() { diff --git a/src/test/java/org/springframework/data/elasticsearch/SampleMappingEntity.java b/src/test/java/org/springframework/data/elasticsearch/SampleMappingEntity.java index f82629c84..831595321 100644 --- a/src/test/java/org/springframework/data/elasticsearch/SampleMappingEntity.java +++ b/src/test/java/org/springframework/data/elasticsearch/SampleMappingEntity.java @@ -18,6 +18,11 @@ package org.springframework.data.elasticsearch; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldIndex; +import org.springframework.data.elasticsearch.annotations.FieldType; + +import static org.springframework.data.elasticsearch.annotations.FieldIndex.*; +import static org.springframework.data.elasticsearch.annotations.FieldType.*; /** * @author Rizwan Idrees @@ -26,40 +31,41 @@ import org.springframework.data.elasticsearch.annotations.Field; @Document(indexName = "test-mapping", type = "mapping", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1") public class SampleMappingEntity { - @Id - private String id; - @Field(type = "string", index = "not_analyzed", store = true, searchAnalyzer = "standard", indexAnalyzer = "standard") - private String message; + @Id + private String id; - private NestedEntity nested; + @Field(type = String, index = not_analyzed, store = true, searchAnalyzer = "standard", indexAnalyzer = "standard") + private String message; - public String getId() { - return id; - } + private NestedEntity nested; - public void setId(String id) { - this.id = id; - } + public String getId() { + return id; + } - public String getMessage() { - return message; - } + public void setId(String id) { + this.id = id; + } - public void setMessage(String message) { - this.message = message; - } + public String getMessage() { + return message; + } - static class NestedEntity { - @Field(type = "string") - private String someField; + public void setMessage(String message) { + this.message = message; + } - public String getSomeField() { - return someField; - } + static class NestedEntity { + @Field(type = String) + private String someField; - public void setSomeField(String someField) { - this.someField = someField; - } - } + public String getSomeField() { + return someField; + } + + public void setSomeField(String someField) { + this.someField = someField; + } + } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java index 9da718a34..4b2e8a616 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java @@ -79,7 +79,7 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fauthors"; - SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFacet(new TermFacetRequestBuilder(facetName).withStringFields("authors").build()).build(); + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFacet(new TermFacetRequestBuilder(facetName).withFields("authors.untouched").build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -109,8 +109,8 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fauthors"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title","four"))) - .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().withStringFields("authors").build()).build(); + .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four"))) + .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().withFields("authors.untouched").build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -136,7 +136,7 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fauthors"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(facetName).withStringFields("authors").ascTerm().build()).build(); + .withFacet(new TermFacetRequestBuilder(facetName).withFields("authors.untouched").ascTerm().build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -167,7 +167,7 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fauthors"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(facetName).withStringFields("authors").ascCount().build()).build(); + .withFacet(new TermFacetRequestBuilder(facetName).withFields("authors.untouched").ascCount().build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -197,7 +197,7 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fyears"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(facetName).withNumberFields("publishedYears").descCount().build()).build(); + .withFacet(new TermFacetRequestBuilder(facetName).withFields("publishedYears").descCount().build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -227,7 +227,7 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fyears"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(facetName).withNumberFields("publishedYears").withStringFields("authors").ascTerm().build()).build(); + .withFacet(new TermFacetRequestBuilder(facetName).withFields("publishedYears", "authors.untouched").ascTerm().build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -274,8 +274,8 @@ public class ElasticsearchTemplateFacetTests { String numberFacetName = "fAuthors"; String stringFacetName = "fyears"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(numberFacetName).withNumberFields("publishedYears").ascTerm().build()) - .withFacet(new TermFacetRequestBuilder(stringFacetName).withStringFields("authors").ascTerm().build()) + .withFacet(new TermFacetRequestBuilder(numberFacetName).withFields("publishedYears").ascTerm().build()) + .withFacet(new TermFacetRequestBuilder(stringFacetName).withFields("authors.untouched").ascTerm().build()) .build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);