diff --git a/src/main/java/com/arangodb/ArangoCollection.java b/src/main/java/com/arangodb/ArangoCollection.java index f74dd4681..b9e885a2f 100644 --- a/src/main/java/com/arangodb/ArangoCollection.java +++ b/src/main/java/com/arangodb/ArangoCollection.java @@ -439,6 +439,9 @@ MultiDocumentEntity> deleteDocuments( /** * Fetches information about the index with the given {@code id} and returns it. + *
+ * Note: inverted indexes are not returned by this method. Use + * {@link ArangoCollection#getInvertedIndex(String)} instead. * * @param id The index-handle * @return information about the index @@ -447,6 +450,17 @@ MultiDocumentEntity> deleteDocuments( */ IndexEntity getIndex(String id) throws ArangoDBException; + /** + * Fetches information about the inverted index with the given {@code id} and returns it. + * + * @param id The index-handle + * @return information about the index + * @throws ArangoDBException + * @see API Documentation + * @since ArangoDB 3.10 + */ + InvertedIndexEntity getInvertedIndex(String id) throws ArangoDBException; + /** * Deletes the index with the given {@code id} from the collection. * @@ -519,7 +533,7 @@ MultiDocumentEntity> deleteDocuments( * @throws ArangoDBException * @see API * Documentation - * @deprecated since ArangoDB 3.10, use ArangoSearch view instead. + * @deprecated since ArangoDB 3.10, use ArangoSearch or Inverted indexes instead. */ @Deprecated IndexEntity ensureFulltextIndex(Iterable fields, FulltextIndexOptions options) throws ArangoDBException; @@ -549,8 +563,22 @@ MultiDocumentEntity> deleteDocuments( */ IndexEntity ensureZKDIndex(Iterable fields, ZKDIndexOptions options) throws ArangoDBException; + /** + * Creates an inverted index for the collection, if it does not already exist. + * + * @param options index creation options + * @return information about the index + * @throws ArangoDBException + * @see API Documentation + * @since ArangoDB 3.10 + */ + InvertedIndexEntity ensureInvertedIndex(InvertedIndexOptions options) throws ArangoDBException; + /** * Fetches a list of all indexes on this collection. + *
+ * Note: inverted indexes are not returned by this method. Use + * {@link ArangoCollection#getInvertedIndexes()} instead. * * @return information about the indexes * @throws ArangoDBException @@ -560,6 +588,18 @@ MultiDocumentEntity> deleteDocuments( */ Collection getIndexes() throws ArangoDBException; + /** + * Fetches a list of all inverted indexes on this collection. + * + * @return information about the indexes + * @throws ArangoDBException + * @see API + * Documentation + * @since ArangoDB 3.10 + */ + Collection getInvertedIndexes() throws ArangoDBException; + /** * Checks whether the collection exists * diff --git a/src/main/java/com/arangodb/async/ArangoCollectionAsync.java b/src/main/java/com/arangodb/async/ArangoCollectionAsync.java index 4e39bb6b1..977765409 100644 --- a/src/main/java/com/arangodb/async/ArangoCollectionAsync.java +++ b/src/main/java/com/arangodb/async/ArangoCollectionAsync.java @@ -20,7 +20,6 @@ package com.arangodb.async; -import com.arangodb.ArangoDBException; import com.arangodb.ArangoSerializationAccessor; import com.arangodb.entity.*; import com.arangodb.model.*; @@ -145,7 +144,7 @@ CompletableFuture importDocuments( * @see API * Documentation */ - CompletableFuture getDocument(final String key, final Class type) throws ArangoDBException; + CompletableFuture getDocument(final String key, final Class type); /** * Reads a single document @@ -157,8 +156,7 @@ CompletableFuture importDocuments( * @see API * Documentation */ - CompletableFuture getDocument(final String key, final Class type, final DocumentReadOptions options) - throws ArangoDBException; + CompletableFuture getDocument(final String key, final Class type, final DocumentReadOptions options); /** * Reads multiple documents @@ -410,6 +408,9 @@ CompletableFuture>> deleteDocume /** * Returns an index + *
+ * Note: inverted indexes are not returned by this method. Use + * {@link ArangoCollectionAsync#getInvertedIndex(String)} instead. * * @param id The index-handle * @return information about the index @@ -417,6 +418,16 @@ CompletableFuture>> deleteDocume */ CompletableFuture getIndex(final String id); + /** + * Returns an inverted index + * + * @param id The index-handle + * @return information about the index + * @see API Documentation + * @since ArangoDB 3.10 + */ + CompletableFuture getInvertedIndex(String id); + /** * Deletes an index * @@ -487,7 +498,7 @@ CompletableFuture ensurePersistentIndex( * @return information about the index * @see API * Documentation - * @deprecated since ArangoDB 3.10, use ArangoSearch view instead. + * @deprecated since ArangoDB 3.10, use ArangoSearch or Inverted indexes instead. */ @Deprecated CompletableFuture ensureFulltextIndex( @@ -517,8 +528,21 @@ CompletableFuture ensureFulltextIndex( */ CompletableFuture ensureZKDIndex(final Iterable fields, final ZKDIndexOptions options); + /** + * Creates an inverted index for the collection, if it does not already exist. + * + * @param options index creation options + * @return information about the index + * @see API Documentation + * @since ArangoDB 3.10 + */ + CompletableFuture ensureInvertedIndex(InvertedIndexOptions options); + /** * Returns all indexes of the collection + *
+ * Note: inverted indexes are not returned by this method. Use + * {@link ArangoCollectionAsync#getInvertedIndexes()} instead. * * @return information about the indexes * @see ensureFulltextIndex( */ CompletableFuture> getIndexes(); + /** + * Fetches a list of all inverted indexes on this collection. + * + * @return information about the indexes + * @see API + * Documentation + * @since ArangoDB 3.10 + */ + CompletableFuture> getInvertedIndexes(); + /** * Checks whether the collection exists * diff --git a/src/main/java/com/arangodb/async/internal/ArangoCollectionAsyncImpl.java b/src/main/java/com/arangodb/async/internal/ArangoCollectionAsyncImpl.java index 108f62f7b..263a1fe78 100644 --- a/src/main/java/com/arangodb/async/internal/ArangoCollectionAsyncImpl.java +++ b/src/main/java/com/arangodb/async/internal/ArangoCollectionAsyncImpl.java @@ -258,6 +258,11 @@ public CompletableFuture getIndex(final String id) { return executor.execute(getIndexRequest(id), IndexEntity.class); } + @Override + public CompletableFuture getInvertedIndex(String id) { + return executor.execute(getIndexRequest(id), InvertedIndexEntity.class); + } + @Override public CompletableFuture deleteIndex(final String id) { return executor.execute(deleteIndexRequest(id), deleteIndexResponseDeserializer()); @@ -311,11 +316,21 @@ public CompletableFuture ensureZKDIndex( return executor.execute(createZKDIndexRequest(fields, options), IndexEntity.class); } + @Override + public CompletableFuture ensureInvertedIndex(InvertedIndexOptions options) { + return executor.execute(createInvertedIndexRequest(options), InvertedIndexEntity.class); + } + @Override public CompletableFuture> getIndexes() { return executor.execute(getIndexesRequest(), getIndexesResponseDeserializer()); } + @Override + public CompletableFuture> getInvertedIndexes() { + return executor.execute(getIndexesRequest(), getInvertedIndexesResponseDeserializer()); + } + @Override public CompletableFuture exists() { return getInfo().thenApply(Objects::nonNull).exceptionally(Objects::isNull); diff --git a/src/main/java/com/arangodb/entity/IndexType.java b/src/main/java/com/arangodb/entity/IndexType.java index 26a048cb0..3fbd025c3 100644 --- a/src/main/java/com/arangodb/entity/IndexType.java +++ b/src/main/java/com/arangodb/entity/IndexType.java @@ -41,7 +41,7 @@ public enum IndexType { geo2, /** - * @deprecated since ArangoDB 3.10, use ArangoSearch view instead. + * @deprecated since ArangoDB 3.10, use ArangoSearch or Inverted indexes instead. */ @Deprecated fulltext, @@ -50,5 +50,10 @@ public enum IndexType { ttl, - zkd + zkd, + + /** + * @since ArangoDB 3.10 + */ + inverted } diff --git a/src/main/java/com/arangodb/entity/InvertedIndexEntity.java b/src/main/java/com/arangodb/entity/InvertedIndexEntity.java new file mode 100644 index 000000000..e252a148c --- /dev/null +++ b/src/main/java/com/arangodb/entity/InvertedIndexEntity.java @@ -0,0 +1,154 @@ +/* + * DISCLAIMER + * + * Copyright 2016 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.entity; + +import com.arangodb.entity.arangosearch.AnalyzerFeature; +import com.arangodb.entity.arangosearch.ConsolidationPolicy; +import com.arangodb.entity.arangosearch.StoredValue; + +import java.util.Collection; +import java.util.Set; + +/** + * TODO: add documentation + * + * @author Michele Rastelli + * @see API Documentation + * @since ArangoDB 3.10 + */ +public class InvertedIndexEntity implements Entity { + + private String id; + private Boolean isNewlyCreated; + private Boolean unique; + private Boolean sparse; + private Long version; + private Integer code; + private IndexType type; + private String name; + private Collection fields; + private Boolean searchField; + private Collection storedValues; + private InvertedIndexPrimarySort primarySort; + private String analyzer; + private Set features; + private Boolean includeAllFields; + private Boolean trackListPositions; + private Long cleanupIntervalStep; + private Long commitIntervalMsec; + private Long consolidationIntervalMsec; + private ConsolidationPolicy consolidationPolicy; + private Long writebufferIdle; + private Long writebufferActive; + private Long writebufferSizeMax; + + public String getId() { + return id; + } + + public Boolean getIsNewlyCreated() { + return isNewlyCreated; + } + + public Boolean getUnique() { + return unique; + } + + public Boolean getSparse() { + return sparse; + } + + public Long getVersion() { + return version; + } + + public Integer getCode() { + return code; + } + + public IndexType getType() { + return type; + } + + public String getName() { + return name; + } + + public Collection getFields() { + return fields; + } + + public Boolean getSearchField() { + return searchField; + } + + public Collection getStoredValues() { + return storedValues; + } + + public InvertedIndexPrimarySort getPrimarySort() { + return primarySort; + } + + public String getAnalyzer() { + return analyzer; + } + + public Set getFeatures() { + return features; + } + + public Boolean getIncludeAllFields() { + return includeAllFields; + } + + public Boolean getTrackListPositions() { + return trackListPositions; + } + + public Long getCleanupIntervalStep() { + return cleanupIntervalStep; + } + + public Long getCommitIntervalMsec() { + return commitIntervalMsec; + } + + public Long getConsolidationIntervalMsec() { + return consolidationIntervalMsec; + } + + public ConsolidationPolicy getConsolidationPolicy() { + return consolidationPolicy; + } + + public Long getWritebufferIdle() { + return writebufferIdle; + } + + public Long getWritebufferActive() { + return writebufferActive; + } + + public Long getWritebufferSizeMax() { + return writebufferSizeMax; + } +} diff --git a/src/main/java/com/arangodb/entity/InvertedIndexField.java b/src/main/java/com/arangodb/entity/InvertedIndexField.java new file mode 100644 index 000000000..92ee15cb1 --- /dev/null +++ b/src/main/java/com/arangodb/entity/InvertedIndexField.java @@ -0,0 +1,98 @@ +package com.arangodb.entity; + +import com.arangodb.entity.arangosearch.AnalyzerFeature; + +import java.util.*; + +/** + * TODO: add documentation + * + * @author Michele Rastelli + * @see API Documentation + * @since ArangoDB 3.10 + */ +public class InvertedIndexField implements Entity { + private String name; + private String analyzer; + private Boolean includeAllFields; + private Boolean searchField; + private Boolean trackListPositions; + private final Set features = new HashSet<>(); + private final Collection nested = new ArrayList<>(); + + public String getName() { + return name; + } + + public InvertedIndexField name(String name) { + this.name = name; + return this; + } + + public String getAnalyzer() { + return analyzer; + } + + public InvertedIndexField analyzer(String analyzer) { + this.analyzer = analyzer; + return this; + } + + public Boolean getIncludeAllFields() { + return includeAllFields; + } + + public InvertedIndexField includeAllFields(Boolean includeAllFields) { + this.includeAllFields = includeAllFields; + return this; + } + + public Boolean getSearchField() { + return searchField; + } + + public InvertedIndexField searchField(Boolean searchField) { + this.searchField = searchField; + return this; + } + + public Boolean getTrackListPositions() { + return trackListPositions; + } + + public InvertedIndexField trackListPositions(Boolean trackListPositions) { + this.trackListPositions = trackListPositions; + return this; + } + + public Set getFeatures() { + return features; + } + + public InvertedIndexField features(AnalyzerFeature... features) { + Collections.addAll(this.features, features); + return this; + } + + public Collection getNested() { + return nested; + } + + public InvertedIndexField nested(InvertedIndexField... nested) { + Collections.addAll(this.nested, nested); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InvertedIndexField that = (InvertedIndexField) o; + return Objects.equals(name, that.name) && Objects.equals(analyzer, that.analyzer) && Objects.equals(includeAllFields, that.includeAllFields) && Objects.equals(searchField, that.searchField) && Objects.equals(trackListPositions, that.trackListPositions) && Objects.equals(features, that.features) && Objects.equals(nested, that.nested); + } + + @Override + public int hashCode() { + return Objects.hash(name, analyzer, includeAllFields, searchField, trackListPositions, features, nested); + } +} diff --git a/src/main/java/com/arangodb/entity/InvertedIndexPrimarySort.java b/src/main/java/com/arangodb/entity/InvertedIndexPrimarySort.java new file mode 100644 index 000000000..3173965d2 --- /dev/null +++ b/src/main/java/com/arangodb/entity/InvertedIndexPrimarySort.java @@ -0,0 +1,81 @@ +package com.arangodb.entity; + +import com.arangodb.entity.arangosearch.ArangoSearchCompression; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * TODO: add documentation + * + * @author Michele Rastelli + * @see API Documentation + * @since ArangoDB 3.10 + */ +public class InvertedIndexPrimarySort implements Entity { + private final List fields = new ArrayList<>(); + private ArangoSearchCompression compression; + + public List getFields() { + return fields; + } + + public InvertedIndexPrimarySort fields(Field... fields) { + Collections.addAll(this.fields, fields); + return this; + } + + public ArangoSearchCompression getCompression() { + return compression; + } + + public InvertedIndexPrimarySort compression(ArangoSearchCompression compression) { + this.compression = compression; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InvertedIndexPrimarySort that = (InvertedIndexPrimarySort) o; + return Objects.equals(fields, that.fields) && compression == that.compression; + } + + @Override + public int hashCode() { + return Objects.hash(fields, compression); + } + + public static class Field { + private final String field; + private final Direction direction; + + public Field(String field, Direction direction) { + this.field = field; + this.direction = direction; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Field field1 = (Field) o; + return Objects.equals(field, field1.field) && direction == field1.direction; + } + + @Override + public int hashCode() { + return Objects.hash(field, direction); + } + + public enum Direction { + asc, + desc + } + + } + +} diff --git a/src/main/java/com/arangodb/entity/arangosearch/AnalyzerFeature.java b/src/main/java/com/arangodb/entity/arangosearch/AnalyzerFeature.java index 8fdf06509..76549f0cb 100644 --- a/src/main/java/com/arangodb/entity/arangosearch/AnalyzerFeature.java +++ b/src/main/java/com/arangodb/entity/arangosearch/AnalyzerFeature.java @@ -37,8 +37,14 @@ public enum AnalyzerFeature { norm, /** - * sequentially increasing term position, required for PHRASE(). If present then the frequency feature is also required + * sequentially increasing term position, required for PHRASE(). If present then the frequency feature is also required. */ - position + position, + + /** + * enable search highlighting capabilities (Enterprise Edition only). If present, then the `position` and `frequency` features are also required. + * @since ArangoDB 3.10 + */ + offset } diff --git a/src/main/java/com/arangodb/entity/arangosearch/ConsolidationPolicy.java b/src/main/java/com/arangodb/entity/arangosearch/ConsolidationPolicy.java index b0cb50a0a..21091a8a6 100644 --- a/src/main/java/com/arangodb/entity/arangosearch/ConsolidationPolicy.java +++ b/src/main/java/com/arangodb/entity/arangosearch/ConsolidationPolicy.java @@ -20,6 +20,8 @@ package com.arangodb.entity.arangosearch; +import java.util.Objects; + /** * @author Mark Vollmary */ @@ -73,4 +75,16 @@ public Long getSegmentThreshold() { return segmentThreshold; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConsolidationPolicy that = (ConsolidationPolicy) o; + return type == that.type && Objects.equals(threshold, that.threshold) && Objects.equals(segmentThreshold, that.segmentThreshold); + } + + @Override + public int hashCode() { + return Objects.hash(type, threshold, segmentThreshold); + } } diff --git a/src/main/java/com/arangodb/entity/arangosearch/StoredValue.java b/src/main/java/com/arangodb/entity/arangosearch/StoredValue.java index 5d3c2bd36..c7809162a 100644 --- a/src/main/java/com/arangodb/entity/arangosearch/StoredValue.java +++ b/src/main/java/com/arangodb/entity/arangosearch/StoredValue.java @@ -22,6 +22,7 @@ import java.util.List; +import java.util.Objects; /** * @author Michele Rastelli @@ -58,4 +59,16 @@ public ArangoSearchCompression getCompression() { return compression; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StoredValue that = (StoredValue) o; + return Objects.equals(fields, that.fields) && compression == that.compression; + } + + @Override + public int hashCode() { + return Objects.hash(fields, compression); + } } diff --git a/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java b/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java index 21aa94475..eab584827 100644 --- a/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java +++ b/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java @@ -254,6 +254,11 @@ public IndexEntity getIndex(final String id) throws ArangoDBException { return executor.execute(getIndexRequest(id), IndexEntity.class); } + @Override + public InvertedIndexEntity getInvertedIndex(String id) throws ArangoDBException { + return executor.execute(getIndexRequest(id), InvertedIndexEntity.class); + } + @Override public String deleteIndex(final String id) throws ArangoDBException { return executor.execute(deleteIndexRequest(id), deleteIndexResponseDeserializer()); @@ -279,6 +284,11 @@ public IndexEntity ensurePersistentIndex(final Iterable fields, final Pe return executor.execute(createPersistentIndexRequest(fields, options), IndexEntity.class); } + @Override + public InvertedIndexEntity ensureInvertedIndex(final InvertedIndexOptions options) throws ArangoDBException { + return executor.execute(createInvertedIndexRequest(options), InvertedIndexEntity.class); + } + @Override public IndexEntity ensureGeoIndex(final Iterable fields, final GeoIndexOptions options) throws ArangoDBException { @@ -309,6 +319,11 @@ public Collection getIndexes() throws ArangoDBException { return executor.execute(getIndexesRequest(), getIndexesResponseDeserializer()); } + @Override + public Collection getInvertedIndexes() throws ArangoDBException { + return executor.execute(getIndexesRequest(), getInvertedIndexesResponseDeserializer()); + } + @Override public boolean exists() throws ArangoDBException { try { diff --git a/src/main/java/com/arangodb/internal/InternalArangoCollection.java b/src/main/java/com/arangodb/internal/InternalArangoCollection.java index f7d87537e..fdfdbc24e 100644 --- a/src/main/java/com/arangodb/internal/InternalArangoCollection.java +++ b/src/main/java/com/arangodb/internal/InternalArangoCollection.java @@ -576,6 +576,13 @@ protected Request createPersistentIndexRequest( return request; } + protected Request createInvertedIndexRequest(final InvertedIndexOptions options) { + final Request request = request(db.dbName(), RequestType.POST, PATH_API_INDEX); + request.putQueryParam(COLLECTION, name); + request.setBody(util().serialize(options)); + return request; + } + protected Request createGeoIndexRequest(final Iterable fields, final GeoIndexOptions options) { final Request request = request(db.dbName(), RequestType.POST, PATH_API_INDEX); request.putQueryParam(COLLECTION, name); @@ -617,8 +624,31 @@ protected Request getIndexesRequest() { } protected ResponseDeserializer> getIndexesResponseDeserializer() { - return response -> util().deserialize(response.getBody().get("indexes"), new Type>() { - }.getType()); + return response -> { + Collection indexes = new ArrayList<>(); + Iterator it = response.getBody().get("indexes").arrayIterator(); + while (it.hasNext()) { + VPackSlice idx = it.next(); + if (!"inverted".equals(idx.get("type").getAsString())) { + indexes.add(util().deserialize(idx, IndexEntity.class)); + } + } + return indexes; + }; + } + + protected ResponseDeserializer> getInvertedIndexesResponseDeserializer() { + return response -> { + Collection indexes = new ArrayList<>(); + Iterator it = response.getBody().get("indexes").arrayIterator(); + while (it.hasNext()) { + VPackSlice idx = it.next(); + if ("inverted".equals(idx.get("type").getAsString())) { + indexes.add(util().deserialize(idx, InvertedIndexEntity.class)); + } + } + return indexes; + }; } protected Request truncateRequest(final CollectionTruncateOptions options) { diff --git a/src/main/java/com/arangodb/internal/velocypack/VPackDeserializers.java b/src/main/java/com/arangodb/internal/velocypack/VPackDeserializers.java index 3a5a32084..294ea1f41 100644 --- a/src/main/java/com/arangodb/internal/velocypack/VPackDeserializers.java +++ b/src/main/java/com/arangodb/internal/velocypack/VPackDeserializers.java @@ -20,18 +20,7 @@ package com.arangodb.internal.velocypack; -import com.arangodb.entity.BaseDocument; -import com.arangodb.entity.BaseEdgeDocument; -import com.arangodb.entity.CollectionStatus; -import com.arangodb.entity.CollectionType; -import com.arangodb.entity.License; -import com.arangodb.entity.LogLevel; -import com.arangodb.entity.MinReplicationFactor; -import com.arangodb.entity.Permissions; -import com.arangodb.entity.QueryExecutionState; -import com.arangodb.entity.ReplicationFactor; -import com.arangodb.entity.ViewEntity; -import com.arangodb.entity.ViewType; +import com.arangodb.entity.*; import com.arangodb.entity.arangosearch.AnalyzerType; import com.arangodb.entity.arangosearch.ArangoSearchCompression; import com.arangodb.entity.arangosearch.ArangoSearchProperties; @@ -159,6 +148,17 @@ public class VPackDeserializers { public static final VPackDeserializer VIEW_TYPE = (parent, vpack, context) -> "arangosearch".equals(vpack.getAsString()) ? ViewType.ARANGO_SEARCH : ViewType.valueOf(vpack.getAsString().toUpperCase(Locale.ENGLISH)); + public static final VPackDeserializer STORED_VALUE = (parent, vpack, context) -> { + VPackSlice fields = vpack.get("fields"); + VPackSlice compression = vpack.get("compression"); + final Iterator fieldsIterator = fields.arrayIterator(); + List fieldsList = new ArrayList<>(); + while (fieldsIterator.hasNext()) { + fieldsList.add(fieldsIterator.next().getAsString()); + } + return new StoredValue(fieldsList, ArangoSearchCompression.valueOf(compression.getAsString())); + }; + public static final VPackDeserializer ARANGO_SEARCH_PROPERTIES = (parent, vpack, context) -> { final ArangoSearchProperties properties = new ArangoSearchProperties(); final VPackSlice consolidationIntervalMsec = vpack.get("consolidationIntervalMsec"); @@ -240,21 +240,10 @@ public class VPackDeserializers { } final VPackSlice storedValues = vpack.get("storedValues"); - if (storedValues.isArray()) { - final Iterator storedValueIterator = storedValues.arrayIterator(); - for (; storedValueIterator.hasNext(); ) { - final VPackSlice entry = storedValueIterator.next(); - if (entry.isObject()) { - VPackSlice fields = entry.get("fields"); - VPackSlice compression = entry.get("compression"); - if (fields.isArray() && compression.isString()) { - final Iterator fieldsIterator = fields.arrayIterator(); - List fieldsList = new ArrayList<>(); - fieldsIterator.forEachRemaining(it -> fieldsList.add(it.getAsString())); - properties.addStoredValues(new StoredValue(fieldsList, ArangoSearchCompression.valueOf(compression.getAsString()))); - } - } - } + final Iterator storedValueIterator = storedValues.arrayIterator(); + while (storedValueIterator.hasNext()) { + StoredValue sv = context.deserialize(storedValueIterator.next(), StoredValue.class); + properties.addStoredValues(sv); } return properties; @@ -330,4 +319,9 @@ protected static FieldLink deserializeField(final Entry fiel (parent, vpack, context) -> ZKDIndexOptions.FieldValueTypes.valueOf(vpack.getAsString().toUpperCase(Locale.ENGLISH)); + public static final VPackDeserializer INVERTED_INDEX_PRIMARY_SORT_FIELD = (parent, vpack, context) -> { + InvertedIndexPrimarySort.Field.Direction dir = vpack.get("asc").getAsBoolean() ? + InvertedIndexPrimarySort.Field.Direction.asc : InvertedIndexPrimarySort.Field.Direction.desc; + return new InvertedIndexPrimarySort.Field(vpack.get("field").getAsString(), dir); + }; } diff --git a/src/main/java/com/arangodb/internal/velocypack/VPackDriverModule.java b/src/main/java/com/arangodb/internal/velocypack/VPackDriverModule.java index 4bd3e84f9..0cccb9893 100644 --- a/src/main/java/com/arangodb/internal/velocypack/VPackDriverModule.java +++ b/src/main/java/com/arangodb/internal/velocypack/VPackDriverModule.java @@ -21,10 +21,7 @@ package com.arangodb.internal.velocypack; import com.arangodb.entity.*; -import com.arangodb.entity.arangosearch.ArangoSearchProperties; -import com.arangodb.entity.arangosearch.ArangoSearchPropertiesEntity; -import com.arangodb.entity.arangosearch.ConsolidationPolicy; -import com.arangodb.entity.arangosearch.ConsolidationType; +import com.arangodb.entity.arangosearch.*; import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; import com.arangodb.internal.DocumentFields; import com.arangodb.internal.velocystream.internal.AuthenticationRequest; @@ -100,11 +97,13 @@ public > void setup(final C context) { context.registerDeserializer(ReplicationFactor.class, VPackDeserializers.REPLICATION_FACTOR); context.registerDeserializer(MinReplicationFactor.class, VPackDeserializers.MIN_REPLICATION_FACTOR); context.registerDeserializer(ViewType.class, VPackDeserializers.VIEW_TYPE); + context.registerDeserializer(StoredValue.class, VPackDeserializers.STORED_VALUE); context.registerDeserializer(ArangoSearchProperties.class, VPackDeserializers.ARANGO_SEARCH_PROPERTIES); context.registerDeserializer(ArangoSearchPropertiesEntity.class, VPackDeserializers.ARANGO_SEARCH_PROPERTIES_ENTITY); context.registerDeserializer(ConsolidationPolicy.class, VPackDeserializers.CONSOLIDATE); context.registerDeserializer(CollectionSchema.class, VPackDeserializers.COLLECTION_VALIDATION); context.registerDeserializer(ZKDIndexOptions.FieldValueTypes.class, VPackDeserializers.ZKD_FIELD_VALUE_TYPES); + context.registerDeserializer(InvertedIndexPrimarySort.Field.class, VPackDeserializers.INVERTED_INDEX_PRIMARY_SORT_FIELD); } @Override diff --git a/src/main/java/com/arangodb/model/FulltextIndexOptions.java b/src/main/java/com/arangodb/model/FulltextIndexOptions.java index 447c237b8..4ed5068ea 100644 --- a/src/main/java/com/arangodb/model/FulltextIndexOptions.java +++ b/src/main/java/com/arangodb/model/FulltextIndexOptions.java @@ -26,7 +26,7 @@ * @author Mark Vollmary * @see API * Documentation - * @deprecated since ArangoDB 3.10, use ArangoSearch view instead. + * @deprecated since ArangoDB 3.10, use ArangoSearch or Inverted indexes instead. */ @Deprecated public class FulltextIndexOptions extends IndexOptions { diff --git a/src/main/java/com/arangodb/model/IndexOptions.java b/src/main/java/com/arangodb/model/IndexOptions.java index 1132e1c8e..7cafad173 100644 --- a/src/main/java/com/arangodb/model/IndexOptions.java +++ b/src/main/java/com/arangodb/model/IndexOptions.java @@ -59,7 +59,7 @@ public T name(final String name) { return getThis(); } - protected String getName() { + public String getName() { return name; } } diff --git a/src/main/java/com/arangodb/model/InvertedIndexOptions.java b/src/main/java/com/arangodb/model/InvertedIndexOptions.java new file mode 100644 index 000000000..faa2ed58e --- /dev/null +++ b/src/main/java/com/arangodb/model/InvertedIndexOptions.java @@ -0,0 +1,226 @@ +/* + * DISCLAIMER + * + * Copyright 2016 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; +import com.arangodb.entity.InvertedIndexField; +import com.arangodb.entity.InvertedIndexPrimarySort; +import com.arangodb.entity.arangosearch.*; + +import java.util.*; + +/** + * TODO: add documentation + * + * @author Michele Rastelli + * @see API Documentation + * @since ArangoDB 3.10 + */ +public class InvertedIndexOptions extends IndexOptions { + + protected final IndexType type = IndexType.inverted; + private Integer parallelism; + private InvertedIndexPrimarySort primarySort; + private final Collection storedValues = new ArrayList<>(); + private String analyzer; + private final Set features = new HashSet<>(); + private Boolean includeAllFields; + private Boolean trackListPositions; + private Boolean searchField; + private final Collection fields = new ArrayList<>(); + private Long consolidationIntervalMsec; + private Long commitIntervalMsec; + private Long cleanupIntervalStep; + private ConsolidationPolicy consolidationPolicy; + private Long writebufferIdle; + private Long writebufferActive; + private Long writebufferSizeMax; + + public InvertedIndexOptions() { + super(); + } + + @Override + protected InvertedIndexOptions getThis() { + return this; + } + + protected IndexType getType() { + return type; + } + + public Integer getParallelism() { + return parallelism; + } + + public InvertedIndexOptions parallelism(Integer parallelism) { + this.parallelism = parallelism; + return this; + } + + public InvertedIndexPrimarySort getPrimarySort() { + return primarySort; + } + + public InvertedIndexOptions primarySort(InvertedIndexPrimarySort primarySort) { + this.primarySort = primarySort; + return this; + } + + public Collection getStoredValues() { + return storedValues; + } + + public InvertedIndexOptions storedValues(StoredValue... storedValues) { + Collections.addAll(this.storedValues, storedValues); + return this; + } + + public String getAnalyzer() { + return analyzer; + } + + public InvertedIndexOptions analyzer(String analyzer) { + this.analyzer = analyzer; + return this; + } + + public Set getFeatures() { + return features; + } + + public InvertedIndexOptions features(AnalyzerFeature... features) { + Collections.addAll(this.features, features); + return this; + } + + public Boolean getIncludeAllFields() { + return includeAllFields; + } + + public InvertedIndexOptions includeAllFields(Boolean includeAllFields) { + this.includeAllFields = includeAllFields; + return this; + } + + public Boolean getTrackListPositions() { + return trackListPositions; + } + + public InvertedIndexOptions trackListPositions(Boolean trackListPositions) { + this.trackListPositions = trackListPositions; + return this; + } + + public Boolean getSearchField() { + return searchField; + } + + public InvertedIndexOptions searchField(Boolean searchField) { + this.searchField = searchField; + return this; + } + + public Collection getFields() { + return fields; + } + + public InvertedIndexOptions fields(InvertedIndexField... fields) { + Collections.addAll(this.fields, fields); + return this; + } + + public Long getConsolidationIntervalMsec() { + return consolidationIntervalMsec; + } + + public InvertedIndexOptions consolidationIntervalMsec(Long consolidationIntervalMsec) { + this.consolidationIntervalMsec = consolidationIntervalMsec; + return this; + } + + public Long getCommitIntervalMsec() { + return commitIntervalMsec; + } + + public InvertedIndexOptions commitIntervalMsec(Long commitIntervalMsec) { + this.commitIntervalMsec = commitIntervalMsec; + return this; + } + + public Long getCleanupIntervalStep() { + return cleanupIntervalStep; + } + + public InvertedIndexOptions cleanupIntervalStep(Long cleanupIntervalStep) { + this.cleanupIntervalStep = cleanupIntervalStep; + return this; + } + + public ConsolidationPolicy getConsolidationPolicy() { + return consolidationPolicy; + } + + public InvertedIndexOptions consolidationPolicy(ConsolidationPolicy consolidationPolicy) { + this.consolidationPolicy = consolidationPolicy; + return this; + } + + public Long getWritebufferIdle() { + return writebufferIdle; + } + + public InvertedIndexOptions writebufferIdle(Long writebufferIdle) { + this.writebufferIdle = writebufferIdle; + return this; + } + + public Long getWritebufferActive() { + return writebufferActive; + } + + public InvertedIndexOptions writebufferActive(Long writebufferActive) { + this.writebufferActive = writebufferActive; + return this; + } + + public Long getWritebufferSizeMax() { + return writebufferSizeMax; + } + + public InvertedIndexOptions writebufferSizeMax(Long writebufferSizeMax) { + this.writebufferSizeMax = writebufferSizeMax; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InvertedIndexOptions that = (InvertedIndexOptions) o; + return type == that.type && Objects.equals(parallelism, that.parallelism) && Objects.equals(primarySort, that.primarySort) && Objects.equals(storedValues, that.storedValues) && Objects.equals(analyzer, that.analyzer) && Objects.equals(features, that.features) && Objects.equals(includeAllFields, that.includeAllFields) && Objects.equals(trackListPositions, that.trackListPositions) && Objects.equals(searchField, that.searchField) && Objects.equals(fields, that.fields) && Objects.equals(consolidationIntervalMsec, that.consolidationIntervalMsec) && Objects.equals(commitIntervalMsec, that.commitIntervalMsec) && Objects.equals(cleanupIntervalStep, that.cleanupIntervalStep) && Objects.equals(consolidationPolicy, that.consolidationPolicy) && Objects.equals(writebufferIdle, that.writebufferIdle) && Objects.equals(writebufferActive, that.writebufferActive) && Objects.equals(writebufferSizeMax, that.writebufferSizeMax); + } + + @Override + public int hashCode() { + return Objects.hash(type, parallelism, primarySort, storedValues, analyzer, features, includeAllFields, trackListPositions, searchField, fields, consolidationIntervalMsec, commitIntervalMsec, cleanupIntervalStep, consolidationPolicy, writebufferIdle, writebufferActive, writebufferSizeMax); + } +} diff --git a/src/test/java/com/arangodb/ArangoCollectionTest.java b/src/test/java/com/arangodb/ArangoCollectionTest.java index 67437779d..66067edb6 100644 --- a/src/test/java/com/arangodb/ArangoCollectionTest.java +++ b/src/test/java/com/arangodb/ArangoCollectionTest.java @@ -20,21 +20,7 @@ package com.arangodb; -import com.arangodb.entity.BaseDocument; -import com.arangodb.entity.BaseEdgeDocument; -import com.arangodb.entity.CollectionEntity; -import com.arangodb.entity.CollectionPropertiesEntity; -import com.arangodb.entity.CollectionRevisionEntity; -import com.arangodb.entity.DocumentCreateEntity; -import com.arangodb.entity.DocumentDeleteEntity; -import com.arangodb.entity.DocumentEntity; -import com.arangodb.entity.DocumentImportEntity; -import com.arangodb.entity.DocumentUpdateEntity; -import com.arangodb.entity.IndexEntity; -import com.arangodb.entity.IndexType; -import com.arangodb.entity.MultiDocumentEntity; -import com.arangodb.entity.Permissions; -import com.arangodb.entity.ShardEntity; +import com.arangodb.entity.*; import com.arangodb.model.*; import com.arangodb.model.DocumentImportOptions.OnDuplicate; import com.arangodb.util.MapBuilder; @@ -47,14 +33,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; diff --git a/src/test/java/com/arangodb/ArangoSearchTest.java b/src/test/java/com/arangodb/ArangoSearchTest.java index 173c0a47e..22494fb02 100644 --- a/src/test/java/com/arangodb/ArangoSearchTest.java +++ b/src/test/java/com/arangodb/ArangoSearchTest.java @@ -987,5 +987,27 @@ void collationAnalyzer(ArangoDatabase db) { createGetAndDeleteTypedAnalyzer(db, collationAnalyzer); } + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void offsetFeature(ArangoDatabase db) { + assumeTrue(isEnterprise()); + assumeTrue(isAtLeastVersion(3, 10)); + + String name = "test-" + rnd(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + features.add(AnalyzerFeature.offset); + + AnalyzerEntity options = new AnalyzerEntity(); + options.setFeatures(features); + options.setName(name); + options.setType(AnalyzerType.identity); + options.setProperties(Collections.emptyMap()); + + createGetAndDeleteAnalyzer(db, options); + } } diff --git a/src/test/java/com/arangodb/InvertedIndexTest.java b/src/test/java/com/arangodb/InvertedIndexTest.java new file mode 100644 index 000000000..3a42b941e --- /dev/null +++ b/src/test/java/com/arangodb/InvertedIndexTest.java @@ -0,0 +1,188 @@ +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.entity.arangosearch.*; +import com.arangodb.entity.arangosearch.analyzer.DelimiterAnalyzer; +import com.arangodb.entity.arangosearch.analyzer.DelimiterAnalyzerProperties; +import com.arangodb.model.InvertedIndexOptions; +import com.arangodb.model.PersistentIndexOptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.*; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class InvertedIndexTest extends BaseJunit5 { + + private static final String COLLECTION_NAME = "InvertedIndexTest_collection"; + + private static Stream cols() { + return dbsStream().map(db -> db.collection(COLLECTION_NAME)).map(Arguments::of); + } + + @BeforeAll + static void init() { + initCollections(COLLECTION_NAME); + } + + private void createAnalyzer(String analyzerName, ArangoDatabase db) { + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + DelimiterAnalyzer da = new DelimiterAnalyzer(); + da.setName(analyzerName); + da.setFeatures(features); + DelimiterAnalyzerProperties props = new DelimiterAnalyzerProperties(); + props.setDelimiter("-"); + da.setProperties(props); + + db.createSearchAnalyzer(da); + } + + private InvertedIndexOptions createOptions(String analyzerName) { + InvertedIndexField field = new InvertedIndexField() + .name("foo") + .analyzer(AnalyzerType.identity.toString()) + .includeAllFields(true) + .searchField(false) + .trackListPositions(false) + .features( + AnalyzerFeature.position, + AnalyzerFeature.frequency, + AnalyzerFeature.norm, + AnalyzerFeature.offset + ); + + if (isEnterprise()) { + field.nested( + new InvertedIndexField() + .name("bar") + .analyzer(analyzerName) + .searchField(true) + .features(AnalyzerFeature.position) + .nested( + new InvertedIndexField() + .name("baz") + .analyzer(AnalyzerType.identity.toString()) + .searchField(false) + .features(AnalyzerFeature.frequency) + ) + ); + } + + return new InvertedIndexOptions() + .name("invertedIndex-" + UUID.randomUUID()) + .inBackground(true) + .parallelism(5) + .primarySort(new InvertedIndexPrimarySort() + .fields( + new InvertedIndexPrimarySort.Field("f1", InvertedIndexPrimarySort.Field.Direction.asc), + new InvertedIndexPrimarySort.Field("f2", InvertedIndexPrimarySort.Field.Direction.desc) + ) + .compression(ArangoSearchCompression.lz4) + ) + .storedValues(new StoredValue(Arrays.asList("f3", "f4"), ArangoSearchCompression.none)) + .analyzer(analyzerName) + .features(AnalyzerFeature.position, AnalyzerFeature.frequency) + .includeAllFields(false) + .trackListPositions(true) + .searchField(true) + .fields(field) + .consolidationIntervalMsec(11L) + .commitIntervalMsec(22L) + .cleanupIntervalStep(33L) + .consolidationPolicy(ConsolidationPolicy.of(ConsolidationType.BYTES_ACCUM).threshold(1.)) + .writebufferIdle(44L) + .writebufferActive(55L) + .writebufferSizeMax(66L); + } + + private void assertCorrectIndexEntity(InvertedIndexEntity indexResult, InvertedIndexOptions options) { + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getId()).isNotNull().isNotEmpty(); + // FIXME: in single server this is null + // assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getVersion()).isNotNull(); + assertThat(indexResult.getCode()).isNotNull(); + assertThat(indexResult.getType()).isEqualTo(IndexType.inverted); + assertThat(indexResult.getName()).isEqualTo(options.getName()); + assertThat(indexResult.getFields()).containsExactlyElementsOf(options.getFields()); + assertThat(indexResult.getSearchField()).isEqualTo(options.getSearchField()); + assertThat(indexResult.getStoredValues()).containsExactlyElementsOf(options.getStoredValues()); + assertThat(indexResult.getPrimarySort()).isEqualTo(options.getPrimarySort()); + assertThat(indexResult.getAnalyzer()).isEqualTo(options.getAnalyzer()); + assertThat(indexResult.getFeatures()).hasSameElementsAs(options.getFeatures()); + assertThat(indexResult.getIncludeAllFields()).isEqualTo(options.getIncludeAllFields()); + assertThat(indexResult.getTrackListPositions()).isEqualTo(options.getTrackListPositions()); + assertThat(indexResult.getCleanupIntervalStep()).isEqualTo(options.getCleanupIntervalStep()); + assertThat(indexResult.getCommitIntervalMsec()).isEqualTo(options.getCommitIntervalMsec()); + assertThat(indexResult.getConsolidationIntervalMsec()).isEqualTo(options.getConsolidationIntervalMsec()); + assertThat(indexResult.getConsolidationPolicy()).isEqualTo(options.getConsolidationPolicy()); + assertThat(indexResult.getWritebufferIdle()).isEqualTo(options.getWritebufferIdle()); + assertThat(indexResult.getWritebufferActive()).isEqualTo(options.getWritebufferActive()); + assertThat(indexResult.getWritebufferSizeMax()).isEqualTo(options.getWritebufferSizeMax()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void createAndGetInvertedIndex(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 10)); + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options); + assertCorrectIndexEntity(created, options); + InvertedIndexEntity loadedIndex = collection.getInvertedIndex(created.getName()); + assertCorrectIndexEntity(loadedIndex, options); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getInvertedIndexesShouldNotReturnOtherIndexTypes(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 10)); + + // create persistent index + collection.ensurePersistentIndex(Collections.singletonList("foo"), new PersistentIndexOptions().name("persistentIndex")); + + // create inverted index + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options); + + Collection loadedIndexes = collection.getInvertedIndexes(); + assertThat(loadedIndexes).map(InvertedIndexEntity::getName) + .doesNotContain("persistentIndex") + .contains(created.getName()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getIndexesShouldNotReturnInvertedIndexes(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 10)); + + // create persistent index + collection.ensurePersistentIndex(Collections.singletonList("foo"), new PersistentIndexOptions().name("persistentIndex")); + + // create inverted index + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options); + + Collection loadedIndexes = collection.getIndexes(); + assertThat(loadedIndexes).map(IndexEntity::getName) + .doesNotContain(created.getName()) + .contains("persistentIndex"); + } + +} diff --git a/src/test/java/com/arangodb/async/InvertedIndexTest.java b/src/test/java/com/arangodb/async/InvertedIndexTest.java new file mode 100644 index 000000000..1b9eebe14 --- /dev/null +++ b/src/test/java/com/arangodb/async/InvertedIndexTest.java @@ -0,0 +1,199 @@ +package com.arangodb.async; + +import com.arangodb.entity.*; +import com.arangodb.entity.arangosearch.*; +import com.arangodb.entity.arangosearch.analyzer.DelimiterAnalyzer; +import com.arangodb.entity.arangosearch.analyzer.DelimiterAnalyzerProperties; +import com.arangodb.model.InvertedIndexOptions; +import com.arangodb.model.PersistentIndexOptions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class InvertedIndexTest extends BaseTest { + + private static final String COLLECTION_NAME = "InvertedIndexTestAsync_collection"; + + InvertedIndexTest() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + if (!collection.exists().get()) { + collection.create().get(); + } + } + + @BeforeAll + static void setup() throws InterruptedException, ExecutionException { + db.createCollection(COLLECTION_NAME, null).get(); + } + + @AfterEach + void teardown() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).drop().get(); + } + + + private void createAnalyzer(String analyzerName, ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + DelimiterAnalyzer da = new DelimiterAnalyzer(); + da.setName(analyzerName); + da.setFeatures(features); + DelimiterAnalyzerProperties props = new DelimiterAnalyzerProperties(); + props.setDelimiter("-"); + da.setProperties(props); + + db.createSearchAnalyzer(da).get(); + } + + private InvertedIndexOptions createOptions(String analyzerName) throws ExecutionException, InterruptedException { + InvertedIndexField field = new InvertedIndexField() + .name("foo") + .analyzer(AnalyzerType.identity.toString()) + .includeAllFields(true) + .searchField(false) + .trackListPositions(false) + .features( + AnalyzerFeature.position, + AnalyzerFeature.frequency, + AnalyzerFeature.norm, + AnalyzerFeature.offset + ); + + if (isEnterprise()) { + field.nested( + new InvertedIndexField() + .name("bar") + .analyzer(analyzerName) + .searchField(true) + .features(AnalyzerFeature.position) + .nested( + new InvertedIndexField() + .name("baz") + .analyzer(AnalyzerType.identity.toString()) + .searchField(false) + .features(AnalyzerFeature.frequency) + ) + ); + } + + return new InvertedIndexOptions() + .name("invertedIndex-" + UUID.randomUUID()) + .inBackground(true) + .parallelism(5) + .primarySort(new InvertedIndexPrimarySort() + .fields( + new InvertedIndexPrimarySort.Field("f1", InvertedIndexPrimarySort.Field.Direction.asc), + new InvertedIndexPrimarySort.Field("f2", InvertedIndexPrimarySort.Field.Direction.desc) + ) + .compression(ArangoSearchCompression.lz4) + ) + .storedValues(new StoredValue(Arrays.asList("f3", "f4"), ArangoSearchCompression.none)) + .analyzer(analyzerName) + .features(AnalyzerFeature.position, AnalyzerFeature.frequency) + .includeAllFields(false) + .trackListPositions(true) + .searchField(true) + .fields(field) + .consolidationIntervalMsec(11L) + .commitIntervalMsec(22L) + .cleanupIntervalStep(33L) + .consolidationPolicy(ConsolidationPolicy.of(ConsolidationType.BYTES_ACCUM).threshold(1.)) + .writebufferIdle(44L) + .writebufferActive(55L) + .writebufferSizeMax(66L); + } + + private void assertCorrectIndexEntity(InvertedIndexEntity indexResult, InvertedIndexOptions options) { + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getId()).isNotNull().isNotEmpty(); + // FIXME: in single server this is null + // assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getVersion()).isNotNull(); + assertThat(indexResult.getCode()).isNotNull(); + assertThat(indexResult.getType()).isEqualTo(IndexType.inverted); + assertThat(indexResult.getName()).isEqualTo(options.getName()); + assertThat(indexResult.getFields()).containsExactlyElementsOf(options.getFields()); + assertThat(indexResult.getSearchField()).isEqualTo(options.getSearchField()); + assertThat(indexResult.getStoredValues()).containsExactlyElementsOf(options.getStoredValues()); + assertThat(indexResult.getPrimarySort()).isEqualTo(options.getPrimarySort()); + assertThat(indexResult.getAnalyzer()).isEqualTo(options.getAnalyzer()); + assertThat(indexResult.getFeatures()).hasSameElementsAs(options.getFeatures()); + assertThat(indexResult.getIncludeAllFields()).isEqualTo(options.getIncludeAllFields()); + assertThat(indexResult.getTrackListPositions()).isEqualTo(options.getTrackListPositions()); + assertThat(indexResult.getCleanupIntervalStep()).isEqualTo(options.getCleanupIntervalStep()); + assertThat(indexResult.getCommitIntervalMsec()).isEqualTo(options.getCommitIntervalMsec()); + assertThat(indexResult.getConsolidationIntervalMsec()).isEqualTo(options.getConsolidationIntervalMsec()); + assertThat(indexResult.getConsolidationPolicy()).isEqualTo(options.getConsolidationPolicy()); + assertThat(indexResult.getWritebufferIdle()).isEqualTo(options.getWritebufferIdle()); + assertThat(indexResult.getWritebufferActive()).isEqualTo(options.getWritebufferActive()); + assertThat(indexResult.getWritebufferSizeMax()).isEqualTo(options.getWritebufferSizeMax()); + } + + @Test + void createAndGetInvertedIndex() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options).get(); + assertCorrectIndexEntity(created, options); + InvertedIndexEntity loadedIndex = collection.getInvertedIndex(created.getName()).get(); + assertCorrectIndexEntity(loadedIndex, options); + } + + @Test + void getInvertedIndexesShouldNotReturnOtherIndexTypes() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // create persistent index + collection.ensurePersistentIndex(Collections.singletonList("foo"), new PersistentIndexOptions().name("persistentIndex")); + + // create inverted index + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options).get(); + + Collection loadedIndexes = collection.getInvertedIndexes().get(); + assertThat(loadedIndexes).map(InvertedIndexEntity::getName) + .doesNotContain("persistentIndex") + .contains(created.getName()); + } + + @Test + void getIndexesShouldNotReturnInvertedIndexes() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // create persistent index + collection.ensurePersistentIndex(Collections.singletonList("foo"), new PersistentIndexOptions().name("persistentIndex")); + + // create inverted index + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options).get(); + + Collection loadedIndexes = collection.getIndexes().get(); + assertThat(loadedIndexes).map(IndexEntity::getName) + .doesNotContain(created.getName()) + .contains("persistentIndex"); + } + +} diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index f67855e9c..579f1b9db 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -8,7 +8,7 @@ - +