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 411aab94c..bf5c941dc 100644 --- a/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java @@ -15,7 +15,12 @@ */ package org.springframework.data.elasticsearch.annotations; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * @author Rizwan Idrees @@ -46,4 +51,7 @@ public @interface Field { String indexAnalyzer() default ""; String[] ignoreFields() default {}; + + boolean includeInParent() default false; + } 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 0f5a9cb55..9eb8b5212 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -79,13 +79,13 @@ class MappingBuilder { // Properties XContentBuilder xContentBuilder = mapping.startObject(FIELD_PROPERTIES); - mapEntity(xContentBuilder, clazz, true, idFieldName, EMPTY, false, FieldType.Auto); + mapEntity(xContentBuilder, clazz, true, idFieldName, EMPTY, false, FieldType.Auto,null); return xContentBuilder.endObject().endObject().endObject(); } private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, boolean isRootObject, String idFieldName, - String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType) throws IOException { + String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType, Field fieldAnnotation) throws IOException { java.lang.reflect.Field[] fields = retrieveFields(clazz); @@ -94,7 +94,12 @@ class MappingBuilder { if (nestedOrObjectField) { type = fieldType.toString().toLowerCase(); } - xContentBuilder.startObject(nestedObjectFieldName).field(FIELD_TYPE, type).startObject(FIELD_PROPERTIES); + XContentBuilder t = xContentBuilder.startObject(nestedObjectFieldName).field(FIELD_TYPE, type); + + if (nestedOrObjectField && FieldType.Nested == fieldType && fieldAnnotation.includeInParent()) { + t.field("include_in_parent", fieldAnnotation.includeInParent()); + } + t.startObject(FIELD_PROPERTIES); } for (java.lang.reflect.Field field : fields) { @@ -112,7 +117,7 @@ class MappingBuilder { continue; } boolean nestedOrObject = isNestedOrObjectField(field); - mapEntity(xContentBuilder, getFieldType(field), false, EMPTY, field.getName(), nestedOrObject, singleField.type()); + mapEntity(xContentBuilder, getFieldType(field), false, EMPTY, field.getName(), nestedOrObject, singleField.type(),field.getAnnotation(Field.class)); if (nestedOrObject) { continue; } diff --git a/src/test/java/org/springframework/data/elasticsearch/NestedObjectTests.java b/src/test/java/org/springframework/data/elasticsearch/NestedObjectTests.java index a8b9900f1..d9c02e1f9 100644 --- a/src/test/java/org/springframework/data/elasticsearch/NestedObjectTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/NestedObjectTests.java @@ -15,13 +15,20 @@ */ package org.springframework.data.elasticsearch; -import static org.apache.commons.lang.RandomStringUtils.*; -import static org.elasticsearch.index.query.QueryBuilders.*; +import static org.apache.commons.lang.RandomStringUtils.randomNumeric; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; +import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; @@ -35,7 +42,12 @@ import org.springframework.data.elasticsearch.core.query.GetQuery; import org.springframework.data.elasticsearch.core.query.IndexQuery; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.SearchQuery; -import org.springframework.data.elasticsearch.entities.*; +import org.springframework.data.elasticsearch.entities.Author; +import org.springframework.data.elasticsearch.entities.Book; +import org.springframework.data.elasticsearch.entities.Car; +import org.springframework.data.elasticsearch.entities.GirlFriend; +import org.springframework.data.elasticsearch.entities.Person; +import org.springframework.data.elasticsearch.entities.PersonMultipleLevelNested; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -71,17 +83,17 @@ public class NestedObjectTests { @Test public void shouldIndexInitialLevelNestedObject() { - List cars = new ArrayList(); + final List cars = new ArrayList(); - Car saturn = new Car(); + final Car saturn = new Car(); saturn.setName("Saturn"); saturn.setModel("SL"); - Car subaru = new Car(); + final Car subaru = new Car(); subaru.setName("Subaru"); subaru.setModel("Imprezza"); - Car ford = new Car(); + final Car ford = new Car(); ford.setName("Ford"); ford.setModel("Focus"); @@ -89,26 +101,26 @@ public class NestedObjectTests { cars.add(subaru); cars.add(ford); - Person foo = new Person(); + final Person foo = new Person(); foo.setName("Foo"); foo.setId("1"); foo.setCar(cars); - Car car = new Car(); + final Car car = new Car(); car.setName("Saturn"); car.setModel("Imprezza"); - Person bar = new Person(); + final Person bar = new Person(); bar.setId("2"); bar.setName("Bar"); bar.setCar(Arrays.asList(car)); - List indexQueries = new ArrayList(); - IndexQuery indexQuery1 = new IndexQuery(); + final List indexQueries = new ArrayList(); + final IndexQuery indexQuery1 = new IndexQuery(); indexQuery1.setId(foo.getId()); indexQuery1.setObject(foo); - IndexQuery indexQuery2 = new IndexQuery(); + final IndexQuery indexQuery2 = new IndexQuery(); indexQuery2.setId(bar.getId()); indexQuery2.setObject(bar); @@ -119,10 +131,10 @@ public class NestedObjectTests { elasticsearchTemplate.bulkIndex(indexQueries); elasticsearchTemplate.refresh(Person.class, true); - QueryBuilder builder = nestedQuery("car", boolQuery().must(termQuery("car.name", "saturn")).must(termQuery("car.model", "imprezza"))); + final QueryBuilder builder = nestedQuery("car", boolQuery().must(termQuery("car.name", "saturn")).must(termQuery("car.model", "imprezza"))); - SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build(); - List persons = elasticsearchTemplate.queryForList(searchQuery, Person.class); + final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build(); + final List persons = elasticsearchTemplate.queryForList(searchQuery, Person.class); assertThat(persons.size(), is(1)); } @@ -130,7 +142,7 @@ public class NestedObjectTests { @Test public void shouldIndexMultipleLevelNestedObject() { //given - List indexQueries = createPerson(); + final List indexQueries = createPerson(); //when elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class); @@ -138,16 +150,36 @@ public class NestedObjectTests { elasticsearchTemplate.refresh(PersonMultipleLevelNested.class, true); //then - GetQuery getQuery = new GetQuery(); + final GetQuery getQuery = new GetQuery(); getQuery.setId("1"); - PersonMultipleLevelNested personIndexed = elasticsearchTemplate.queryForObject(getQuery, PersonMultipleLevelNested.class); + final PersonMultipleLevelNested personIndexed = elasticsearchTemplate.queryForObject(getQuery, PersonMultipleLevelNested.class); assertThat(personIndexed, is(notNullValue())); } + @Test + public void shouldIndexMultipleLevelNestedObjectWithIncludeInParent() { + //given + final List indexQueries = createPerson(); + + //when + elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class); + elasticsearchTemplate.bulkIndex(indexQueries); + // then + + final Map mapping = elasticsearchTemplate.getMapping(PersonMultipleLevelNested.class); + + assertThat(mapping,is(notNullValue())); + final Map propertyMap = (Map) mapping.get("properties"); + assertThat(propertyMap,is(notNullValue())); + final Map bestCarsAttributes = (Map) propertyMap.get("bestCars"); + assertThat(bestCarsAttributes.get("include_in_parent"),is(notNullValue())); + } + + @Test public void shouldSearchUsingNestedQueryOnMultipleLevelNestedObject() { //given - List indexQueries = createPerson(); + final List indexQueries = createPerson(); //when elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class); @@ -155,15 +187,15 @@ public class NestedObjectTests { elasticsearchTemplate.refresh(PersonMultipleLevelNested.class, true); //then - BoolQueryBuilder builder = boolQuery(); + final BoolQueryBuilder builder = boolQuery(); builder.must(nestedQuery("girlFriends", termQuery("girlFriends.type", "temp"))) - .must(nestedQuery("girlFriends.cars", termQuery("girlFriends.cars.name", "Ford".toLowerCase()))); + .must(nestedQuery("girlFriends.cars", termQuery("girlFriends.cars.name", "Ford".toLowerCase()))); - SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(builder) - .build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(builder) + .build(); - Page personIndexed = elasticsearchTemplate.queryForPage(searchQuery, PersonMultipleLevelNested.class); + final Page personIndexed = elasticsearchTemplate.queryForPage(searchQuery, PersonMultipleLevelNested.class); assertThat(personIndexed, is(notNullValue())); assertThat(personIndexed.getTotalElements(), is(1L)); assertThat(personIndexed.getContent().get(0).getId(), is("1")); @@ -172,55 +204,55 @@ public class NestedObjectTests { private List createPerson() { - PersonMultipleLevelNested person1 = new PersonMultipleLevelNested(); + final PersonMultipleLevelNested person1 = new PersonMultipleLevelNested(); person1.setId("1"); person1.setName("name"); - Car saturn = new Car(); + final Car saturn = new Car(); saturn.setName("Saturn"); saturn.setModel("SL"); - Car subaru = new Car(); + final Car subaru = new Car(); subaru.setName("Subaru"); subaru.setModel("Imprezza"); - Car car = new Car(); + final Car car = new Car(); car.setName("Saturn"); car.setModel("Imprezza"); - Car ford = new Car(); + final Car ford = new Car(); ford.setName("Ford"); ford.setModel("Focus"); - GirlFriend permanent = new GirlFriend(); + final GirlFriend permanent = new GirlFriend(); permanent.setName("permanent"); permanent.setType("permanent"); permanent.setCars(Arrays.asList(saturn, subaru)); - GirlFriend temp = new GirlFriend(); + final GirlFriend temp = new GirlFriend(); temp.setName("temp"); temp.setType("temp"); temp.setCars(Arrays.asList(car, ford)); person1.setGirlFriends(Arrays.asList(permanent, temp)); - IndexQuery indexQuery1 = new IndexQuery(); + final IndexQuery indexQuery1 = new IndexQuery(); indexQuery1.setId(person1.getId()); indexQuery1.setObject(person1); - PersonMultipleLevelNested person2 = new PersonMultipleLevelNested(); + final PersonMultipleLevelNested person2 = new PersonMultipleLevelNested(); person2.setId("2"); person2.setName("name"); person2.setGirlFriends(Arrays.asList(permanent)); - IndexQuery indexQuery2 = new IndexQuery(); + final IndexQuery indexQuery2 = new IndexQuery(); indexQuery2.setId(person2.getId()); indexQuery2.setObject(person2); - List indexQueries = new ArrayList(); + final List indexQueries = new ArrayList(); indexQueries.add(indexQuery1); indexQueries.add(indexQuery2); @@ -230,17 +262,17 @@ public class NestedObjectTests { @Test public void shouldSearchBooksForPersonInitialLevelNestedType() { - List cars = new ArrayList(); + final List cars = new ArrayList(); - Car saturn = new Car(); + final Car saturn = new Car(); saturn.setName("Saturn"); saturn.setModel("SL"); - Car subaru = new Car(); + final Car subaru = new Car(); subaru.setName("Subaru"); subaru.setModel("Imprezza"); - Car ford = new Car(); + final Car ford = new Car(); ford.setName("Ford"); ford.setModel("Focus"); @@ -248,43 +280,43 @@ public class NestedObjectTests { cars.add(subaru); cars.add(ford); - Book java = new Book(); + final Book java = new Book(); java.setId("1"); java.setName("java"); - Author javaAuthor = new Author(); + final Author javaAuthor = new Author(); javaAuthor.setId("1"); javaAuthor.setName("javaAuthor"); java.setAuthor(javaAuthor); - Book spring = new Book(); + final Book spring = new Book(); spring.setId("2"); spring.setName("spring"); - Author springAuthor = new Author(); + final Author springAuthor = new Author(); springAuthor.setId("2"); springAuthor.setName("springAuthor"); spring.setAuthor(springAuthor); - Person foo = new Person(); + final Person foo = new Person(); foo.setName("Foo"); foo.setId("1"); foo.setCar(cars); foo.setBooks(Arrays.asList(java, spring)); - Car car = new Car(); + final Car car = new Car(); car.setName("Saturn"); car.setModel("Imprezza"); - Person bar = new Person(); + final Person bar = new Person(); bar.setId("2"); bar.setName("Bar"); bar.setCar(Arrays.asList(car)); - List indexQueries = new ArrayList(); - IndexQuery indexQuery1 = new IndexQuery(); + final List indexQueries = new ArrayList(); + final IndexQuery indexQuery1 = new IndexQuery(); indexQuery1.setId(foo.getId()); indexQuery1.setObject(foo); - IndexQuery indexQuery2 = new IndexQuery(); + final IndexQuery indexQuery2 = new IndexQuery(); indexQuery2.setId(bar.getId()); indexQuery2.setObject(bar); @@ -295,22 +327,22 @@ public class NestedObjectTests { elasticsearchTemplate.bulkIndex(indexQueries); elasticsearchTemplate.refresh(Person.class, true); - QueryBuilder builder = nestedQuery("books", boolQuery().must(termQuery("books.name", "java"))); + final QueryBuilder builder = nestedQuery("books", boolQuery().must(termQuery("books.name", "java"))); - SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build(); - List persons = elasticsearchTemplate.queryForList(searchQuery, Person.class); + final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build(); + final List persons = elasticsearchTemplate.queryForList(searchQuery, Person.class); assertThat(persons.size(), is(1)); } /* DATAES-73 - */ + */ @Test public void shouldIndexAndSearchMapAsNestedType() { //given - Book book1 = new Book(); - Book book2 = new Book(); + final Book book1 = new Book(); + final Book book2 = new Book(); book1.setId(randomNumeric(5)); book1.setName("testBook1"); @@ -318,21 +350,21 @@ public class NestedObjectTests { book2.setId(randomNumeric(5)); book2.setName("testBook2"); - Map> map1 = new HashMap>(); + final Map> map1 = new HashMap>(); map1.put(1, Arrays.asList("test1", "test2")); - Map> map2 = new HashMap>(); + final Map> map2 = new HashMap>(); map2.put(1, Arrays.asList("test3", "test4")); book1.setBuckets(map1); book2.setBuckets(map2); - List indexQueries = new ArrayList(); - IndexQuery indexQuery1 = new IndexQuery(); + final List indexQueries = new ArrayList(); + final IndexQuery indexQuery1 = new IndexQuery(); indexQuery1.setId(book1.getId()); indexQuery1.setObject(book1); - IndexQuery indexQuery2 = new IndexQuery(); + final IndexQuery indexQuery2 = new IndexQuery(); indexQuery2.setId(book2.getId()); indexQuery2.setObject(book2); @@ -342,10 +374,10 @@ public class NestedObjectTests { elasticsearchTemplate.bulkIndex(indexQueries); elasticsearchTemplate.refresh(Book.class, true); //then - SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(nestedQuery("buckets", termQuery("buckets.1", "test3"))) - .build(); - Page books = elasticsearchTemplate.queryForPage(searchQuery, Book.class); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(nestedQuery("buckets", termQuery("buckets.1", "test3"))) + .build(); + final Page books = elasticsearchTemplate.queryForPage(searchQuery, Book.class); assertThat(books.getContent().size(), is(1)); assertThat(books.getContent().get(0).getId(), is(book2.getId())); diff --git a/src/test/java/org/springframework/data/elasticsearch/entities/Person.java b/src/test/java/org/springframework/data/elasticsearch/entities/Person.java index 675b02afb..14cc0dc2b 100644 --- a/src/test/java/org/springframework/data/elasticsearch/entities/Person.java +++ b/src/test/java/org/springframework/data/elasticsearch/entities/Person.java @@ -40,7 +40,7 @@ public class Person { @Field(type = FieldType.Nested) private List car; - @Field(type = FieldType.Nested) + @Field(type = FieldType.Nested, includeInParent=true) private List books; public String getId() { diff --git a/src/test/java/org/springframework/data/elasticsearch/entities/PersonMultipleLevelNested.java b/src/test/java/org/springframework/data/elasticsearch/entities/PersonMultipleLevelNested.java index b04a878e4..076d074aa 100644 --- a/src/test/java/org/springframework/data/elasticsearch/entities/PersonMultipleLevelNested.java +++ b/src/test/java/org/springframework/data/elasticsearch/entities/PersonMultipleLevelNested.java @@ -43,6 +43,9 @@ public class PersonMultipleLevelNested { @Field(type = FieldType.Nested) private List cars; + @Field(type = FieldType.Nested, includeInParent=true) + private List bestCars; + public String getId() { return id; } @@ -74,4 +77,13 @@ public class PersonMultipleLevelNested { public void setCars(List cars) { this.cars = cars; } + + public List getBestCars() { + return bestCars; + } + + public void setBestCars(List bestCars) { + this.bestCars = bestCars; + } + }