From 8fd834d39284bb1e0ab6457530ac616a0908950f Mon Sep 17 00:00:00 2001 From: puppylpg Date: Thu, 22 Sep 2022 23:47:26 +0800 Subject: [PATCH 1/3] Support partially update document by entity. --- .../core/AbstractElasticsearchTemplate.java | 25 ++++++++++- .../core/DocumentOperations.java | 10 +++++ .../core/ElasticsearchIntegrationTests.java | 42 +++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java index 919c221de..8c3ecf0fa 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java @@ -49,6 +49,7 @@ import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm; import org.springframework.data.elasticsearch.core.query.UpdateQuery; +import org.springframework.data.elasticsearch.core.query.UpdateResponse; import org.springframework.data.elasticsearch.core.routing.DefaultRoutingResolver; import org.springframework.data.elasticsearch.core.routing.RoutingResolver; import org.springframework.data.elasticsearch.support.VersionInfo; @@ -75,6 +76,7 @@ * @author Subhobrata Dey * @author Steven Pearce * @author Anton Naydenov + * @author Haibo Liu */ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware { @@ -305,7 +307,7 @@ public String delete(Object entity) { @Override public String delete(Object entity, IndexCoordinates index) { String entityId = getEntityId(entity); - Assert.notNull(entityId, "entity must have an if that is notnull"); + Assert.notNull(entityId, "entity must have an id that is notnull"); return this.delete(entityId, index); } @@ -460,6 +462,25 @@ public IndexCoordinates getIndexCoordinatesFor(Class clazz) { return getRequiredPersistentEntity(clazz).getIndexCoordinates(); } + @Override + public UpdateResponse update(T entity) { + return update(this.buildUpdateQueryByEntity(entity), this.getIndexCoordinatesFor(entity.getClass())); + } + + protected UpdateQuery buildUpdateQueryByEntity(T entity) { + String id = this.getEntityId(entity); + Assert.notNull(entity, "entity must have an id that is notnull"); + + UpdateQuery.Builder updateQueryBuilder = UpdateQuery.builder(id) + .withDocument(elasticsearchConverter.mapObject(entity)); + + String routing = this.getEntityRouting(entity); + if (Objects.nonNull(routing)) { + updateQueryBuilder.withRouting(routing); + } + return updateQueryBuilder.build(); + } + protected T updateIndexedObject(T entity, IndexedObjectInformation indexedObjectInformation) { ElasticsearchPersistentEntity persistentEntity = elasticsearchConverter.getMappingContext() @@ -500,7 +521,7 @@ ElasticsearchPersistentEntity getRequiredPersistentEntity(Class clazz) { } @Nullable - private String getEntityId(Object entity) { + public String getEntityId(Object entity) { Object id = entityOperations.forEntity(entity, elasticsearchConverter.getConversionService(), routingResolver) .getId(); diff --git a/src/main/java/org/springframework/data/elasticsearch/core/DocumentOperations.java b/src/main/java/org/springframework/data/elasticsearch/core/DocumentOperations.java index 5a7eb9ebb..8f3afb928 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/DocumentOperations.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/DocumentOperations.java @@ -37,6 +37,7 @@ * @author Peter-Josef Meisch * @author Farid Faoudi * @author Sijia Liu + * @author Haibo Liu * @since 4.0 */ public interface DocumentOperations { @@ -293,6 +294,15 @@ default void bulkUpdate(List queries, IndexCoordinates index) { */ ByQueryResponse delete(Query query, Class clazz, IndexCoordinates index); + /** + * Partially update a document by the given entity. + * + * @param entity the entity to update partially + * @return the update response + * @param the entity type + */ + UpdateResponse update(T entity); + /** * Partial update of the document. * diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java index 0a0c31863..0059752ca 100755 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java @@ -112,6 +112,7 @@ * @author Farid Faoudi * @author Peer Mueller * @author Sijia Liu + * @author Haibo Liu */ @SpringIntegrationTest public abstract class ElasticsearchIntegrationTests { @@ -186,6 +187,20 @@ protected abstract Query getMatchAllQueryWithIncludesAndInlineExpressionScript(@ protected abstract Query getQueryWithRescorer(); + @Test + public void shouldThrowDataAccessExceptionIfDocumentDoesNotExistWhileDoingPartialUpdateByEntity() { + + // given + String documentId = nextIdAsString(); + String messageBeforeUpdate = "some test message"; + + SampleEntity sampleEntity = SampleEntity.builder().id(documentId).message(messageBeforeUpdate) + .version(System.currentTimeMillis()).build(); + + assertThatThrownBy(() -> operations.update(sampleEntity)) + .isInstanceOf(DataAccessException.class); + } + @Test public void shouldThrowDataAccessExceptionIfDocumentDoesNotExistWhileDoingPartialUpdate() { @@ -1505,6 +1520,33 @@ public void shouldDeleteIndexForGivenEntity() { assertThat(indexOperations.exists()).isFalse(); } + @Test + public void shouldDoPartialUpdateBySuppliedEntityForExistingDocument() { + + // given + String documentId = nextIdAsString(); + String messageBeforeUpdate = "some test message"; + String messageAfterUpdate = "test message"; + + SampleEntity sampleEntity = SampleEntity.builder().id(documentId).message(messageBeforeUpdate) + .version(System.currentTimeMillis()).build(); + + IndexQuery indexQuery = getIndexQuery(sampleEntity); + + operations.index(indexQuery, IndexCoordinates.of(indexNameProvider.indexName())); + + // modify the entity + sampleEntity.setMessage(messageAfterUpdate); + + // when + operations.update(sampleEntity); + + // then + SampleEntity indexedEntity = operations.get(documentId, SampleEntity.class, + IndexCoordinates.of(indexNameProvider.indexName())); + assertThat(indexedEntity.getMessage()).isEqualTo(messageAfterUpdate); + } + @Test public void shouldDoPartialUpdateForExistingDocument() { From 0f3acf515487fb5433a503e56077aa06f36d849d Mon Sep 17 00:00:00 2001 From: puppylpg Date: Sat, 24 Sep 2022 21:57:27 +0800 Subject: [PATCH 2/3] fix typo --- CONTRIBUTING.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 18c1924f9..7fbe06192 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -8,4 +8,4 @@ In order to run the tests locally with `./mvnw test` you need to have docker run == Class names of the test classes -Tset classes that do depend on the client have either `ERHLC` (when using the deprecated Elasticsearch `RestHighLevelClient`) or `ELC` (the new `ElasticsearchClient`) in their name. +Test classes that do depend on the client have either `ERHLC` (when using the deprecated Elasticsearch `RestHighLevelClient`) or `ELC` (the new `ElasticsearchClient`) in their name. From 0ed7947655257dd4d647429217de16aa15c20e78 Mon Sep 17 00:00:00 2001 From: puppylpg Date: Sun, 25 Sep 2022 14:11:52 +0800 Subject: [PATCH 3/3] polish --- .../core/AbstractElasticsearchTemplate.java | 10 ++++++---- .../core/ElasticsearchIntegrationTests.java | 17 ++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java index 8c3ecf0fa..315c149ea 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java @@ -464,17 +464,19 @@ public IndexCoordinates getIndexCoordinatesFor(Class clazz) { @Override public UpdateResponse update(T entity) { - return update(this.buildUpdateQueryByEntity(entity), this.getIndexCoordinatesFor(entity.getClass())); + return update(buildUpdateQueryByEntity(entity), getIndexCoordinatesFor(entity.getClass())); } protected UpdateQuery buildUpdateQueryByEntity(T entity) { - String id = this.getEntityId(entity); + Assert.notNull(entity, "entity must not be null"); + + String id = getEntityId(entity); Assert.notNull(entity, "entity must have an id that is notnull"); UpdateQuery.Builder updateQueryBuilder = UpdateQuery.builder(id) .withDocument(elasticsearchConverter.mapObject(entity)); - String routing = this.getEntityRouting(entity); + String routing = getEntityRouting(entity); if (Objects.nonNull(routing)) { updateQueryBuilder.withRouting(routing); } @@ -521,7 +523,7 @@ ElasticsearchPersistentEntity getRequiredPersistentEntity(Class clazz) { } @Nullable - public String getEntityId(Object entity) { + private String getEntityId(Object entity) { Object id = entityOperations.forEntity(entity, elasticsearchConverter.getConversionService(), routingResolver) .getId(); diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java index 0059752ca..949ad383f 100755 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java @@ -187,7 +187,7 @@ protected abstract Query getMatchAllQueryWithIncludesAndInlineExpressionScript(@ protected abstract Query getQueryWithRescorer(); - @Test + @Test // #2304 public void shouldThrowDataAccessExceptionIfDocumentDoesNotExistWhileDoingPartialUpdateByEntity() { // given @@ -1520,30 +1520,29 @@ public void shouldDeleteIndexForGivenEntity() { assertThat(indexOperations.exists()).isFalse(); } - @Test + @Test // #2304 public void shouldDoPartialUpdateBySuppliedEntityForExistingDocument() { // given String documentId = nextIdAsString(); String messageBeforeUpdate = "some test message"; String messageAfterUpdate = "test message"; + String originalTypeInfo = "some type"; - SampleEntity sampleEntity = SampleEntity.builder().id(documentId).message(messageBeforeUpdate) + SampleEntity sampleEntity = SampleEntity.builder().id(documentId).message(messageBeforeUpdate).type(originalTypeInfo) .version(System.currentTimeMillis()).build(); - - IndexQuery indexQuery = getIndexQuery(sampleEntity); - - operations.index(indexQuery, IndexCoordinates.of(indexNameProvider.indexName())); + operations.save(sampleEntity); // modify the entity sampleEntity.setMessage(messageAfterUpdate); + sampleEntity.setType(null); // when operations.update(sampleEntity); // then - SampleEntity indexedEntity = operations.get(documentId, SampleEntity.class, - IndexCoordinates.of(indexNameProvider.indexName())); + SampleEntity indexedEntity = operations.get(documentId, SampleEntity.class); + assertThat(indexedEntity.getType()).isEqualTo(originalTypeInfo); assertThat(indexedEntity.getMessage()).isEqualTo(messageAfterUpdate); }