Skip to content

Commit fb95bb0

Browse files
committed
DATAES-42 : Adding support for letting Elasticsearch generate Id for document
1 parent bbc46d9 commit fb95bb0

File tree

5 files changed

+166
-11
lines changed

5 files changed

+166
-11
lines changed

src/main/java/org/springframework/data/elasticsearch/core/DefaultResultMapper.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,15 @@
2626
import org.elasticsearch.search.SearchHitField;
2727
import org.elasticsearch.search.facet.Facet;
2828
import org.springframework.data.domain.Pageable;
29+
import org.springframework.data.elasticsearch.annotations.Document;
2930
import org.springframework.data.elasticsearch.core.facet.DefaultFacetMapper;
3031
import org.springframework.data.elasticsearch.core.facet.FacetResult;
32+
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
33+
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
34+
import org.springframework.data.mapping.PersistentProperty;
35+
import org.springframework.data.mapping.context.MappingContext;
3136

37+
import java.lang.reflect.Method;
3238
import java.io.ByteArrayOutputStream;
3339
import java.io.IOException;
3440
import java.nio.charset.Charset;
@@ -41,9 +47,16 @@
4147
*/
4248
public class DefaultResultMapper extends AbstractResultMapper {
4349

50+
private MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
51+
4452
public DefaultResultMapper(){
4553
super(new DefaultEntityMapper());
4654
}
55+
56+
public DefaultResultMapper(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext){
57+
super(new DefaultEntityMapper());
58+
this.mappingContext = mappingContext;
59+
}
4760

4861
public DefaultResultMapper(EntityMapper entityMapper) {
4962
super(entityMapper);
@@ -55,11 +68,14 @@ public <T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pa
5568
List<T> results = new ArrayList<T>();
5669
for (SearchHit hit : response.getHits()) {
5770
if (hit != null) {
71+
T result = null;
5872
if (!Strings.isNullOrEmpty(hit.sourceAsString())) {
59-
results.add(mapEntity(hit.sourceAsString(), clazz));
73+
result = mapEntity(hit.sourceAsString(), clazz);
6074
} else {
61-
results.add(mapEntity(hit.getFields().values(), clazz));
75+
result = mapEntity(hit.getFields().values(), clazz);
6276
}
77+
setPersistentEntityId(result, hit.getId(), clazz);
78+
results.add(result);
6379
}
6480
}
6581
List<FacetResult> facets = new ArrayList<FacetResult>();
@@ -106,6 +122,27 @@ private String buildJSONFromFields(Collection<SearchHitField> values) {
106122

107123
@Override
108124
public <T> T mapResult(GetResponse response, Class<T> clazz) {
109-
return mapEntity(response.getSourceAsString(),clazz);
125+
T result = mapEntity(response.getSourceAsString(),clazz);
126+
if (result != null){
127+
setPersistentEntityId(result, response.getId(), clazz);
128+
}
129+
return result;
130+
}
131+
132+
private <T> void setPersistentEntityId(T result, String id, Class<T> clazz) {
133+
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)){
134+
PersistentProperty<ElasticsearchPersistentProperty> idProperty = mappingContext.getPersistentEntity(clazz).getIdProperty();
135+
// Only deal with String because ES generated Ids are strings !
136+
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)){
137+
Method setter = idProperty.getSetter();
138+
if (setter != null){
139+
try{
140+
setter.invoke(result, id);
141+
} catch (Throwable t) {
142+
t.printStackTrace();
143+
}
144+
}
145+
}
146+
}
110147
}
111148
}

src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@
5656
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
5757
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
5858
import org.springframework.data.elasticsearch.core.query.*;
59+
import org.springframework.data.mapping.PersistentProperty;
5960
import org.springframework.util.Assert;
6061

6162
import java.io.IOException;
63+
import java.lang.reflect.Method;
6264
import java.util.*;
6365

