diff --git a/ChangeLog.md b/ChangeLog.md index d866472bd..40bf7228d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ### Added - added support for named indices +- added support for TTL indices - added minReplicationAttribute for collections and graphs - added batched thread support in ArangoCollection.importDocuments (by @rkhaja) diff --git a/src/main/java/com/arangodb/ArangoCollection.java b/src/main/java/com/arangodb/ArangoCollection.java index b00f8b6dc..7ece5889d 100644 --- a/src/main/java/com/arangodb/ArangoCollection.java +++ b/src/main/java/com/arangodb/ArangoCollection.java @@ -32,20 +32,7 @@ import com.arangodb.entity.IndexEntity; import com.arangodb.entity.MultiDocumentEntity; import com.arangodb.entity.Permissions; -import com.arangodb.model.CollectionCreateOptions; -import com.arangodb.model.CollectionPropertiesOptions; -import com.arangodb.model.DocumentCreateOptions; -import com.arangodb.model.DocumentDeleteOptions; -import com.arangodb.model.DocumentExistsOptions; -import com.arangodb.model.DocumentImportOptions; -import com.arangodb.model.DocumentReadOptions; -import com.arangodb.model.DocumentReplaceOptions; -import com.arangodb.model.DocumentUpdateOptions; -import com.arangodb.model.FulltextIndexOptions; -import com.arangodb.model.GeoIndexOptions; -import com.arangodb.model.HashIndexOptions; -import com.arangodb.model.PersistentIndexOptions; -import com.arangodb.model.SkiplistIndexOptions; +import com.arangodb.model.*; /** * Interface for operations on ArangoDB collection level. @@ -53,6 +40,7 @@ * @see Collection API Documentation * @see Documents API Documentation * @author Mark Vollmary + * @author Heiko Kernbach */ public interface ArangoCollection extends ArangoSerializationAccessor { @@ -561,6 +549,20 @@ MultiDocumentEntity> deleteDocuments( */ IndexEntity ensureFulltextIndex(Iterable fields, FulltextIndexOptions options) throws ArangoDBException; + /** + * Creates a ttl index for the collection, if it does not already exist. + * + * @see API + * Documentation + * @param fields + * A list of attribute paths + * @param options + * Additional options, can be null + * @return information about the index + * @throws ArangoDBException + */ + IndexEntity ensureTtlIndex(Iterable fields, TtlIndexOptions options) throws ArangoDBException; + /** * Fetches a list of all indexes on this collection. * diff --git a/src/main/java/com/arangodb/entity/IndexEntity.java b/src/main/java/com/arangodb/entity/IndexEntity.java index 0a11fbe06..201396e09 100644 --- a/src/main/java/com/arangodb/entity/IndexEntity.java +++ b/src/main/java/com/arangodb/entity/IndexEntity.java @@ -40,6 +40,7 @@ public class IndexEntity implements Entity { private Boolean geoJson; private Boolean constraint; private Boolean deduplicate; + private Integer expireAfter; private Boolean inBackground; public IndexEntity() { @@ -90,6 +91,10 @@ public Boolean getGeoJson() { return geoJson; } + public Integer getExpireAfter() { + return expireAfter; + } + public Boolean getConstraint() { return constraint; } diff --git a/src/main/java/com/arangodb/entity/IndexType.java b/src/main/java/com/arangodb/entity/IndexType.java index b08f0a3d5..1507ec3d8 100644 --- a/src/main/java/com/arangodb/entity/IndexType.java +++ b/src/main/java/com/arangodb/entity/IndexType.java @@ -22,8 +22,9 @@ /** * @author Mark Vollmary + * @author Heiko Kernbach * */ public enum IndexType { - primary, hash, skiplist, persistent, geo, geo1, geo2, fulltext, edge + primary, hash, skiplist, persistent, geo, geo1, geo2, fulltext, edge, ttl } diff --git a/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java b/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java index 4fda706c6..690e8f43f 100644 --- a/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java +++ b/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java @@ -32,6 +32,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.arangodb.model.*; import com.arangodb.ArangoCollection; import com.arangodb.ArangoDBException; import com.arangodb.entity.CollectionEntity; @@ -45,20 +46,6 @@ import com.arangodb.entity.MultiDocumentEntity; import com.arangodb.entity.Permissions; import com.arangodb.internal.util.DocumentUtil; -import com.arangodb.model.CollectionCreateOptions; -import com.arangodb.model.CollectionPropertiesOptions; -import com.arangodb.model.DocumentCreateOptions; -import com.arangodb.model.DocumentDeleteOptions; -import com.arangodb.model.DocumentExistsOptions; -import com.arangodb.model.DocumentImportOptions; -import com.arangodb.model.DocumentReadOptions; -import com.arangodb.model.DocumentReplaceOptions; -import com.arangodb.model.DocumentUpdateOptions; -import com.arangodb.model.FulltextIndexOptions; -import com.arangodb.model.GeoIndexOptions; -import com.arangodb.model.HashIndexOptions; -import com.arangodb.model.PersistentIndexOptions; -import com.arangodb.model.SkiplistIndexOptions; import com.arangodb.velocypack.VPackSlice; /** @@ -131,10 +118,12 @@ public Collection importDocuments(Collection values, Do try { documentImportEntity = completableFuture.get(); } catch (InterruptedException | ExecutionException e) { + executorService.shutdown(); throw new ArangoDBException(e); } documentImportEntityList.add(documentImportEntity); } + executorService.shutdown(); return documentImportEntityList; } @@ -334,6 +323,12 @@ public IndexEntity ensureFulltextIndex(final Iterable fields, final Full return executor.execute(createFulltextIndexRequest(fields, options), IndexEntity.class); } + @Override + public IndexEntity ensureTtlIndex(final Iterable fields, final TtlIndexOptions options) + throws ArangoDBException { + return executor.execute(createTtlIndexRequest(fields, options), IndexEntity.class); + } + @Override public Collection getIndexes() throws ArangoDBException { return executor.execute(getIndexesRequest(), getIndexesResponseDeserializer()); diff --git a/src/main/java/com/arangodb/internal/InternalArangoCollection.java b/src/main/java/com/arangodb/internal/InternalArangoCollection.java index 1aee8ace9..310cc023a 100644 --- a/src/main/java/com/arangodb/internal/InternalArangoCollection.java +++ b/src/main/java/com/arangodb/internal/InternalArangoCollection.java @@ -39,23 +39,7 @@ import com.arangodb.internal.util.ArangoSerializationFactory.Serializer; import com.arangodb.internal.util.DocumentUtil; import com.arangodb.internal.util.RequestUtils; -import com.arangodb.model.CollectionPropertiesOptions; -import com.arangodb.model.CollectionRenameOptions; -import com.arangodb.model.DocumentCreateOptions; -import com.arangodb.model.DocumentDeleteOptions; -import com.arangodb.model.DocumentExistsOptions; -import com.arangodb.model.DocumentImportOptions; -import com.arangodb.model.DocumentReadOptions; -import com.arangodb.model.DocumentReplaceOptions; -import com.arangodb.model.DocumentUpdateOptions; -import com.arangodb.model.FulltextIndexOptions; -import com.arangodb.model.GeoIndexOptions; -import com.arangodb.model.HashIndexOptions; -import com.arangodb.model.ImportType; -import com.arangodb.model.OptionsBuilder; -import com.arangodb.model.PersistentIndexOptions; -import com.arangodb.model.SkiplistIndexOptions; -import com.arangodb.model.UserAccessOptions; +import com.arangodb.model.*; import com.arangodb.util.ArangoSerializer; import com.arangodb.velocypack.Type; import com.arangodb.velocypack.VPackSlice; @@ -654,6 +638,14 @@ protected Request createFulltextIndexRequest(final Iterable fields, fina return request; } + protected Request createTtlIndexRequest(final Iterable fields, final TtlIndexOptions options) { + final Request request = request(db.name(), RequestType.POST, PATH_API_INDEX); + request.putQueryParam(COLLECTION, name); + request.setBody( + util().serialize(OptionsBuilder.build(options != null ? options : new TtlIndexOptions(), fields))); + return request; + } + protected Request getIndexesRequest() { final Request request = request(db.name(), RequestType.GET, PATH_API_INDEX); request.putQueryParam(COLLECTION, name); diff --git a/src/main/java/com/arangodb/model/OptionsBuilder.java b/src/main/java/com/arangodb/model/OptionsBuilder.java index 5c348cfb7..7c5e92e58 100644 --- a/src/main/java/com/arangodb/model/OptionsBuilder.java +++ b/src/main/java/com/arangodb/model/OptionsBuilder.java @@ -62,6 +62,10 @@ public static FulltextIndexOptions build(final FulltextIndexOptions options, fin return options.fields(fields); } + public static TtlIndexOptions build(final TtlIndexOptions options, final Iterable fields) { + return options.fields(fields); + } + public static CollectionCreateOptions build(final CollectionCreateOptions options, final String name) { return options.name(name); } diff --git a/src/main/java/com/arangodb/model/TtlIndexOptions.java b/src/main/java/com/arangodb/model/TtlIndexOptions.java new file mode 100644 index 000000000..45a8ff3e9 --- /dev/null +++ b/src/main/java/com/arangodb/model/TtlIndexOptions.java @@ -0,0 +1,69 @@ +/* + * DISCLAIMER + * + * Copyright 2019 ArangoDB GmbH, Cologne, Germany + * + * 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. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.model; + +import com.arangodb.entity.IndexType; + +/** + * @author Heiko Kernbach + * @see API Documentation + */ +public class TtlIndexOptions extends IndexOptions { + + private Iterable fields; + private final IndexType type = IndexType.ttl; + private Integer expireAfter; + + public TtlIndexOptions() { + super(); + } + + protected Iterable getFields() { + return fields; + } + + /** + * @param fields A list of attribute paths + * @return options + */ + protected TtlIndexOptions fields(final Iterable fields) { + this.fields = fields; + return this; + } + + protected IndexType getType() { + return type; + } + + /** + * @param expireAfter The time (in seconds) after a document’s creation after which the documents count as “expired”. + * @return options + */ + public TtlIndexOptions expireAfter(final Integer expireAfter) { + this.expireAfter = expireAfter; + return this; + } + + protected Integer getExpireAfter() { + return expireAfter; + } + +} diff --git a/src/test/java/com/arangodb/ArangoCollectionTest.java b/src/test/java/com/arangodb/ArangoCollectionTest.java index e1d3ac565..17908f3d1 100644 --- a/src/test/java/com/arangodb/ArangoCollectionTest.java +++ b/src/test/java/com/arangodb/ArangoCollectionTest.java @@ -1288,6 +1288,44 @@ public void createFulltextIndexWithOptions() { assertThat(indexResult.getName(), is("myFulltextIndex")); } + @Test + public void createTtlIndexWithoutOptions() { + if (!requireVersion(3, 5)) { + return; + } + final Collection fields = new ArrayList(); + fields.add("a"); + try { + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureTtlIndex(fields, null); + } catch (final ArangoDBException e) { + assertThat(e.getResponseCode(), is(400)); + assertThat(e.getErrorNum(), is(10)); + assertThat(e.getMessage(), containsString("expireAfter attribute must be a number")); + } + } + + @Test + public void createTtlIndexWithOptions() { + if (!requireVersion(3, 5)) { + return; + } + final Collection fields = new ArrayList(); + fields.add("a"); + + final TtlIndexOptions options = new TtlIndexOptions(); + options.name("myTtlIndex"); + options.expireAfter(3600); + + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureTtlIndex(fields, options); + assertThat(indexResult, is(notNullValue())); + assertThat(indexResult.getFields(), hasItem("a")); + assertThat(indexResult.getId(), startsWith(COLLECTION_NAME)); + assertThat(indexResult.getIsNewlyCreated(), is(true)); + assertThat(indexResult.getType(), is(IndexType.ttl)); + assertThat(indexResult.getExpireAfter(), is(3600)); + assertThat(indexResult.getName(), is("myTtlIndex")); + } + @Test public void getIndexes() { final Collection fields = new ArrayList(); diff --git a/src/test/java/com/arangodb/ArangoDBTest.java b/src/test/java/com/arangodb/ArangoDBTest.java index 0a243656c..49cfbf906 100644 --- a/src/test/java/com/arangodb/ArangoDBTest.java +++ b/src/test/java/com/arangodb/ArangoDBTest.java @@ -441,17 +441,20 @@ public void loadproperties2() { @Test public void accessMultipleDatabases() { + String db1 = "multipledb1"; + String db2 = "multipledb2"; + try { - arangoDB.createDatabase("db1"); - arangoDB.createDatabase("db2"); + arangoDB.createDatabase(db1); + arangoDB.createDatabase(db2); - final ArangoDBVersion version1 = arangoDB.db("db1").getVersion(); + final ArangoDBVersion version1 = arangoDB.db(db1).getVersion(); assertThat(version1, is(notNullValue())); - final ArangoDBVersion version2 = arangoDB.db("db2").getVersion(); + final ArangoDBVersion version2 = arangoDB.db(db2).getVersion(); assertThat(version2, is(notNullValue())); } finally { - arangoDB.db("db1").drop(); - arangoDB.db("db2").drop(); + arangoDB.db(db1).drop(); + arangoDB.db(db2).drop(); } }