From 23cfac0e25d2f319e843c9b7f9c7178641a137e0 Mon Sep 17 00:00:00 2001 From: Anush Date: Wed, 6 Mar 2024 18:50:12 +0530 Subject: [PATCH] v1.8.0 (#26) * v1.8.0 * chore: StartFromFactory * chore: bump to v1.8.0 * chore: resolve imports --- build.gradle | 2 +- gradle.properties | 6 +- .../io/qdrant/client/ConditionFactory.java | 18 ++++ .../java/io/qdrant/client/QdrantClient.java | 100 ++++++++++++++++++ .../io/qdrant/client/StartFromFactory.java | 65 ++++++++++++ .../io/qdrant/client/CollectionsTest.java | 13 +++ 6 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 src/main/java/io/qdrant/client/StartFromFactory.java diff --git a/build.gradle b/build.gradle index 450f83a..0fd3d16 100644 --- a/build.gradle +++ b/build.gradle @@ -116,7 +116,7 @@ tasks.register('downloadProtos') { def outputDirectory = outputs.files.singleFile.toPath().toString() def protoFileRegex = Pattern.compile(".*?lib/api/src/grpc/proto/.*?.proto") try (def httpClient = HttpClients.createDefault()) { - def url = "https://api.github.com/repos/qdrant/qdrant/tarball/refs/tags/${qdrantProtosVersion}" + def url = "https://api.github.com/repos/qdrant/qdrant/tarball/${qdrantProtosVersion}" logger.debug("downloading protos from {}", url) def response = httpClient.execute(new HttpGet(url)) try (InputStream tarballStream = response.getEntity().getContent()) { diff --git a/gradle.properties b/gradle.properties index f589747..f8e6727 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ # The version of qdrant to use to download protos -qdrantProtosVersion=v1.7.0 +qdrantProtosVersion=v1.8.0 # The version of qdrant docker image to run integration tests against -qdrantVersion=v1.7.0 +qdrantVersion=v1.8.0 # The version of the client to generate -packageVersion=1.7.2 \ No newline at end of file +packageVersion=1.8.0 \ No newline at end of file diff --git a/src/main/java/io/qdrant/client/ConditionFactory.java b/src/main/java/io/qdrant/client/ConditionFactory.java index 0abe7af..bcc991b 100644 --- a/src/main/java/io/qdrant/client/ConditionFactory.java +++ b/src/main/java/io/qdrant/client/ConditionFactory.java @@ -3,6 +3,7 @@ import java.util.List; import io.qdrant.client.grpc.Points.Condition; +import io.qdrant.client.grpc.Points.DatetimeRange; import io.qdrant.client.grpc.Points.FieldCondition; import io.qdrant.client.grpc.Points.Filter; import io.qdrant.client.grpc.Points.GeoBoundingBox; @@ -370,4 +371,21 @@ public static Condition filter(Filter filter) { .setFilter(filter) .build(); } + + /** + * Matches records where the given field has a datetime value within the + * specified range + * + * @param field The name of the field. + * @param datetimeRange The datetime range to match. + * @return a new instance of {@link Condition} + */ + public static Condition datetimeRange(String field, DatetimeRange datetimeRange) { + return Condition.newBuilder() + .setField(FieldCondition.newBuilder() + .setKey(field) + .setDatetimeRange(datetimeRange) + .build()) + .build(); + } } diff --git a/src/main/java/io/qdrant/client/QdrantClient.java b/src/main/java/io/qdrant/client/QdrantClient.java index 9e83ed2..e3f0e56 100644 --- a/src/main/java/io/qdrant/client/QdrantClient.java +++ b/src/main/java/io/qdrant/client/QdrantClient.java @@ -23,6 +23,8 @@ import io.qdrant.client.grpc.Collections.AliasOperations; import io.qdrant.client.grpc.Collections.ChangeAliases; import io.qdrant.client.grpc.Collections.CollectionDescription; +import io.qdrant.client.grpc.Collections.CollectionExistsRequest; +import io.qdrant.client.grpc.Collections.CollectionExistsResponse; import io.qdrant.client.grpc.Collections.CollectionInfo; import io.qdrant.client.grpc.Collections.CollectionOperationResponse; import io.qdrant.client.grpc.Collections.CreateAlias; @@ -476,6 +478,33 @@ public ListenableFuture updateCollectionAsync(Updat }, MoreExecutors.directExecutor()); } + /** + * Check if a collection exists + * + * @param collectionName The name of the collection. + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture collectionExistsAsync(String collectionName) { + return collectionExistsAsync(collectionName, null); + } + + /** + * Check if a collection exists + * + * @param collectionName The name of the collection. + * @param timeout The timeout for the call. + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture collectionExistsAsync(String collectionName, @Nullable Duration timeout) { + Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty"); + logger.debug("Collection exists '{}'", collectionName); + + ListenableFuture future = getCollections(timeout) + .collectionExists(CollectionExistsRequest.newBuilder().setCollectionName(collectionName).build()); + addLogFailureCallback(future, "Collection exists"); + return Futures.transform(future, response -> response.getResult().getExists(), MoreExecutors.directExecutor()); + } + //endregion //region Alias Management @@ -1515,6 +1544,38 @@ public ListenableFuture setPayloadAsync( @Nullable Boolean wait, @Nullable WriteOrderingType ordering, @Nullable Duration timeout + ) { + return setPayloadAsync( + collectionName, + payload, + pointsSelector, + wait, + null, + ordering, + timeout + ); + } + + /** + * Sets the payload for the points. + * + * @param collectionName The name of the collection. + * @param payload New payload values + * @param pointsSelector Selector for the points whose payloads are to be set. + * @param wait Whether to wait until the changes have been applied. Defaults to true. + * @param key The key for which to set the payload if nested + * @param ordering Write ordering guarantees. + * @param timeout The timeout for the call. + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture setPayloadAsync( + String collectionName, + Map payload, + @Nullable PointsSelector pointsSelector, + @Nullable Boolean wait, + @Nullable String key, + @Nullable WriteOrderingType ordering, + @Nullable Duration timeout ) { SetPayloadPoints.Builder requestBuilder = SetPayloadPoints.newBuilder() .setCollectionName(collectionName) @@ -1529,6 +1590,10 @@ public ListenableFuture setPayloadAsync( requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build()); } + if (key != null) { + requestBuilder.setKey(key); + } + return setPayloadAsync(requestBuilder.build(), timeout); } @@ -1687,6 +1752,38 @@ public ListenableFuture overwritePayloadAsync( @Nullable Boolean wait, @Nullable WriteOrderingType ordering, @Nullable Duration timeout + ) { + return overwritePayloadAsync( + collectionName, + payload, + pointsSelector, + wait, + null, + ordering, + timeout + ); + } + + /** + * Overwrites the payload for the points. + * + * @param collectionName The name of the collection. + * @param payload New payload values + * @param pointsSelector Selector for the points whose payloads are to be overwritten. + * @param wait Whether to wait until the changes have been applied. Defaults to true. + * @param key The key for which to overwrite the payload if nested + * @param ordering Write ordering guarantees. + * @param timeout The timeout for the call. + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture overwritePayloadAsync( + String collectionName, + Map payload, + @Nullable PointsSelector pointsSelector, + @Nullable Boolean wait, + @Nullable String key, + @Nullable WriteOrderingType ordering, + @Nullable Duration timeout ) { SetPayloadPoints.Builder requestBuilder = SetPayloadPoints.newBuilder() .setCollectionName(collectionName) @@ -1701,6 +1798,9 @@ public ListenableFuture overwritePayloadAsync( requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build()); } + if (key != null) + requestBuilder.setKey(key); + return overwritePayloadAsync(requestBuilder.build(), timeout); } diff --git a/src/main/java/io/qdrant/client/StartFromFactory.java b/src/main/java/io/qdrant/client/StartFromFactory.java new file mode 100644 index 0000000..247a444 --- /dev/null +++ b/src/main/java/io/qdrant/client/StartFromFactory.java @@ -0,0 +1,65 @@ +package io.qdrant.client; + +import java.time.Instant; + +import com.google.protobuf.Timestamp; + +import io.qdrant.client.grpc.Points.StartFrom; + +/** + * Convenience methods for constructing {@link StartFrom} + */ +public final class StartFromFactory { + private StartFromFactory() { + } + + /** + * Creates a {@link StartFrom} value from a {@link float} + * + * @param value The value + * @return a new instance of {@link StartFrom} + */ + public static StartFrom startFrom(float value) { + return StartFrom.newBuilder().setFloat(value).build(); + } + + /** + * Creates a {@link StartFrom} value from a {@link int} + * + * @param value The value + * @return a new instance of {@link StartFrom} + */ + public static StartFrom startFrom(int value) { + return StartFrom.newBuilder().setInteger(value).build(); + } + + /** + * Creates a {@link StartFrom} value from a {@link String} timestamp + * + * @param value The value + * @return a new instance of {@link StartFrom} + */ + public static StartFrom startFrom(String value) { + return StartFrom.newBuilder().setDatetime(value).build(); + } + + /** + * Creates a {@link StartFrom} value from a {@link Timestamp} + * + * @param value The value + * @return a new instance of {@link StartFrom} + */ + public static StartFrom startFrom(Timestamp value) { + return StartFrom.newBuilder().setTimestamp(value).build(); + } + + /** + * Creates a {@link StartFrom} value from a {@link Timestamp} + * + * @param value The value + * @return a new instance of {@link StartFrom} + */ + public static StartFrom startFrom(Instant value) { + return StartFrom.newBuilder().setTimestamp(Timestamp.newBuilder().setSeconds(value.getEpochSecond())).build(); + } +} diff --git a/src/test/java/io/qdrant/client/CollectionsTest.java b/src/test/java/io/qdrant/client/CollectionsTest.java index 900b835..b7b5499 100644 --- a/src/test/java/io/qdrant/client/CollectionsTest.java +++ b/src/test/java/io/qdrant/client/CollectionsTest.java @@ -1,6 +1,7 @@ package io.qdrant.client; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -180,6 +181,18 @@ public void getCollectionInfo_with_missing_collection() { assertEquals(Status.Code.NOT_FOUND, underlyingException.getStatus().getCode()); } + @Test + public void collectionExists() throws ExecutionException, InterruptedException { + assertFalse(client.collectionExistsAsync(testName).get()); + + CreateCollection createCollection = getCreateCollection(testName); + client.createCollectionAsync(createCollection).get(); + assertTrue(client.collectionExistsAsync(testName).get()); + + client.deleteCollectionAsync(testName).get(); + assertFalse(client.collectionExistsAsync(testName).get()); + } + @Test public void createAlias() throws ExecutionException, InterruptedException { CreateCollection createCollection = getCreateCollection(testName);