6466
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
@@ -106,7 +108,7 @@ public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearch
106108
this.client = client;
107109
this.elasticsearchConverter = (elasticsearchConverter == null) ? new MappingElasticsearchConverter(
108110
new SimpleElasticsearchMappingContext()) : elasticsearchConverter;
109-
this.resultsMapper = (resultsMapper == null) ? new DefaultResultMapper() : resultsMapper;
111+
this.resultsMapper = (resultsMapper == null) ? new DefaultResultMapper(this.elasticsearchConverter.getMappingContext()) : resultsMapper;
110112
}
111113

112114
@Override
@@ -145,7 +147,9 @@ public <T> T queryForObject(GetQuery query, Class<T> clazz, GetResultMapper mapp
145147
GetResponse response = client
146148
.prepareGet(persistentEntity.getIndexName(), persistentEntity.getIndexType(), query.getId()).execute()
147149
.actionGet();
148-
return mapper.mapResult(response, clazz);
150+
151+
T entity = mapper.mapResult(response, clazz);
152+
return entity;
149153
}
150154

151155
@Override
@@ -246,7 +250,12 @@ public <T> long count(SearchQuery query, Class<T> clazz) {
246250

247251
@Override
248252
public String index(IndexQuery query) {
249-
return prepareIndex(query).execute().actionGet().getId();
253+
String documentId = prepareIndex(query).execute().actionGet().getId();
254+
// We should call this because we are not going through a mapper.
255+
if (query.getObject() != null){
256+
setPersistentEntityId(query.getObject(), documentId);
257+
}
258+
return documentId;
250259
}
251260

252261
@Override
@@ -540,10 +549,16 @@ private IndexRequestBuilder prepareIndex(IndexQuery query) {
540549

541550
IndexRequestBuilder indexRequestBuilder = null;
542551

543-
if(query.getObject() != null) {
544-
indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(
545-
resultsMapper.getEntityMapper().mapToString(query.getObject()));
546-
} else if(query.getSource() != null) {
552+
if (query.getObject() != null) {
553+
// If we have a query id and a document id, do not ask ES to generate one.
554+
String entityId = getPersistentEntityId(query.getObject());
555+
if (query.getId() != null && entityId != null){
556+
indexRequestBuilder = client.prepareIndex(indexName, type, query.getId());
557+
} else {
558+
indexRequestBuilder = client.prepareIndex(indexName, type);
559+
}
560+
indexRequestBuilder.setSource(resultsMapper.getEntityMapper().mapToString(query.getObject()));
561+
} else if (query.getSource() != null) {
547562
indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(query.getSource());
548563
} else {
549564
throw new ElasticsearchException("object or source is null, failed to index the document [id: " + query.getId() + "]");
@@ -613,6 +628,40 @@ private ElasticsearchPersistentEntity getPersistentEntityFor(Class clazz) {
613628
+ " is not a Document. Make sure the document class is annotated with @Document(indexName=\"foo\")");
614629
return elasticsearchConverter.getMappingContext().getPersistentEntity(clazz);
615630
}
631+
632+
private String getPersistentEntityId(Object entity){
633+
PersistentProperty idProperty = getPersistentEntityFor(entity.getClass()).getIdProperty();
634+
if (idProperty != null){
635+
Method getter = idProperty.getGetter();
636+
if (getter != null){
637+
try{
638+
Object id = getter.invoke(entity);
639+
if (id != null){
640+
return String.valueOf(id);
641+
}
642+
643+
} catch (Throwable t){
644+
t.printStackTrace();
645+
}
646+
}
647+
}
648+
return null;
649+
}
650+
651+
private void setPersistentEntityId(Object entity, String id){
652+
PersistentProperty idProperty = getPersistentEntityFor(entity.getClass()).getIdProperty();
653+
// Only deal with String because ES generated Ids are strings !
654+
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)){
655+
Method setter = idProperty.getSetter();
656+
if (setter != null){
657+
try{
658+
setter.invoke(entity, id);
659+
} catch (Throwable t) {
660+
t.printStackTrace();
661+
}
662+
}
663+
}
664+
}
616665

617666
private String[] retrieveIndexNameFromPersistentEntity(Class clazz) {
618667
return new String[]{getPersistentEntityFor(clazz).getIndexName()};

src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,4 +1034,62 @@ public void shouldReturnDocumentAboveMinimalScoreGivenQuery() {
10341034
assertThat(page.getTotalElements(),is(1L));
10351035
assertThat(page.getContent().get(0).getMessage(), is("ab"));
10361036
}
1037+
1038+
1039+
@Test
1040+
public void shouldDoIndexWithoutId() {
1041+
// given
1042+
// document
1043+
SampleEntity sampleEntity = new SampleEntity();
1044+
sampleEntity.setMessage("some message");
1045+
sampleEntity.setVersion(System.currentTimeMillis());
1046+
1047+
IndexQuery indexQuery = new IndexQuery();
1048+
indexQuery.setObject(sampleEntity);
1049+
// when
1050+
String documentId = elasticsearchTemplate.index(indexQuery);
1051+
// then
1052+
assertThat(sampleEntity.getId(), is(equalTo(documentId)));
1053+
1054+
GetQuery getQuery = new GetQuery();
1055+
getQuery.setId(documentId);
1056+
SampleEntity result = elasticsearchTemplate.queryForObject(getQuery, SampleEntity.class);
1057+
assertThat(result.getId(), is(equalTo(documentId)));
1058+
}
1059+
1060+
@Test
1061+
public void shouldDoBulkIndexWithoutId() {
1062+
// given
1063+
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
1064+
// first document
1065+
String documentId = randomNumeric(5);
1066+
SampleEntity sampleEntity1 = new SampleEntity();
1067+
sampleEntity1.setMessage("some message");
1068+
sampleEntity1.setVersion(System.currentTimeMillis());
1069+
1070+
IndexQuery indexQuery1 = new IndexQuery();
1071+
//indexQuery1.setId(documentId);
1072+
indexQuery1.setObject(sampleEntity1);
1073+
indexQueries.add(indexQuery1);
1074+
1075+
// second document
1076+
SampleEntity sampleEntity2 = new SampleEntity();
1077+
sampleEntity2.setMessage("some message");
1078+
sampleEntity2.setVersion(System.currentTimeMillis());
1079+
1080+
IndexQuery indexQuery2 = new IndexQuery();
1081+
indexQuery2.setObject(sampleEntity2);
1082+
indexQueries.add(indexQuery2);
1083+
// when
1084+
elasticsearchTemplate.bulkIndex(indexQueries);
1085+
elasticsearchTemplate.refresh(SampleEntity.class, true);
1086+
// then
1087+
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
1088+
Page<SampleEntity> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class);
1089+
assertThat(sampleEntities.getTotalElements(), is(equalTo(2L)));
1090+
1091+
assertThat(sampleEntities.getContent().get(0).getId(), is(notNullValue()));
1092+
assertThat(sampleEntities.getContent().get(1).getId(), is(notNullValue()));
1093+
}
1094+
10371095
}

src/test/java/org/springframework/data/elasticsearch/repository/support/IntegerIDRepositoryTests.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.junit.Test;
2121
import org.junit.runner.RunWith;
2222
import org.springframework.beans.factory.annotation.Autowired;
23-
import org.springframework.data.elasticsearch.DoubleIDEntity;
2423
import org.springframework.data.elasticsearch.IntegerIDEntity;
2524
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
2625
import org.springframework.data.elasticsearch.repositories.IntegerIDRepository;

src/test/java/org/springframework/data/elasticsearch/repository/support/SimpleElasticsearchRepositoryTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ public void shouldSaveDocument() {
104104
assertThat(entityFromElasticSearch, is(notNullValue()));
105105
}
106106

107+
@Test
108+
public void shouldSaveDocumentWithoutId() {
109+
// given
110+
SampleEntity sampleEntity = new SampleEntity();
111+
sampleEntity.setMessage("some message");
112+
sampleEntity.setVersion(System.currentTimeMillis());
113+
// when
114+
repository.save(sampleEntity);
115+
// then
116+
assertThat(sampleEntity.getId(), is(notNullValue()));
117+
}
118+
107119
@Test
108120
public void shouldFindDocumentById() {
109121
// given

0 commit comments

Comments
 (0)