From b5dfb594286a81521bc6fa3302d1f63c3c3f1dca Mon Sep 17 00:00:00 2001 From: Jakub Vavrik Date: Wed, 9 Oct 2013 18:22:31 +0200 Subject: [PATCH 1/3] Added skipping of fields that are marked using spring data @Transient annotation --- .../data/elasticsearch/core/MappingBuilder.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 77ee015ea..2b27e26e5 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -16,6 +16,7 @@ package org.springframework.data.elasticsearch.core; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.springframework.data.annotation.Transient; import org.springframework.data.elasticsearch.annotations.*; import org.springframework.data.elasticsearch.core.facet.FacetRequest; import org.springframework.data.elasticsearch.core.geo.GeoPoint; @@ -71,6 +72,11 @@ class MappingBuilder { } for (java.lang.reflect.Field field : fields) { + + if (field.isAnnotationPresent(Transient.class)) { + continue; + } + if (isEntity(field)) { mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName()); } From 680e07726fd2fe70a9a1f90c2dfd71abbf17ec1e Mon Sep 17 00:00:00 2001 From: Jakub Vavrik Date: Fri, 11 Oct 2013 13:05:13 +0200 Subject: [PATCH 2/3] Added simple fix for infinite recursion in entity mapping if class contains a field of same type as the class. In such case will skip that field. --- .../data/elasticsearch/core/MappingBuilder.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 2b27e26e5..bb77c9824 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -25,7 +25,9 @@ import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; import java.io.IOException; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import static org.apache.commons.lang.StringUtils.EMPTY; import static org.apache.commons.lang.StringUtils.isNotBlank; @@ -57,14 +59,13 @@ class MappingBuilder { static XContentBuilder buildMapping(Class clazz, String indexType, String idFieldName) throws IOException { XContentBuilder xContentBuilder = jsonBuilder().startObject().startObject(indexType).startObject(FIELD_PROPERTIES); - mapEntity(xContentBuilder, clazz, true, idFieldName, EMPTY); + mapEntity(xContentBuilder, clazz, true, idFieldName, EMPTY, new HashSet()); return xContentBuilder.endObject().endObject().endObject(); } private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, boolean isRootObject, String idFieldName, - String nestedObjectFieldName) throws IOException { - + String nestedObjectFieldName, Set ancestorClasses) throws IOException { java.lang.reflect.Field[] fields = clazz.getDeclaredFields(); if (!isRootObject && isAnyPropertyAnnotatedAsField(fields)) { @@ -78,7 +79,12 @@ class MappingBuilder { } if (isEntity(field)) { - mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName()); + if (ancestorClasses.contains(field.getType())) { + continue; + } else { + ancestorClasses.add(field.getType()); + mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName(), ancestorClasses); + } } Field singleField = field.getAnnotation(Field.class); From be9a34cedd860412e2faf371302533892d15b1fc Mon Sep 17 00:00:00 2001 From: Jakub Vavrik Date: Fri, 11 Oct 2013 13:23:03 +0200 Subject: [PATCH 3/3] Added test for infinite recursion mapping. --- .../SampleRecursiveMappingEntity.java | 75 +++++++++++++++++++ .../core/SimpleRecursiveMappingTest.java | 25 +++++++ 2 files changed, 100 insertions(+) create mode 100644 src/test/java/org/springframework/data/elasticsearch/SampleRecursiveMappingEntity.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/SimpleRecursiveMappingTest.java diff --git a/src/test/java/org/springframework/data/elasticsearch/SampleRecursiveMappingEntity.java b/src/test/java/org/springframework/data/elasticsearch/SampleRecursiveMappingEntity.java new file mode 100644 index 000000000..d2024c193 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/SampleRecursiveMappingEntity.java @@ -0,0 +1,75 @@ +/* + * 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; + +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.Document; +import org.springframework.data.elasticsearch.annotations.Field; + +import static org.springframework.data.elasticsearch.annotations.FieldIndex.not_analyzed; +import static org.springframework.data.elasticsearch.annotations.FieldType.String; + +/** + * @author Jakub Vavrik + */ +@Document(indexName = "test-recursive-mapping", type = "mapping", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1") +public class SampleRecursiveMappingEntity { + + @Id + private String id; + + @Field(type = String, index = not_analyzed, store = true, searchAnalyzer = "standard", indexAnalyzer = "standard") + private String message; + + @Field + private NestedEntity nested; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + static class NestedEntity { + @Field + private static NestedEntity someField = new NestedEntity(); + @Field + private Boolean something; + + public NestedEntity getSomeField() { + return someField; + } + + public void setSomeField(NestedEntity someField) { + this.someField = someField; + } + + public Boolean getSomething() { return something; } + + public void setSomething(Boolean something) { this.something = something; } + } + +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/SimpleRecursiveMappingTest.java b/src/test/java/org/springframework/data/elasticsearch/core/SimpleRecursiveMappingTest.java new file mode 100644 index 000000000..0ac8cd3f9 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/SimpleRecursiveMappingTest.java @@ -0,0 +1,25 @@ +package org.springframework.data.elasticsearch.core; + +import junit.framework.Assert; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.junit.Test; +import org.springframework.data.elasticsearch.SampleRecursiveMappingEntity; + +import java.io.IOException; + +/** + * Test that classes that have fields of same type do not end in infinite loop when mapping. + */ +public class SimpleRecursiveMappingTest { + + private static final String EXPECTED = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\"" + + "type\":\"string\",\"index\":\"not_analyzed\",\"search_analyzer\":\"standard\"," + + "\"index_analyzer\":\"standard\"},\"nested\":{\"type\":\"object\",\"properties\":{\"" + + "something\":{\"store\":false}}},\"nested\":{\"store\":false}}}}"; + + @Test + public void testInfiniteLoopAvoidance() throws IOException { + XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleRecursiveMappingEntity.class, "mapping", "id"); + Assert.assertEquals(EXPECTED, xContentBuilder.string()); + } +}