From 0e37c5d36fb221d537439f3641e60153f79f682b Mon Sep 17 00:00:00 2001 From: Kevin Leturc Date: Mon, 22 Sep 2014 20:40:32 +0200 Subject: [PATCH] DATAES-76 - Add support for mapping generation of inherited fields --- .../data/elasticsearch/annotations/Field.java | 2 + .../elasticsearch/core/MappingBuilder.java | 20 ++++++- .../builder/SampleInheritedEntityBuilder.java | 55 +++++++++++++++++++ .../core/MappingBuilderTests.java | 48 ++++++++++++++-- .../entities/AbstractInheritedEntity.java | 52 ++++++++++++++++++ .../entities/SampleInheritedEntity.java | 42 ++++++++++++++ 6 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 src/test/java/org/springframework/data/elasticsearch/builder/SampleInheritedEntityBuilder.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/entities/AbstractInheritedEntity.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/entities/SampleInheritedEntity.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 708fa8b0d..411aab94c 100644 --- a/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java @@ -23,10 +23,12 @@ import java.lang.annotation.*; * @author Artur Konczak * @author Jonathan Yan * @author Jakub Vavrik + * @author Kevin Leturc */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @Documented +@Inherited public @interface Field { FieldType type() default FieldType.Auto; 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 6945ff1b2..ce83ee402 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -20,7 +20,9 @@ import static org.elasticsearch.common.xcontent.XContentFactory.*; import static org.springframework.util.StringUtils.*; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -37,6 +39,7 @@ import org.springframework.data.util.TypeInformation; * @author Rizwan Idrees * @author Mohsin Husen * @author Artur Konczak + * @author Kevin Leturc */ class MappingBuilder { @@ -75,7 +78,7 @@ class MappingBuilder { private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, boolean isRootObject, String idFieldName, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType) throws IOException { - java.lang.reflect.Field[] fields = clazz.getDeclaredFields(); + java.lang.reflect.Field[] fields = retrieveFields(clazz); if (!isRootObject && (isAnyPropertyAnnotatedAsField(fields) || nestedOrObjectField)) { String type = FieldType.Object.toString().toLowerCase(); @@ -125,6 +128,21 @@ class MappingBuilder { } } + private static java.lang.reflect.Field[] retrieveFields(Class clazz) { + // Create list of fields. + List fields = new ArrayList(); + + // Keep backing up the inheritance hierarchy. + Class targetClass = clazz; + do { + fields.addAll(Arrays.asList(targetClass.getDeclaredFields())); + targetClass = targetClass.getSuperclass(); + } + while(targetClass != null && targetClass != Object.class); + + return fields.toArray(new java.lang.reflect.Field[fields.size()]); + } + private static boolean isAnnotated(java.lang.reflect.Field field) { return field.getAnnotation(Field.class) != null || field.getAnnotation(MultiField.class) != null || field.getAnnotation(GeoPointField.class) != null; } diff --git a/src/test/java/org/springframework/data/elasticsearch/builder/SampleInheritedEntityBuilder.java b/src/test/java/org/springframework/data/elasticsearch/builder/SampleInheritedEntityBuilder.java new file mode 100644 index 000000000..e36672ffc --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/builder/SampleInheritedEntityBuilder.java @@ -0,0 +1,55 @@ +/* + * Copyright 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.builder; + +import java.util.Date; + +import org.springframework.data.elasticsearch.core.query.IndexQuery; +import org.springframework.data.elasticsearch.entities.SampleInheritedEntity; + +/** + * @author Kevin Leturc + */ +public class SampleInheritedEntityBuilder { + + private SampleInheritedEntity result; + + public SampleInheritedEntityBuilder(String id) { + result = new SampleInheritedEntity(); + result.setId(id); + } + + public SampleInheritedEntityBuilder createdDate(Date createdDate) { + result.setCreatedDate(createdDate); + return this; + } + + public SampleInheritedEntityBuilder message(String message) { + result.setMessage(message); + return this; + } + + public SampleInheritedEntity 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/MappingBuilderTests.java b/src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java index e10cb7ec7..6dfc845fc 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java @@ -22,19 +22,18 @@ import static org.junit.Assert.*; import java.io.IOException; import java.math.BigDecimal; +import java.util.Date; import java.util.List; import org.elasticsearch.common.xcontent.XContentBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.elasticsearch.builder.SampleInheritedEntityBuilder; import org.springframework.data.elasticsearch.builder.StockPriceBuilder; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.SearchQuery; -import org.springframework.data.elasticsearch.entities.MinimalEntity; -import org.springframework.data.elasticsearch.entities.SampleTransientEntity; -import org.springframework.data.elasticsearch.entities.SimpleRecursiveEntity; -import org.springframework.data.elasticsearch.entities.StockPrice; +import org.springframework.data.elasticsearch.entities.*; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -42,6 +41,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @author Stuart Stevenson * @author Jakub Vavrik * @author Mohsin Husen + * @author Keivn Leturc */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:elasticsearch-template-test.xml") @@ -107,4 +107,44 @@ public class MappingBuilderTests { XContentBuilder xContentBuilder = MappingBuilder.buildMapping(MinimalEntity.class, "mapping", "id", "parentType"); assertThat(xContentBuilder.string(), is(expected)); } + + /* + * DATAES-76 + */ + @Test + public void shouldBuildMappingWithSuperclass() throws IOException { + final String expected = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\"" + + "type\":\"string\",\"index\":\"not_analyzed\",\"search_analyzer\":\"standard\"," + + "\"index_analyzer\":\"standard\"},\"createdDate\":{\"store\":false," + + "\"type\":\"date\",\"index\":\"not_analyzed\"}}}}"; + + XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleInheritedEntity.class, "mapping", "id", null); + assertThat(xContentBuilder.string(), is(expected)); + } + + /* + * DATAES-76 + */ + @Test + public void shouldAddSampleInheritedEntityDocumentToIndex() throws IOException { + //Given + + //When + elasticsearchTemplate.deleteIndex(SampleInheritedEntity.class); + elasticsearchTemplate.createIndex(SampleInheritedEntity.class); + elasticsearchTemplate.putMapping(SampleInheritedEntity.class); + Date createdDate = new Date(); + String message = "msg"; + String id = "abc"; + elasticsearchTemplate.index(new SampleInheritedEntityBuilder(id).createdDate(createdDate).message(message).buildIndex()); + elasticsearchTemplate.refresh(SampleInheritedEntity.class, true); + + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build(); + List result = elasticsearchTemplate.queryForList(searchQuery, SampleInheritedEntity.class); + //Then + assertThat(result.size(), is(1)); + SampleInheritedEntity entry = result.get(0); + assertThat(entry.getCreatedDate(), is(createdDate)); + assertThat(entry.getMessage(), is(message)); + } } diff --git a/src/test/java/org/springframework/data/elasticsearch/entities/AbstractInheritedEntity.java b/src/test/java/org/springframework/data/elasticsearch/entities/AbstractInheritedEntity.java new file mode 100644 index 000000000..c5ee1b5f0 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/entities/AbstractInheritedEntity.java @@ -0,0 +1,52 @@ +/* + * Copyright 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.entities; + +import java.util.Date; + +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldIndex; +import org.springframework.data.elasticsearch.annotations.FieldType; + +/** + * @author Kevin Letur + */ +public class AbstractInheritedEntity { + + @Id + private String id; + + @Field(type = FieldType.Date, index = FieldIndex.not_analyzed) + private Date createdDate; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Date getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Date createdDate) { + this.createdDate = createdDate; + } + +} diff --git a/src/test/java/org/springframework/data/elasticsearch/entities/SampleInheritedEntity.java b/src/test/java/org/springframework/data/elasticsearch/entities/SampleInheritedEntity.java new file mode 100644 index 000000000..16685a6f3 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/entities/SampleInheritedEntity.java @@ -0,0 +1,42 @@ +/* + * Copyright 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.entities; + +import static org.springframework.data.elasticsearch.annotations.FieldIndex.*; +import static org.springframework.data.elasticsearch.annotations.FieldType.String; + +import org.springframework.data.elasticsearch.annotations.Document; +import org.springframework.data.elasticsearch.annotations.Field; + +/** + * @author Kevin Leturc + */ +@Document(indexName = "test-inherited-mapping", type = "mapping", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1") +public class SampleInheritedEntity extends AbstractInheritedEntity { + + @Field(type = String, index = not_analyzed, store = true, searchAnalyzer = "standard", indexAnalyzer = "standard") + private String message; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + +}