From 2775f1f765e645e6c5f518a6b520f982036fedf2 Mon Sep 17 00:00:00 2001 From: puppylpg Date: Thu, 22 Sep 2022 23:47:26 +0800 Subject: [PATCH 1/2] 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 2bf5acf90..56b9718ff 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/AbstractElasticsearchTemplate.java @@ -48,6 +48,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; @@ -74,6 +75,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); } @@ -468,6 +470,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() @@ -508,7 +529,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 ced39843e..8f8b3438e 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 { @@ -307,6 +308,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 cbe7697de..f0239e99a 100755 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java @@ -105,6 +105,7 @@ * @author Farid Faoudi * @author Peer Mueller * @author Sijia Liu + * @author Haibo Liu */ @SpringIntegrationTest public abstract class ElasticsearchIntegrationTests implements NewElasticsearchClientDevelopment { @@ -177,6 +178,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() { @@ -1499,6 +1514,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 af3885f3cb05627169e65a6ea6b3c39c728d0c78 Mon Sep 17 00:00:00 2001 From: puppylpg Date: Sat, 24 Sep 2022 21:57:27 +0800 Subject: [PATCH 2/2] 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.