diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java b/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java index 3d89ca5b5..5be1d1015 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java @@ -51,6 +51,7 @@ public abstract class InternalArangoCollection extends ArangoExecuteable { private static final String MERGE_OBJECTS = "mergeObjects"; private static final String KEEP_NULL = "keepNull"; private static final String REFILL_INDEX_CACHES = "refillIndexCaches"; + private static final String VERSION_ATTRIBUTE = "versionAttribute"; private static final String IGNORE_REVS = "ignoreRevs"; private static final String RETURN_NEW = "returnNew"; private static final String RETURN_OLD = "returnOld"; @@ -103,6 +104,7 @@ private InternalRequest createInsertDocumentRequest(final DocumentCreateOptions request.putQueryParam(MERGE_OBJECTS, params.getMergeObjects()); request.putQueryParam(KEEP_NULL, params.getKeepNull()); request.putQueryParam(REFILL_INDEX_CACHES, params.getRefillIndexCaches()); + request.putQueryParam(VERSION_ATTRIBUTE, params.getVersionAttribute()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); return request; } @@ -241,6 +243,7 @@ private InternalRequest createReplaceDocumentRequest(final DocumentReplaceOption request.putQueryParam(RETURN_OLD, params.getReturnOld()); request.putQueryParam(SILENT, params.getSilent()); request.putQueryParam(REFILL_INDEX_CACHES, params.getRefillIndexCaches()); + request.putQueryParam(VERSION_ATTRIBUTE, params.getVersionAttribute()); return request; } @@ -303,6 +306,7 @@ private InternalRequest createUpdateDocumentRequest(final DocumentUpdateOptions request.putQueryParam(RETURN_OLD, params.getReturnOld()); request.putQueryParam(SILENT, params.getSilent()); request.putQueryParam(REFILL_INDEX_CACHES, params.getRefillIndexCaches()); + request.putQueryParam(VERSION_ATTRIBUTE, params.getVersionAttribute()); return request; } diff --git a/core/src/main/java/com/arangodb/model/DocumentCreateOptions.java b/core/src/main/java/com/arangodb/model/DocumentCreateOptions.java index 55a9fb9ca..3bac8c242 100644 --- a/core/src/main/java/com/arangodb/model/DocumentCreateOptions.java +++ b/core/src/main/java/com/arangodb/model/DocumentCreateOptions.java @@ -37,6 +37,7 @@ public final class DocumentCreateOptions { private Boolean mergeObjects; private Boolean keepNull; private Boolean refillIndexCaches; + private String versionAttribute; public DocumentCreateOptions() { super(); @@ -177,4 +178,34 @@ public DocumentCreateOptions refillIndexCaches(Boolean refillIndexCaches) { this.refillIndexCaches = refillIndexCaches; return this; } + + public String getVersionAttribute() { + return versionAttribute; + } + + /** + * Only applicable if {@link #overwriteMode(OverwriteMode)} is set to {@link OverwriteMode#update} or + * {@link OverwriteMode#replace}. + * You can use the {@code versionAttribute} option for external versioning support. + * If set, the attribute with the name specified by the option is looked up in the stored document and the attribute + * value is compared numerically to the value of the versioning attribute in the supplied document that is supposed + * to update/replace it. + * If the version number in the new document is higher (rounded down to a whole number) than in the document that + * already exists in the database, then the update/replace operation is performed normally. This is also the case if + * the new versioning attribute has a non-numeric value, if it is a negative number, or if the attribute doesn't + * exist in the supplied or stored document. + * If the version number in the new document is lower or equal to what exists in the database, the operation is not + * performed and the existing document thus not changed. No error is returned in this case. + * The attribute can only be a top-level attribute. + * You can check if _oldRev (if present) and _rev are different to determine if the document has been changed. + * + * @param versionAttribute the attribute name to use for versioning + * @return options + * @since ArangoDB 3.12 + */ + public DocumentCreateOptions versionAttribute(String versionAttribute) { + this.versionAttribute = versionAttribute; + return this; + } + } diff --git a/core/src/main/java/com/arangodb/model/DocumentReplaceOptions.java b/core/src/main/java/com/arangodb/model/DocumentReplaceOptions.java index b8f9fa9a7..98bdf8221 100644 --- a/core/src/main/java/com/arangodb/model/DocumentReplaceOptions.java +++ b/core/src/main/java/com/arangodb/model/DocumentReplaceOptions.java @@ -36,6 +36,7 @@ public final class DocumentReplaceOptions { private Boolean silent; private String streamTransactionId; private Boolean refillIndexCaches; + private String versionAttribute; public DocumentReplaceOptions() { super(); @@ -154,4 +155,32 @@ public DocumentReplaceOptions refillIndexCaches(Boolean refillIndexCaches) { this.refillIndexCaches = refillIndexCaches; return this; } + + public String getVersionAttribute() { + return versionAttribute; + } + + /** + * You can use the {@code versionAttribute} option for external versioning support. + * If set, the attribute with the name specified by the option is looked up in the stored document and the attribute + * value is compared numerically to the value of the versioning attribute in the supplied document that is supposed + * to update/replace it. + * If the version number in the new document is higher (rounded down to a whole number) than in the document that + * already exists in the database, then the update/replace operation is performed normally. This is also the case if + * the new versioning attribute has a non-numeric value, if it is a negative number, or if the attribute doesn't + * exist in the supplied or stored document. + * If the version number in the new document is lower or equal to what exists in the database, the operation is not + * performed and the existing document thus not changed. No error is returned in this case. + * The attribute can only be a top-level attribute. + * You can check if _oldRev (if present) and _rev are different to determine if the document has been changed. + * + * @param versionAttribute the attribute name to use for versioning + * @return options + * @since ArangoDB 3.12 + */ + public DocumentReplaceOptions versionAttribute(String versionAttribute) { + this.versionAttribute = versionAttribute; + return this; + } + } diff --git a/core/src/main/java/com/arangodb/model/DocumentUpdateOptions.java b/core/src/main/java/com/arangodb/model/DocumentUpdateOptions.java index 0b7d73126..b9166c6b9 100644 --- a/core/src/main/java/com/arangodb/model/DocumentUpdateOptions.java +++ b/core/src/main/java/com/arangodb/model/DocumentUpdateOptions.java @@ -38,6 +38,7 @@ public final class DocumentUpdateOptions { private Boolean silent; private String streamTransactionId; private Boolean refillIndexCaches; + private String versionAttribute; public DocumentUpdateOptions() { super(); @@ -190,4 +191,31 @@ public DocumentUpdateOptions refillIndexCaches(Boolean refillIndexCaches) { return this; } + public String getVersionAttribute() { + return versionAttribute; + } + + /** + * You can use the {@code versionAttribute} option for external versioning support. + * If set, the attribute with the name specified by the option is looked up in the stored document and the attribute + * value is compared numerically to the value of the versioning attribute in the supplied document that is supposed + * to update/replace it. + * If the version number in the new document is higher (rounded down to a whole number) than in the document that + * already exists in the database, then the update/replace operation is performed normally. This is also the case if + * the new versioning attribute has a non-numeric value, if it is a negative number, or if the attribute doesn't + * exist in the supplied or stored document. + * If the version number in the new document is lower or equal to what exists in the database, the operation is not + * performed and the existing document thus not changed. No error is returned in this case. + * The attribute can only be a top-level attribute. + * You can check if _oldRev (if present) and _rev are different to determine if the document has been changed. + * + * @param versionAttribute the attribute name to use for versioning + * @return options + * @since ArangoDB 3.12 + */ + public DocumentUpdateOptions versionAttribute(String versionAttribute) { + this.versionAttribute = versionAttribute; + return this; + } + } diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java index e2c3ca18d..4628699bc 100644 --- a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java @@ -310,6 +310,200 @@ void insertDocumentOverwriteModeUpdateKeepNullFalse(ArangoCollectionAsync collec assertThat(updated.getProperties()).doesNotContainKey("foo"); } + @ParameterizedTest + @MethodSource("asyncCols") + void insertDocumentOverwriteModeUpdateWithExternalVersioning(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc).get(); + doc.addAttribute("_version", 2); + DocumentCreateEntity updateResult = collection.insertDocument( + doc, + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .versionAttribute("_version") + .returnNew(true) + ).get(); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(2); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void insertDocumentOverwriteModeUpdateWithExternalVersioningFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc).get(); + doc.addAttribute("_version", 0); + DocumentCreateEntity updateResult = collection.insertDocument( + doc, + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .versionAttribute("_version") + .returnNew(true) + ).get(); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(1); + + } + + @ParameterizedTest + @MethodSource("asyncCols") + void insertDocumentsOverwriteModeUpdateWithExternalVersioning(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)).get(); + + d1.addAttribute("_version", 2); + d2.addAttribute("_version", 2); + MultiDocumentEntity> updateResult = collection.insertDocuments( + Arrays.asList(d1, d2), + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .versionAttribute("_version") + .returnNew(true), + BaseDocument.class + ).get(); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(2); + }); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void insertDocumentsOverwriteModeUpdateWithExternalVersioningFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)).get(); + + d1.addAttribute("_version", 0); + d2.addAttribute("_version", 0); + MultiDocumentEntity> updateResult = collection.insertDocuments( + Arrays.asList(d1, d2), + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .versionAttribute("_version") + .returnNew(true), + BaseDocument.class + ).get(); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(1); + }); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void insertDocumentOverwriteModeReplaceWithExternalVersioning(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc).get(); + doc.addAttribute("_version", 2); + DocumentCreateEntity updateResult = collection.insertDocument( + doc, + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.replace) + .versionAttribute("_version") + .returnNew(true) + ).get(); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(2); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void insertDocumentOverwriteModeReplaceUpdateWithExternalVersioningFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc).get(); + doc.addAttribute("_version", 0); + DocumentCreateEntity updateResult = collection.insertDocument( + doc, + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.replace) + .versionAttribute("_version") + .returnNew(true) + ).get(); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(1); + + } + + @ParameterizedTest + @MethodSource("asyncCols") + void insertDocumentsOverwriteModeReplaceWithExternalVersioning(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)).get(); + + d1.addAttribute("_version", 2); + d2.addAttribute("_version", 2); + MultiDocumentEntity> updateResult = collection.insertDocuments( + Arrays.asList(d1, d2), + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.replace) + .versionAttribute("_version") + .returnNew(true), + BaseDocument.class + ).get(); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(2); + }); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void insertDocumentsOverwriteModeReplaceWithExternalVersioningFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)).get(); + + d1.addAttribute("_version", 0); + d2.addAttribute("_version", 0); + MultiDocumentEntity> updateResult = collection.insertDocuments( + Arrays.asList(d1, d2), + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.replace) + .versionAttribute("_version") + .returnNew(true), + BaseDocument.class + ).get(); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(1); + }); + } + @ParameterizedTest @MethodSource("asyncCols") void insertDocumentWaitForSync(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { @@ -704,6 +898,92 @@ void updateDocumentIfMatchFail(ArangoCollectionAsync collection) throws Executio assertThat(thrown).isInstanceOf(ArangoDBException.class); } + @ParameterizedTest + @MethodSource("asyncCols") + void updateDocumentWithExternalVersioning(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc).get(); + doc.addAttribute("_version", 2); + DocumentUpdateEntity updateResult = collection.updateDocument( + doc.getKey(), + doc, + new DocumentUpdateOptions().versionAttribute("_version").returnNew(true) + ).get(); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(2); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void updateDocumentWithExternalVersioningFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc).get(); + doc.addAttribute("_version", 0); + DocumentUpdateEntity updateResult = collection.updateDocument( + doc.getKey(), + doc, + new DocumentUpdateOptions().versionAttribute("_version").returnNew(true) + ).get(); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(1); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void updateDocumentsWithExternalVersioning(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)).get(); + + d1.addAttribute("_version", 2); + d2.addAttribute("_version", 2); + MultiDocumentEntity> updateResult = collection.updateDocuments( + Arrays.asList(d1, d2), + new DocumentUpdateOptions().versionAttribute("_version").returnNew(true), + BaseDocument.class + ).get(); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(2); + }); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void updateDocumentsWithExternalVersioningFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)).get(); + + d1.addAttribute("_version", 0); + d2.addAttribute("_version", 0); + MultiDocumentEntity> updateResult = collection.updateDocuments( + Arrays.asList(d1, d2), + new DocumentUpdateOptions().versionAttribute("_version").returnNew(true), + BaseDocument.class + ).get(); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(1); + }); + } + @ParameterizedTest @MethodSource("asyncCols") void updateDocumentReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { @@ -1077,6 +1357,92 @@ void replaceDocumentIgnoreRevsFalse(ArangoCollectionAsync collection) throws Exe assertThat(thrown).isInstanceOf(ArangoDBException.class); } + @ParameterizedTest + @MethodSource("asyncCols") + void replaceDocumentWithExternalVersioning(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc).get(); + doc.addAttribute("_version", 2); + DocumentUpdateEntity replaceResult = collection.replaceDocument( + doc.getKey(), + doc, + new DocumentReplaceOptions().versionAttribute("_version").returnNew(true) + ).get(); + assertThat(replaceResult.getNew().getAttribute("_version")).isEqualTo(2); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void replaceDocumentWithExternalVersioningFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc).get(); + doc.addAttribute("_version", 0); + DocumentUpdateEntity replaceResult = collection.replaceDocument( + doc.getKey(), + doc, + new DocumentReplaceOptions().versionAttribute("_version").returnNew(true) + ).get(); + assertThat(replaceResult.getNew().getAttribute("_version")).isEqualTo(1); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void replaceDocumentsWithExternalVersioning(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)).get(); + + d1.addAttribute("_version", 2); + d2.addAttribute("_version", 2); + MultiDocumentEntity> replaceResult = collection.replaceDocuments( + Arrays.asList(d1, d2), + new DocumentReplaceOptions().versionAttribute("_version").returnNew(true), + BaseDocument.class + ).get(); + + assertThat(replaceResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(2); + }); + } + + @ParameterizedTest + @MethodSource("asyncCols") + void replaceDocumentsWithExternalVersioningFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)).get(); + + d1.addAttribute("_version", 0); + d2.addAttribute("_version", 0); + MultiDocumentEntity> replaceResult = collection.replaceDocuments( + Arrays.asList(d1, d2), + new DocumentReplaceOptions().versionAttribute("_version").returnNew(true), + BaseDocument.class + ).get(); + + assertThat(replaceResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(1); + }); + } + @ParameterizedTest @MethodSource("asyncCols") void replaceDocumentReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionTest.java index 8d1087992..35d6ce98d 100644 --- a/driver/src/test/java/com/arangodb/ArangoCollectionTest.java +++ b/driver/src/test/java/com/arangodb/ArangoCollectionTest.java @@ -40,6 +40,7 @@ import org.junit.jupiter.params.provider.MethodSource; import java.util.*; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -313,6 +314,200 @@ void insertDocumentOverwriteModeUpdateKeepNullFalse(ArangoCollection collection) assertThat(updated.getProperties()).doesNotContainKey("foo"); } + @ParameterizedTest + @MethodSource("cols") + void insertDocumentOverwriteModeUpdateWithExternalVersioning(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc); + doc.addAttribute("_version", 2); + DocumentCreateEntity updateResult = collection.insertDocument( + doc, + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .versionAttribute("_version") + .returnNew(true) + ); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(2); + } + + @ParameterizedTest + @MethodSource("cols") + void insertDocumentOverwriteModeUpdateWithExternalVersioningFail(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc); + doc.addAttribute("_version", 0); + DocumentCreateEntity updateResult = collection.insertDocument( + doc, + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .versionAttribute("_version") + .returnNew(true) + ); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(1); + + } + + @ParameterizedTest + @MethodSource("cols") + void insertDocumentsOverwriteModeUpdateWithExternalVersioning(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)); + + d1.addAttribute("_version", 2); + d2.addAttribute("_version", 2); + MultiDocumentEntity> updateResult = collection.insertDocuments( + Arrays.asList(d1, d2), + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .versionAttribute("_version") + .returnNew(true), + BaseDocument.class + ); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(2); + }); + } + + @ParameterizedTest + @MethodSource("cols") + void insertDocumentsOverwriteModeUpdateWithExternalVersioningFail(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)); + + d1.addAttribute("_version", 0); + d2.addAttribute("_version", 0); + MultiDocumentEntity> updateResult = collection.insertDocuments( + Arrays.asList(d1, d2), + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .versionAttribute("_version") + .returnNew(true), + BaseDocument.class + ); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(1); + }); + } + + @ParameterizedTest + @MethodSource("cols") + void insertDocumentOverwriteModeReplaceWithExternalVersioning(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc); + doc.addAttribute("_version", 2); + DocumentCreateEntity updateResult = collection.insertDocument( + doc, + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.replace) + .versionAttribute("_version") + .returnNew(true) + ); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(2); + } + + @ParameterizedTest + @MethodSource("cols") + void insertDocumentOverwriteModeReplaceUpdateWithExternalVersioningFail(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc); + doc.addAttribute("_version", 0); + DocumentCreateEntity updateResult = collection.insertDocument( + doc, + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.replace) + .versionAttribute("_version") + .returnNew(true) + ); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(1); + + } + + @ParameterizedTest + @MethodSource("cols") + void insertDocumentsOverwriteModeReplaceWithExternalVersioning(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)); + + d1.addAttribute("_version", 2); + d2.addAttribute("_version", 2); + MultiDocumentEntity> updateResult = collection.insertDocuments( + Arrays.asList(d1, d2), + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.replace) + .versionAttribute("_version") + .returnNew(true), + BaseDocument.class + ); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(2); + }); + } + + @ParameterizedTest + @MethodSource("cols") + void insertDocumentsOverwriteModeReplaceWithExternalVersioningFail(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)); + + d1.addAttribute("_version", 0); + d2.addAttribute("_version", 0); + MultiDocumentEntity> updateResult = collection.insertDocuments( + Arrays.asList(d1, d2), + new DocumentCreateOptions() + .overwriteMode(OverwriteMode.replace) + .versionAttribute("_version") + .returnNew(true), + BaseDocument.class + ); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(1); + }); + } + @ParameterizedTest @MethodSource("cols") void insertDocumentWaitForSync(ArangoCollection collection) { @@ -729,6 +924,92 @@ void updateDocumentIfMatchFail(ArangoCollection collection) { assertThat(thrown).isInstanceOf(ArangoDBException.class); } + @ParameterizedTest + @MethodSource("cols") + void updateDocumentWithExternalVersioning(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc); + doc.addAttribute("_version", 2); + DocumentUpdateEntity updateResult = collection.updateDocument( + doc.getKey(), + doc, + new DocumentUpdateOptions().versionAttribute("_version").returnNew(true) + ); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(2); + } + + @ParameterizedTest + @MethodSource("cols") + void updateDocumentWithExternalVersioningFail(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc); + doc.addAttribute("_version", 0); + DocumentUpdateEntity updateResult = collection.updateDocument( + doc.getKey(), + doc, + new DocumentUpdateOptions().versionAttribute("_version").returnNew(true) + ); + assertThat(updateResult.getNew().getAttribute("_version")).isEqualTo(1); + } + + @ParameterizedTest + @MethodSource("cols") + void updateDocumentsWithExternalVersioning(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)); + + d1.addAttribute("_version", 2); + d2.addAttribute("_version", 2); + MultiDocumentEntity> updateResult = collection.updateDocuments( + Arrays.asList(d1, d2), + new DocumentUpdateOptions().versionAttribute("_version").returnNew(true), + BaseDocument.class + ); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(2); + }); + } + + @ParameterizedTest + @MethodSource("cols") + void updateDocumentsWithExternalVersioningFail(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)); + + d1.addAttribute("_version", 0); + d2.addAttribute("_version", 0); + MultiDocumentEntity> updateResult = collection.updateDocuments( + Arrays.asList(d1, d2), + new DocumentUpdateOptions().versionAttribute("_version").returnNew(true), + BaseDocument.class + ); + + assertThat(updateResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(1); + }); + } + @ParameterizedTest @MethodSource("cols") void updateDocumentReturnNew(ArangoCollection collection) { @@ -1102,6 +1383,92 @@ void replaceDocumentIgnoreRevsFalse(ArangoCollection collection) { assertThat(thrown).isInstanceOf(ArangoDBException.class); } + @ParameterizedTest + @MethodSource("cols") + void replaceDocumentWithExternalVersioning(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc); + doc.addAttribute("_version", 2); + DocumentUpdateEntity replaceResult = collection.replaceDocument( + doc.getKey(), + doc, + new DocumentReplaceOptions().versionAttribute("_version").returnNew(true) + ); + assertThat(replaceResult.getNew().getAttribute("_version")).isEqualTo(2); + } + + @ParameterizedTest + @MethodSource("cols") + void replaceDocumentWithExternalVersioningFail(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("_version", 1); + collection.insertDocument(doc); + doc.addAttribute("_version", 0); + DocumentUpdateEntity replaceResult = collection.replaceDocument( + doc.getKey(), + doc, + new DocumentReplaceOptions().versionAttribute("_version").returnNew(true) + ); + assertThat(replaceResult.getNew().getAttribute("_version")).isEqualTo(1); + } + + @ParameterizedTest + @MethodSource("cols") + void replaceDocumentsWithExternalVersioning(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)); + + d1.addAttribute("_version", 2); + d2.addAttribute("_version", 2); + MultiDocumentEntity> replaceResult = collection.replaceDocuments( + Arrays.asList(d1, d2), + new DocumentReplaceOptions().versionAttribute("_version").returnNew(true), + BaseDocument.class + ); + + assertThat(replaceResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(2); + }); + } + + @ParameterizedTest + @MethodSource("cols") + void replaceDocumentsWithExternalVersioningFail(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 12)); + + BaseDocument d1 = new BaseDocument(UUID.randomUUID().toString()); + d1.addAttribute("_version", 1); + BaseDocument d2 = new BaseDocument(UUID.randomUUID().toString()); + d2.addAttribute("_version", 1); + + collection.insertDocuments(Arrays.asList(d1, d2)); + + d1.addAttribute("_version", 0); + d2.addAttribute("_version", 0); + MultiDocumentEntity> replaceResult = collection.replaceDocuments( + Arrays.asList(d1, d2), + new DocumentReplaceOptions().versionAttribute("_version").returnNew(true), + BaseDocument.class + ); + + assertThat(replaceResult.getDocuments()).allSatisfy(it -> { + assertThat(it.getNew()).isNotNull(); + assertThat(it.getNew().getAttribute("_version")).isEqualTo(1); + }); + } + @ParameterizedTest @MethodSource("cols") void replaceDocumentReturnNew(ArangoCollection collection) {