DATAES-132 - adding possibility to configure mapping with nested and include in parent option

This commit is contained in:
msachs 2014-10-20 21:52:54 +02:00 committed by Mohsin Husen
parent cd9ed2cde8
commit 7cf7cb9168
5 changed files with 131 additions and 74 deletions

View File

@ -15,7 +15,12 @@
*/ */
package org.springframework.data.elasticsearch.annotations; 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 * @author Rizwan Idrees
@ -46,4 +51,7 @@ public @interface Field {
String indexAnalyzer() default ""; String indexAnalyzer() default "";
String[] ignoreFields() default {}; String[] ignoreFields() default {};
boolean includeInParent() default false;
} }

View File

@ -79,13 +79,13 @@ class MappingBuilder {
// Properties // Properties
XContentBuilder xContentBuilder = mapping.startObject(FIELD_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(); return xContentBuilder.endObject().endObject().endObject();
} }
private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, boolean isRootObject, String idFieldName, 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); java.lang.reflect.Field[] fields = retrieveFields(clazz);
@ -94,7 +94,12 @@ class MappingBuilder {
if (nestedOrObjectField) { if (nestedOrObjectField) {
type = fieldType.toString().toLowerCase(); 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) { for (java.lang.reflect.Field field : fields) {
@ -112,7 +117,7 @@ class MappingBuilder {
continue; continue;
} }
boolean nestedOrObject = isNestedOrObjectField(field); 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) { if (nestedOrObject) {
continue; continue;
} }

View File

@ -15,13 +15,20 @@
*/ */
package org.springframework.data.elasticsearch; package org.springframework.data.elasticsearch;
import static org.apache.commons.lang.RandomStringUtils.*; import static org.apache.commons.lang.RandomStringUtils.randomNumeric;
import static org.elasticsearch.index.query.QueryBuilders.*; 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.CoreMatchers.is;
import static org.hamcrest.Matchers.notNullValue; 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.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder; 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.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery; 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.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ -71,17 +83,17 @@ public class NestedObjectTests {
@Test @Test
public void shouldIndexInitialLevelNestedObject() { public void shouldIndexInitialLevelNestedObject() {
List<Car> cars = new ArrayList<Car>(); final List<Car> cars = new ArrayList<Car>();
Car saturn = new Car(); final Car saturn = new Car();
saturn.setName("Saturn"); saturn.setName("Saturn");
saturn.setModel("SL"); saturn.setModel("SL");
Car subaru = new Car(); final Car subaru = new Car();
subaru.setName("Subaru"); subaru.setName("Subaru");
subaru.setModel("Imprezza"); subaru.setModel("Imprezza");
Car ford = new Car(); final Car ford = new Car();
ford.setName("Ford"); ford.setName("Ford");
ford.setModel("Focus"); ford.setModel("Focus");
@ -89,26 +101,26 @@ public class NestedObjectTests {
cars.add(subaru); cars.add(subaru);
cars.add(ford); cars.add(ford);
Person foo = new Person(); final Person foo = new Person();
foo.setName("Foo"); foo.setName("Foo");
foo.setId("1"); foo.setId("1");
foo.setCar(cars); foo.setCar(cars);
Car car = new Car(); final Car car = new Car();
car.setName("Saturn"); car.setName("Saturn");
car.setModel("Imprezza"); car.setModel("Imprezza");
Person bar = new Person(); final Person bar = new Person();
bar.setId("2"); bar.setId("2");
bar.setName("Bar"); bar.setName("Bar");
bar.setCar(Arrays.asList(car)); bar.setCar(Arrays.asList(car));
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>(); final List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
IndexQuery indexQuery1 = new IndexQuery(); final IndexQuery indexQuery1 = new IndexQuery();
indexQuery1.setId(foo.getId()); indexQuery1.setId(foo.getId());
indexQuery1.setObject(foo); indexQuery1.setObject(foo);
IndexQuery indexQuery2 = new IndexQuery(); final IndexQuery indexQuery2 = new IndexQuery();
indexQuery2.setId(bar.getId()); indexQuery2.setId(bar.getId());
indexQuery2.setObject(bar); indexQuery2.setObject(bar);
@ -119,10 +131,10 @@ public class NestedObjectTests {
elasticsearchTemplate.bulkIndex(indexQueries); elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(Person.class, true); 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(); final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class); final List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
assertThat(persons.size(), is(1)); assertThat(persons.size(), is(1));
} }
@ -130,7 +142,7 @@ public class NestedObjectTests {
@Test @Test
public void shouldIndexMultipleLevelNestedObject() { public void shouldIndexMultipleLevelNestedObject() {
//given //given
List<IndexQuery> indexQueries = createPerson(); final List<IndexQuery> indexQueries = createPerson();
//when //when
elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class); elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class);
@ -138,16 +150,36 @@ public class NestedObjectTests {
elasticsearchTemplate.refresh(PersonMultipleLevelNested.class, true); elasticsearchTemplate.refresh(PersonMultipleLevelNested.class, true);
//then //then
GetQuery getQuery = new GetQuery(); final GetQuery getQuery = new GetQuery();
getQuery.setId("1"); getQuery.setId("1");
PersonMultipleLevelNested personIndexed = elasticsearchTemplate.queryForObject(getQuery, PersonMultipleLevelNested.class); final PersonMultipleLevelNested personIndexed = elasticsearchTemplate.queryForObject(getQuery, PersonMultipleLevelNested.class);
assertThat(personIndexed, is(notNullValue())); assertThat(personIndexed, is(notNullValue()));
} }
@Test
public void shouldIndexMultipleLevelNestedObjectWithIncludeInParent() {
//given
final List<IndexQuery> 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 @Test
public void shouldSearchUsingNestedQueryOnMultipleLevelNestedObject() { public void shouldSearchUsingNestedQueryOnMultipleLevelNestedObject() {
//given //given
List<IndexQuery> indexQueries = createPerson(); final List<IndexQuery> indexQueries = createPerson();
//when //when
elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class); elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class);
@ -155,15 +187,15 @@ public class NestedObjectTests {
elasticsearchTemplate.refresh(PersonMultipleLevelNested.class, true); elasticsearchTemplate.refresh(PersonMultipleLevelNested.class, true);
//then //then
BoolQueryBuilder builder = boolQuery(); final BoolQueryBuilder builder = boolQuery();
builder.must(nestedQuery("girlFriends", termQuery("girlFriends.type", "temp"))) 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() final SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(builder) .withQuery(builder)
.build(); .build();
Page<PersonMultipleLevelNested> personIndexed = elasticsearchTemplate.queryForPage(searchQuery, PersonMultipleLevelNested.class); final Page<PersonMultipleLevelNested> personIndexed = elasticsearchTemplate.queryForPage(searchQuery, PersonMultipleLevelNested.class);
assertThat(personIndexed, is(notNullValue())); assertThat(personIndexed, is(notNullValue()));
assertThat(personIndexed.getTotalElements(), is(1L)); assertThat(personIndexed.getTotalElements(), is(1L));
assertThat(personIndexed.getContent().get(0).getId(), is("1")); assertThat(personIndexed.getContent().get(0).getId(), is("1"));
@ -172,55 +204,55 @@ public class NestedObjectTests {
private List<IndexQuery> createPerson() { private List<IndexQuery> createPerson() {
PersonMultipleLevelNested person1 = new PersonMultipleLevelNested(); final PersonMultipleLevelNested person1 = new PersonMultipleLevelNested();
person1.setId("1"); person1.setId("1");
person1.setName("name"); person1.setName("name");
Car saturn = new Car(); final Car saturn = new Car();
saturn.setName("Saturn"); saturn.setName("Saturn");
saturn.setModel("SL"); saturn.setModel("SL");
Car subaru = new Car(); final Car subaru = new Car();
subaru.setName("Subaru"); subaru.setName("Subaru");
subaru.setModel("Imprezza"); subaru.setModel("Imprezza");
Car car = new Car(); final Car car = new Car();
car.setName("Saturn"); car.setName("Saturn");
car.setModel("Imprezza"); car.setModel("Imprezza");
Car ford = new Car(); final Car ford = new Car();
ford.setName("Ford"); ford.setName("Ford");
ford.setModel("Focus"); ford.setModel("Focus");
GirlFriend permanent = new GirlFriend(); final GirlFriend permanent = new GirlFriend();
permanent.setName("permanent"); permanent.setName("permanent");
permanent.setType("permanent"); permanent.setType("permanent");
permanent.setCars(Arrays.asList(saturn, subaru)); permanent.setCars(Arrays.asList(saturn, subaru));
GirlFriend temp = new GirlFriend(); final GirlFriend temp = new GirlFriend();
temp.setName("temp"); temp.setName("temp");
temp.setType("temp"); temp.setType("temp");
temp.setCars(Arrays.asList(car, ford)); temp.setCars(Arrays.asList(car, ford));
person1.setGirlFriends(Arrays.asList(permanent, temp)); person1.setGirlFriends(Arrays.asList(permanent, temp));
IndexQuery indexQuery1 = new IndexQuery(); final IndexQuery indexQuery1 = new IndexQuery();
indexQuery1.setId(person1.getId()); indexQuery1.setId(person1.getId());
indexQuery1.setObject(person1); indexQuery1.setObject(person1);
PersonMultipleLevelNested person2 = new PersonMultipleLevelNested(); final PersonMultipleLevelNested person2 = new PersonMultipleLevelNested();
person2.setId("2"); person2.setId("2");
person2.setName("name"); person2.setName("name");
person2.setGirlFriends(Arrays.asList(permanent)); person2.setGirlFriends(Arrays.asList(permanent));
IndexQuery indexQuery2 = new IndexQuery(); final IndexQuery indexQuery2 = new IndexQuery();
indexQuery2.setId(person2.getId()); indexQuery2.setId(person2.getId());
indexQuery2.setObject(person2); indexQuery2.setObject(person2);
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>(); final List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
indexQueries.add(indexQuery1); indexQueries.add(indexQuery1);
indexQueries.add(indexQuery2); indexQueries.add(indexQuery2);
@ -230,17 +262,17 @@ public class NestedObjectTests {
@Test @Test
public void shouldSearchBooksForPersonInitialLevelNestedType() { public void shouldSearchBooksForPersonInitialLevelNestedType() {
List<Car> cars = new ArrayList<Car>(); final List<Car> cars = new ArrayList<Car>();
Car saturn = new Car(); final Car saturn = new Car();
saturn.setName("Saturn"); saturn.setName("Saturn");
saturn.setModel("SL"); saturn.setModel("SL");
Car subaru = new Car(); final Car subaru = new Car();
subaru.setName("Subaru"); subaru.setName("Subaru");
subaru.setModel("Imprezza"); subaru.setModel("Imprezza");
Car ford = new Car(); final Car ford = new Car();
ford.setName("Ford"); ford.setName("Ford");
ford.setModel("Focus"); ford.setModel("Focus");
@ -248,43 +280,43 @@ public class NestedObjectTests {
cars.add(subaru); cars.add(subaru);
cars.add(ford); cars.add(ford);
Book java = new Book(); final Book java = new Book();
java.setId("1"); java.setId("1");
java.setName("java"); java.setName("java");
Author javaAuthor = new Author(); final Author javaAuthor = new Author();
javaAuthor.setId("1"); javaAuthor.setId("1");
javaAuthor.setName("javaAuthor"); javaAuthor.setName("javaAuthor");
java.setAuthor(javaAuthor); java.setAuthor(javaAuthor);
Book spring = new Book(); final Book spring = new Book();
spring.setId("2"); spring.setId("2");
spring.setName("spring"); spring.setName("spring");
Author springAuthor = new Author(); final Author springAuthor = new Author();
springAuthor.setId("2"); springAuthor.setId("2");
springAuthor.setName("springAuthor"); springAuthor.setName("springAuthor");
spring.setAuthor(springAuthor); spring.setAuthor(springAuthor);
Person foo = new Person(); final Person foo = new Person();
foo.setName("Foo"); foo.setName("Foo");
foo.setId("1"); foo.setId("1");
foo.setCar(cars); foo.setCar(cars);
foo.setBooks(Arrays.asList(java, spring)); foo.setBooks(Arrays.asList(java, spring));
Car car = new Car(); final Car car = new Car();
car.setName("Saturn"); car.setName("Saturn");
car.setModel("Imprezza"); car.setModel("Imprezza");
Person bar = new Person(); final Person bar = new Person();
bar.setId("2"); bar.setId("2");
bar.setName("Bar"); bar.setName("Bar");
bar.setCar(Arrays.asList(car)); bar.setCar(Arrays.asList(car));
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>(); final List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
IndexQuery indexQuery1 = new IndexQuery(); final IndexQuery indexQuery1 = new IndexQuery();
indexQuery1.setId(foo.getId()); indexQuery1.setId(foo.getId());
indexQuery1.setObject(foo); indexQuery1.setObject(foo);
IndexQuery indexQuery2 = new IndexQuery(); final IndexQuery indexQuery2 = new IndexQuery();
indexQuery2.setId(bar.getId()); indexQuery2.setId(bar.getId());
indexQuery2.setObject(bar); indexQuery2.setObject(bar);
@ -295,10 +327,10 @@ public class NestedObjectTests {
elasticsearchTemplate.bulkIndex(indexQueries); elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(Person.class, true); 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(); final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class); final List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
assertThat(persons.size(), is(1)); assertThat(persons.size(), is(1));
} }
@ -309,8 +341,8 @@ public class NestedObjectTests {
@Test @Test
public void shouldIndexAndSearchMapAsNestedType() { public void shouldIndexAndSearchMapAsNestedType() {
//given //given
Book book1 = new Book(); final Book book1 = new Book();
Book book2 = new Book(); final Book book2 = new Book();
book1.setId(randomNumeric(5)); book1.setId(randomNumeric(5));
book1.setName("testBook1"); book1.setName("testBook1");
@ -318,21 +350,21 @@ public class NestedObjectTests {
book2.setId(randomNumeric(5)); book2.setId(randomNumeric(5));
book2.setName("testBook2"); book2.setName("testBook2");
Map<Integer, Collection<String>> map1 = new HashMap<Integer, Collection<String>>(); final Map<Integer, Collection<String>> map1 = new HashMap<Integer, Collection<String>>();
map1.put(1, Arrays.asList("test1", "test2")); map1.put(1, Arrays.asList("test1", "test2"));
Map<Integer, Collection<String>> map2 = new HashMap<Integer, Collection<String>>(); final Map<Integer, Collection<String>> map2 = new HashMap<Integer, Collection<String>>();
map2.put(1, Arrays.asList("test3", "test4")); map2.put(1, Arrays.asList("test3", "test4"));
book1.setBuckets(map1); book1.setBuckets(map1);
book2.setBuckets(map2); book2.setBuckets(map2);
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>(); final List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
IndexQuery indexQuery1 = new IndexQuery(); final IndexQuery indexQuery1 = new IndexQuery();
indexQuery1.setId(book1.getId()); indexQuery1.setId(book1.getId());
indexQuery1.setObject(book1); indexQuery1.setObject(book1);
IndexQuery indexQuery2 = new IndexQuery(); final IndexQuery indexQuery2 = new IndexQuery();
indexQuery2.setId(book2.getId()); indexQuery2.setId(book2.getId());
indexQuery2.setObject(book2); indexQuery2.setObject(book2);
@ -342,10 +374,10 @@ public class NestedObjectTests {
elasticsearchTemplate.bulkIndex(indexQueries); elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(Book.class, true); elasticsearchTemplate.refresh(Book.class, true);
//then //then
SearchQuery searchQuery = new NativeSearchQueryBuilder() final SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(nestedQuery("buckets", termQuery("buckets.1", "test3"))) .withQuery(nestedQuery("buckets", termQuery("buckets.1", "test3")))
.build(); .build();
Page<Book> books = elasticsearchTemplate.queryForPage(searchQuery, Book.class); final Page<Book> books = elasticsearchTemplate.queryForPage(searchQuery, Book.class);
assertThat(books.getContent().size(), is(1)); assertThat(books.getContent().size(), is(1));
assertThat(books.getContent().get(0).getId(), is(book2.getId())); assertThat(books.getContent().get(0).getId(), is(book2.getId()));

View File

@ -40,7 +40,7 @@ public class Person {
@Field(type = FieldType.Nested) @Field(type = FieldType.Nested)
private List<Car> car; private List<Car> car;
@Field(type = FieldType.Nested) @Field(type = FieldType.Nested, includeInParent=true)
private List<Book> books; private List<Book> books;
public String getId() { public String getId() {

View File

@ -43,6 +43,9 @@ public class PersonMultipleLevelNested {
@Field(type = FieldType.Nested) @Field(type = FieldType.Nested)
private List<Car> cars; private List<Car> cars;
@Field(type = FieldType.Nested, includeInParent=true)
private List<Car> bestCars;
public String getId() { public String getId() {
return id; return id;
} }
@ -74,4 +77,13 @@ public class PersonMultipleLevelNested {
public void setCars(List<Car> cars) { public void setCars(List<Car> cars) {
this.cars = cars; this.cars = cars;
} }
public List<Car> getBestCars() {
return bestCars;
}
public void setBestCars(List<Car> bestCars) {
this.bestCars = bestCars;
}
} }