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/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index 7d4152af7..a1a055085 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.*; @@ -52,7 +51,6 @@ import org.elasticsearch.action.get.MultiGetResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.search.SearchScrollRequestBuilder; import org.elasticsearch.action.update.UpdateRequestBuilder; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.Client; @@ -64,6 +62,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; @@ -135,6 +134,7 @@ import org.springframework.util.StringUtils; * @author Sascha Woo * @author Ted Liang * @author Jean-Baptiste Nizet + * @author Ivan Greene */ public class ElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware { @@ -1107,7 +1107,8 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati } if (query.getVersion() != null) { indexRequestBuilder.setVersion(query.getVersion()); - indexRequestBuilder.setVersionType(EXTERNAL); + VersionType versionType = retrieveVersionTypeFromPersistentEntity(query.getObject().getClass()); + indexRequestBuilder.setVersionType(versionType); } if (query.getParentId() != null) { @@ -1220,6 +1221,13 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati 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)) { @@ -145,6 +148,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/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java index a607c2394..a52a9065a 100755 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java @@ -52,6 +52,8 @@ import org.springframework.data.elasticsearch.annotations.Document; 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; @@ -78,6 +80,7 @@ import static org.springframework.data.elasticsearch.utils.IndexBuilder.*; * @author Alen Turkovic * @author Sascha Woo * @author Jean-Baptiste Nizet + * @author Ivan Greene */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:elasticsearch-template-test.xml") @@ -1767,6 +1770,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; +}