diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/Document.java b/src/main/java/org/springframework/data/elasticsearch/annotations/Document.java index 85e1bfc1e..2fd0123d6 100644 --- a/src/main/java/org/springframework/data/elasticsearch/annotations/Document.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/Document.java @@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.annotations; import java.lang.annotation.*; +import org.elasticsearch.index.VersionType; import org.springframework.data.annotation.Persistent; /** @@ -25,6 +26,7 @@ import org.springframework.data.annotation.Persistent; * @author Rizwan Idrees * @author Mohsin Husen * @author Mason Chan + * @author Ivan Greene */ @Persistent @@ -48,4 +50,6 @@ public @interface Document { String indexStoreType() default "fs"; boolean createIndex() default true; + + VersionType versionType() default VersionType.EXTERNAL; } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java index 5c0034c85..ff22bfe51 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java @@ -16,7 +16,6 @@ package org.springframework.data.elasticsearch.core; import static org.elasticsearch.client.Requests.refreshRequest; -import static org.elasticsearch.index.VersionType.EXTERNAL; import static org.elasticsearch.index.query.QueryBuilders.moreLikeThisQuery; import static org.elasticsearch.index.query.QueryBuilders.wrapperQuery; import static org.springframework.data.elasticsearch.core.MappingBuilder.buildMapping; @@ -67,6 +66,7 @@ import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.VersionType; import org.elasticsearch.index.query.MoreLikeThisQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; @@ -131,6 +131,7 @@ import org.springframework.util.StringUtils; * @author Don Wellington * @author Zetang Zeng * @author Peter Nowak + * @author Ivan Greene */ public class ElasticsearchRestTemplate implements ElasticsearchOperations, EsClient, ApplicationContextAware { @@ -1343,7 +1344,8 @@ public class ElasticsearchRestTemplate } if (query.getVersion() != null) { indexRequest.version(query.getVersion()); - indexRequest.versionType(EXTERNAL); + VersionType versionType = retrieveVersionTypeFromPersistentEntity(query.getObject().getClass()); + indexRequest.versionType(versionType); } if (query.getParentId() != null) { @@ -1520,6 +1522,13 @@ public class ElasticsearchRestTemplate return null; } + private VersionType retrieveVersionTypeFromPersistentEntity(Class clazz) { + if (clazz != null) { + return getPersistentEntityFor(clazz).getVersionType(); + } + return VersionType.EXTERNAL; + } + private List extractIds(SearchResponse response) { List ids = new ArrayList<>(); for (SearchHit hit : response.getHits()) { diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index b146d6f4c..65c4274a4 100755 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -16,7 +16,6 @@ package org.springframework.data.elasticsearch.core; import static org.elasticsearch.client.Requests.*; -import static org.elasticsearch.index.VersionType.*; import static org.elasticsearch.index.query.QueryBuilders.*; import static org.springframework.data.elasticsearch.core.MappingBuilder.*; import static org.springframework.util.CollectionUtils.*; @@ -65,6 +64,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.VersionType; import org.elasticsearch.index.query.MoreLikeThisQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; @@ -137,6 +137,7 @@ import org.springframework.util.StringUtils; * @author Ted Liang * @author Jean-Baptiste Nizet * @author Zetang Zeng + * @author Ivan Greene */ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient, ApplicationContextAware { @@ -1175,7 +1176,8 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient< } if (query.getVersion() != null) { indexRequestBuilder.setVersion(query.getVersion()); - indexRequestBuilder.setVersionType(EXTERNAL); + VersionType versionType = retrieveVersionTypeFromPersistentEntity(query.getObject().getClass()); + indexRequestBuilder.setVersionType(versionType); } if (query.getParentId() != null) { @@ -1288,6 +1290,13 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient< return null; } + private VersionType retrieveVersionTypeFromPersistentEntity(Class clazz) { + if (clazz != null) { + return getPersistentEntityFor(clazz).getVersionType(); + } + return VersionType.EXTERNAL; + } + private List extractIds(SearchResponse response) { List ids = new ArrayList<>(); for (SearchHit hit : response.getHits()) { diff --git a/src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java b/src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java index d0e1d0db5..761deced4 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java @@ -15,6 +15,7 @@ */ package org.springframework.data.elasticsearch.core.mapping; +import org.elasticsearch.index.VersionType; import org.springframework.data.mapping.PersistentEntity; import org.springframework.lang.Nullable; @@ -26,6 +27,7 @@ import org.springframework.lang.Nullable; * @author Mark Paluch * @author Sascha Woo * @author Oliver Gierke + * @author Ivan Greene */ public interface ElasticsearchPersistentEntity extends PersistentEntity { @@ -51,6 +53,8 @@ public interface ElasticsearchPersistentEntity extends PersistentEntity extends BasicPersistentEntity implements ElasticsearchPersistentEntity, ApplicationContextAware { @@ -65,6 +66,7 @@ public class SimpleElasticsearchPersistentEntity extends BasicPersistentEntit private ElasticsearchPersistentProperty parentIdProperty; private ElasticsearchPersistentProperty scoreProperty; private String settingPath; + private VersionType versionType; private boolean createIndexAndMapping; public SimpleElasticsearchPersistentEntity(TypeInformation typeInformation) { @@ -84,6 +86,7 @@ public class SimpleElasticsearchPersistentEntity extends BasicPersistentEntit this.replicas = document.replicas(); this.refreshInterval = document.refreshInterval(); this.indexStoreType = document.indexStoreType(); + this.versionType = document.versionType(); this.createIndexAndMapping = document.createIndex(); } if (clazz.isAnnotationPresent(Setting.class)) { @@ -155,6 +158,11 @@ public class SimpleElasticsearchPersistentEntity extends BasicPersistentEntit return parentIdProperty; } + @Override + public VersionType getVersionType() { + return versionType; + } + @Override public String settingPath() { return settingPath; diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchEntityInformation.java b/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchEntityInformation.java index 8b19dc98f..d20dccfb0 100644 --- a/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchEntityInformation.java +++ b/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchEntityInformation.java @@ -15,6 +15,7 @@ */ package org.springframework.data.elasticsearch.repository.support; +import org.elasticsearch.index.VersionType; import org.springframework.data.repository.core.EntityInformation; /** @@ -23,6 +24,7 @@ import org.springframework.data.repository.core.EntityInformation; * @author Rizwan Idrees * @author Mohsin Husen * @author Christoph Strobl + * @author Ivan Greene */ public interface ElasticsearchEntityInformation extends EntityInformation { @@ -34,5 +36,7 @@ public interface ElasticsearchEntityInformation extends EntityInformation Long getVersion(T entity); + VersionType getVersionType(); + String getParentId(T entity); } diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/support/MappingElasticsearchEntityInformation.java b/src/main/java/org/springframework/data/elasticsearch/repository/support/MappingElasticsearchEntityInformation.java index bde63ca39..9a2683331 100644 --- a/src/main/java/org/springframework/data/elasticsearch/repository/support/MappingElasticsearchEntityInformation.java +++ b/src/main/java/org/springframework/data/elasticsearch/repository/support/MappingElasticsearchEntityInformation.java @@ -15,6 +15,7 @@ */ package org.springframework.data.elasticsearch.repository.support; +import org.elasticsearch.index.VersionType; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; import org.springframework.data.repository.core.support.PersistentEntityInformation; @@ -32,6 +33,7 @@ import org.springframework.util.Assert; * @author Oliver Gierke * @author Mark Paluch * @author Christoph Strobl + * @author Ivan Greene */ public class MappingElasticsearchEntityInformation extends PersistentEntityInformation implements ElasticsearchEntityInformation { @@ -39,12 +41,13 @@ public class MappingElasticsearchEntityInformation extends PersistentEnti private final ElasticsearchPersistentEntity entityMetadata; private final String indexName; private final String type; + private final VersionType versionType; public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity entity) { - this(entity, entity.getIndexName(), entity.getIndexType()); + this(entity, entity.getIndexName(), entity.getIndexType(), entity.getVersionType()); } - public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity entity, String indexName, String type) { + public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity entity, String indexName, String type, VersionType versionType) { super(entity); Assert.notNull(indexName, "IndexName must not be null!"); @@ -53,6 +56,7 @@ public class MappingElasticsearchEntityInformation extends PersistentEnti this.entityMetadata = entity; this.indexName = indexName; this.type = type; + this.versionType = versionType; } @Override @@ -81,6 +85,11 @@ public class MappingElasticsearchEntityInformation extends PersistentEnti } } + @Override + public VersionType getVersionType() { + return versionType; + } + @Override public String getParentId(T entity) { diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/support/ReactiveElasticsearchRepositoryFactory.java b/src/main/java/org/springframework/data/elasticsearch/repository/support/ReactiveElasticsearchRepositoryFactory.java index 8b4a431da..25f3f5c08 100644 --- a/src/main/java/org/springframework/data/elasticsearch/repository/support/ReactiveElasticsearchRepositoryFactory.java +++ b/src/main/java/org/springframework/data/elasticsearch/repository/support/ReactiveElasticsearchRepositoryFactory.java @@ -47,6 +47,7 @@ import org.springframework.util.Assert; * instances. * * @author Christoph Strobl + * @author Ivan Greene * @since 3.2 */ public class ReactiveElasticsearchRepositoryFactory extends ReactiveRepositoryFactorySupport { @@ -116,7 +117,7 @@ public class ReactiveElasticsearchRepositoryFactory extends ReactiveRepositoryFa ElasticsearchPersistentEntity entity = mappingContext.getRequiredPersistentEntity(domainClass); return new MappingElasticsearchEntityInformation<>((ElasticsearchPersistentEntity) entity, entity.getIndexName(), - entity.getIndexType()); + entity.getIndexType(), entity.getVersionType()); } /** diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java index 4fe02bff0..56a0ff67e 100755 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java @@ -53,6 +53,7 @@ import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage; import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl; import org.springframework.data.elasticsearch.core.query.*; import org.springframework.data.elasticsearch.entities.Book; +import org.springframework.data.elasticsearch.entities.GTEVersionEntity; import org.springframework.data.elasticsearch.entities.HetroEntity1; import org.springframework.data.elasticsearch.entities.HetroEntity2; import org.springframework.data.elasticsearch.entities.SampleEntity; @@ -80,6 +81,7 @@ import static org.springframework.data.elasticsearch.utils.IndexBuilder.*; * @author Jean-Baptiste Nizet * @author Zetang Zeng * @author Peter Nowak + * @author Ivan Greene */ @Ignore @@ -1814,6 +1816,60 @@ public class ElasticsearchTemplateTests { assertThat(sampleEntities.getContent().get(1).get("userId"), is(person2.get("userId"))); } + /* + DATAES-523 + */ + @Test + public void shouldIndexGteEntityWithVersionType() { + // given + String documentId = randomNumeric(5); + GTEVersionEntity entity = GTEVersionEntity.builder().id(documentId) + .name("FooBar") + .version(System.currentTimeMillis()).build(); + + IndexQueryBuilder indexQueryBuilder = new IndexQueryBuilder().withId(documentId) + .withIndexName(INDEX_NAME).withType(TYPE_NAME) + .withVersion(entity.getVersion()) + .withObject(entity); + + Exception ex = null; + + try { + elasticsearchTemplate.index(indexQueryBuilder.build()); + } catch (Exception e) { + ex = e; + } + assertNull(ex); + elasticsearchTemplate.refresh(INDEX_NAME); + + SearchQuery searchQuery = new NativeSearchQueryBuilder().withIndices(INDEX_NAME) + .withTypes(TYPE_NAME).withQuery(matchAllQuery()).build(); + // when + Page entities = elasticsearchTemplate.queryForPage(searchQuery, GTEVersionEntity.class); + // then + assertThat(entities, is(notNullValue())); + assertThat(entities.getTotalElements(), greaterThanOrEqualTo(1L)); + + // reindex with same version + try { + elasticsearchTemplate.index(indexQueryBuilder.build()); + } catch (Exception e) { + ex = e; + } + assertNull(ex); + elasticsearchTemplate.refresh(INDEX_NAME); + + // reindex with version one below + try { + elasticsearchTemplate.index(indexQueryBuilder.withVersion(entity.getVersion() - 1).build()); + } catch (Exception e) { + ex = e; + } + assertNotNull(ex); + String message = ex.getMessage().toLowerCase(); + assertTrue("Exception is version conflict", message.contains("version") && message.contains("conflict")); + } + @Test public void shouldIndexSampleEntityWithIndexAndTypeAtRuntime() { // given diff --git a/src/test/java/org/springframework/data/elasticsearch/entities/GTEVersionEntity.java b/src/test/java/org/springframework/data/elasticsearch/entities/GTEVersionEntity.java new file mode 100644 index 000000000..3a257b444 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/entities/GTEVersionEntity.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018-2019 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.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.elasticsearch.index.VersionType; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Version; +import org.springframework.data.elasticsearch.annotations.Document; + +/** + * @author Ivan Greene + */ +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +@ToString +@Builder +@Document(indexName = "test-index-sample", type = "test-type", shards = 1, replicas = 0, + refreshInterval = "-1", versionType = VersionType.EXTERNAL_GTE) +public class GTEVersionEntity { + + @Version + private Long version; + + @Id + private String id; + + private String name; +}