DATAES-33 : Mapping of Parent-Child Relationships

This commit is contained in:
mohsin.husen
2014-01-04 14:35:53 +00:00
parent bc1abd96e8
commit 5922695e34
14 changed files with 370 additions and 7 deletions

View File

@@ -0,0 +1,29 @@
/*
* 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;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
/**
* MinimalEntity
*
* @author Philipp Jardas
*/
@Document(indexName = "index", type = "type")
public class MinimalEntity {
@Id
private String id;
}

View File

@@ -0,0 +1,95 @@
/*
* 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;
import org.springframework.core.style.ToStringCreator;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.elasticsearch.annotations.*;
/**
* ParentEntity
*
* @author Philipp Jardas
*/
@Document(indexName = ParentEntity.INDEX, type = ParentEntity.PARENT_TYPE, indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public class ParentEntity {
public static final String INDEX = "parent-child";
public static final String PARENT_TYPE = "parent-entity";
public static final String CHILD_TYPE = "child-entity";
@Id
private String id;
@Field(type = FieldType.String, index = FieldIndex.analyzed, store = true)
private String name;
public ParentEntity() {
}
public ParentEntity(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return new ToStringCreator(this).append("id", id).append("name", name).toString();
}
@Document(indexName = INDEX, type = CHILD_TYPE, indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public static class ChildEntity {
@Id
private String id;
@Field(type = FieldType.String, store = true)
@Parent(type = PARENT_TYPE)
private String parentId;
@Field(type = FieldType.String, index = FieldIndex.analyzed, store = true)
private String name;
public ChildEntity() {
}
public ChildEntity(String id, String parentId, String name) {
this.id = id;
this.parentId = parentId;
this.name = name;
}
public String getId() {
return id;
}
public String getParentId() {
return parentId;
}
public String getName() {
return name;
}
@Override
public String toString() {
return new ToStringCreator(this).append("id", id).append("parentId", parentId).append("name", name).toString();
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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.core;
import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery;
import static org.elasticsearch.index.query.QueryBuilders.topChildrenQuery;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import java.util.List;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.ParentEntity;
import org.springframework.data.elasticsearch.ParentEntity.ChildEntity;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Philipp Jardas
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
public class ElasticsearchTemplateParentChildTests {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Before
public void before() {
clean();
elasticsearchTemplate.createIndex(ParentEntity.class);
elasticsearchTemplate.createIndex(ChildEntity.class);
elasticsearchTemplate.putMapping(ParentEntity.class);
elasticsearchTemplate.putMapping(ChildEntity.class);
}
@After
public void clean() {
elasticsearchTemplate.deleteIndex(ChildEntity.class);
elasticsearchTemplate.deleteIndex(ParentEntity.class);
}
@Test
public void shouldIndexParentChildEntity() {
// index two parents
ParentEntity parent1 = index("parent1", "First Parent");
ParentEntity parent2 = index("parent2", "Second Parent");
// index a child for each parent
String child1name = "First";
index("child1", parent1.getId(), child1name);
index("child2", parent2.getId(), "Second");
elasticsearchTemplate.refresh(ParentEntity.class, true);
elasticsearchTemplate.refresh(ChildEntity.class, true);
// find all parents that have the first child
QueryBuilder query = hasChildQuery(ParentEntity.CHILD_TYPE, QueryBuilders.fieldQuery("name", child1name));
List<ParentEntity> parents = elasticsearchTemplate.queryForList(new NativeSearchQuery(query), ParentEntity.class);
// we're expecting only the first parent as result
assertThat("parents", parents, contains(hasProperty("id", is(parent1.getId()))));
}
@Test
public void shouldSearchTopChildrenForGivenParent(){
// index two parents
ParentEntity parent1 = index("parent1", "First Parent");
ParentEntity parent2 = index("parent2", "Second Parent");
// index a child for each parent
String child1name = "First";
index("child1", parent1.getId(), child1name);
index("child2", parent2.getId(), "Second");
elasticsearchTemplate.refresh(ParentEntity.class, true);
elasticsearchTemplate.refresh(ChildEntity.class, true);
// find all parents that have the first child using topChildren Query
QueryBuilder query = topChildrenQuery(ParentEntity.CHILD_TYPE, QueryBuilders.fieldQuery("name", child1name));
List<ParentEntity> parents = elasticsearchTemplate.queryForList(new NativeSearchQuery(query), ParentEntity.class);
// we're expecting only the first parent as result
assertThat("parents", parents, contains(hasProperty("id", is(parent1.getId()))));
}
private ParentEntity index(String parentId, String name) {
ParentEntity parent = new ParentEntity(parentId, name);
IndexQuery index = new IndexQuery();
index.setId(parent.getId());
index.setObject(parent);
elasticsearchTemplate.index(index);
return parent;
}
private ChildEntity index(String childId, String parentId, String name) {
ChildEntity child = new ChildEntity(childId, parentId, name);
IndexQuery index = new IndexQuery();
index.setId(child.getId());
index.setObject(child);
index.setParentId(child.getParentId());
elasticsearchTemplate.index(index);
return child;
}
}

View File

@@ -8,6 +8,7 @@ import org.springframework.data.elasticsearch.SampleTransientEntity;
import org.springframework.data.elasticsearch.SimpleRecursiveEntity;
import org.springframework.data.elasticsearch.StockPrice;
import org.springframework.data.elasticsearch.StockPriceBuilder;
import org.springframework.data.elasticsearch.MinimalEntity;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.test.context.ContextConfiguration;
@@ -44,7 +45,7 @@ public class MappingBuilderTests {
"type\":\"string\",\"index\":\"not_analyzed\",\"search_analyzer\":\"standard\"," +
"\"index_analyzer\":\"standard\"}}}}";
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleTransientEntity.class, "mapping", "id");
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleTransientEntity.class, "mapping", "id", null);
assertThat(xContentBuilder.string(), is(expected));
}
@@ -54,7 +55,7 @@ public class MappingBuilderTests {
final String expected = "{\"mapping\":{\"properties\":{\"price\":{\"store\":false,\"type\":\"double\"}}}}";
//When
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(StockPrice.class, "mapping", "id");
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(StockPrice.class, "mapping", "id", null);
//Then
assertThat(xContentBuilder.string(), is(expected));
@@ -83,4 +84,10 @@ public class MappingBuilderTests {
assertThat(entry.getPrice(), is(new BigDecimal(price)));
}
@Test
public void shouldCreateMappingForSpecifiedParentType() throws IOException {
final String expected = "{\"mapping\":{\"_parent\":{\"type\":\"parentType\"},\"properties\":{}}}";
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(MinimalEntity.class, "mapping", "id", "parentType");
assertThat(xContentBuilder.string(), is(expected));
}
}

View File

@@ -20,7 +20,7 @@ public class SimpleElasticsearchDateMappingTest {
@Test
public void testCorrectDateMappings() throws NoSuchFieldException, IntrospectionException, IOException {
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleDateMappingEntity.class, "mapping", "id");
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleDateMappingEntity.class, "mapping", "id", null);
Assert.assertEquals(EXPECTED_MAPPING, xContentBuilder.string());
}
}