From 8ff1f7b22450fb0c48f332fdbb2c896d29630b44 Mon Sep 17 00:00:00 2001 From: Anush008 Date: Sat, 30 Dec 2023 00:16:07 +0530 Subject: [PATCH 1/9] feat: batch update points --- README.md | 10 +-- build.gradle | 4 +- gradle.properties | 2 +- .../java/io/qdrant/client/QdrantClient.java | 62 +++++++++++++++++++ .../java/io/qdrant/client/PointsTest.java | 30 +++++++++ 5 files changed, 100 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 51c562b..d7fb804 100644 --- a/README.md +++ b/README.md @@ -34,20 +34,20 @@ To install the library, add the following lines to your build config file. io.qdrant client - 1.7.0 + 1.7.1 ``` #### Scala SBT ```sbt -libraryDependencies += "io.qdrant" % "client" % "1.7.0" +libraryDependencies += "io.qdrant" % "client" % "1.7.1" ``` #### Gradle ```gradle -implementation 'io.qdrant:client:1.7.0' +implementation 'io.qdrant:client:1.7.1' ``` ## 📖 Documentation @@ -125,13 +125,13 @@ Insert vectors into a collection // import static convenience methods import static io.qdrant.client.PointIdFactory.id; import static io.qdrant.client.ValueFactory.value; -import static io.qdrant.client.VectorsFactory.vector; +import static io.qdrant.client.VectorsFactory.vectors; Random random = new Random(); List points = IntStream.range(1, 101) .mapToObj(i -> PointStruct.newBuilder() .setId(id(i)) - .setVectors(vector(IntStream.range(1, 101) + .setVectors(vectors(IntStream.range(1, 101) .mapToObj(v -> random.nextFloat()) .collect(Collectors.toList()))) .putAllPayload(ImmutableMap.of( diff --git a/build.gradle b/build.gradle index 5ca962e..ab70027 100644 --- a/build.gradle +++ b/build.gradle @@ -19,8 +19,8 @@ plugins { id 'maven-publish' id 'com.google.protobuf' version '0.9.4' - id "net.ltgt.errorprone" version '3.1.0' - id 'io.github.gradle-nexus.publish-plugin' version "1.3.0" + id 'net.ltgt.errorprone' version '3.1.0' + id 'io.github.gradle-nexus.publish-plugin' version '1.3.0' } group = 'io.qdrant' diff --git a/gradle.properties b/gradle.properties index a609339..b5b794c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ qdrantProtosVersion=v1.7.0 qdrantVersion=v1.7.0 # The version of the client to generate -packageVersion=1.7.0 \ No newline at end of file +packageVersion=1.7.1 \ No newline at end of file diff --git a/src/main/java/io/qdrant/client/QdrantClient.java b/src/main/java/io/qdrant/client/QdrantClient.java index a8dcae4..6d02341 100644 --- a/src/main/java/io/qdrant/client/QdrantClient.java +++ b/src/main/java/io/qdrant/client/QdrantClient.java @@ -39,6 +39,9 @@ import static io.qdrant.client.grpc.Points.DiscoverBatchResponse; import static io.qdrant.client.grpc.Points.DiscoverPoints; import static io.qdrant.client.grpc.Points.DiscoverResponse; +import static io.qdrant.client.grpc.Points.PointsUpdateOperation; +import static io.qdrant.client.grpc.Points.UpdateBatchPoints; +import static io.qdrant.client.grpc.Points.UpdateBatchResponse; import static io.qdrant.client.grpc.Collections.GetCollectionInfoRequest; import static io.qdrant.client.grpc.Collections.GetCollectionInfoResponse; import static io.qdrant.client.grpc.Collections.ListAliasesRequest; @@ -2204,6 +2207,65 @@ public ListenableFuture> recommendBatchAsync( MoreExecutors.directExecutor()); } + /** + * Performs a batch update of points. + * + * @param collectionName The name of the collection. + * @param operations The list of point update operations. + * + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture batchUpdateAsync(String collectionName, List operations) { + return batchUpdateAsync(collectionName, operations, null, null, null); + } + + /** + * Performs a batch update of points. + * + * @param collectionName The name of the collection. + * @param operations The list of point update operations. + * @param wait Whether to wait until the changes have been applied. Defaults to true. + * @param ordering Write ordering guarantees. + * @param timeout The timeout for the call. + * + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture batchUpdateAsync( + String collectionName, + List operations, + @Nullable Boolean wait, + @Nullable WriteOrdering ordering, + @Nullable Duration timeout) { + + UpdateBatchPoints.Builder requestBuilder = UpdateBatchPoints.newBuilder() + .setCollectionName(collectionName) + .addAllOperations(operations) + .setWait(wait == null || wait); + + if (ordering != null) { + requestBuilder.setOrdering(ordering); + } + return batchUpdateAsync(requestBuilder.build(), timeout); + } + + + /** + * Performs a batch update of points. + * + * @param request The update batch request. + * @param timeout The timeout for the call. + * + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture batchUpdateAsync(UpdateBatchPoints request, @Nullable Duration timeout) { + String collectionName = request.getCollectionName(); + Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty"); + logger.debug("Batch update points on '{}'", collectionName); + ListenableFuture future = getPoints(timeout).updateBatch(request); + addLogFailureCallback(future, "Batch update points"); + return future; + } + /** * Look for the points which are closer to stored positive examples and at the same time further to negative * examples, grouped by a given field diff --git a/src/test/java/io/qdrant/client/PointsTest.java b/src/test/java/io/qdrant/client/PointsTest.java index 75cb540..8bc7f5e 100644 --- a/src/test/java/io/qdrant/client/PointsTest.java +++ b/src/test/java/io/qdrant/client/PointsTest.java @@ -14,6 +14,13 @@ import org.testcontainers.shaded.com.google.common.collect.ImmutableSet; import io.qdrant.client.container.QdrantContainer; import io.qdrant.client.grpc.Points.DiscoverPoints; +import io.qdrant.client.grpc.Points.PointVectors; +import io.qdrant.client.grpc.Points.PointsIdsList; +import io.qdrant.client.grpc.Points.PointsSelector; +import io.qdrant.client.grpc.Points.PointsUpdateOperation; +import io.qdrant.client.grpc.Points.UpdateBatchResponse; +import io.qdrant.client.grpc.Points.PointsUpdateOperation.ClearPayload; +import io.qdrant.client.grpc.Points.PointsUpdateOperation.UpdateVectors; import java.util.List; import java.util.concurrent.ExecutionException; @@ -49,6 +56,7 @@ import static io.qdrant.client.TargetVectorFactory.targetVector; import static io.qdrant.client.ValueFactory.value; import static io.qdrant.client.VectorFactory.vector; +import static io.qdrant.client.VectorsFactory.vectors; @Testcontainers class PointsTest { @@ -540,6 +548,28 @@ public void delete_by_filter() throws ExecutionException, InterruptedException { assertEquals(0, points.size()); } + @Test + public void batchPointUpdate() throws ExecutionException, InterruptedException { + createAndSeedCollection(testName); + + List operations = List.of( + PointsUpdateOperation.newBuilder() + .setClearPayload(ClearPayload.newBuilder().setPoints( + PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addIds(id(9)))) + .build()) + .build(), + PointsUpdateOperation.newBuilder() + .setUpdateVectors(UpdateVectors.newBuilder() + .addPoints(PointVectors.newBuilder() + .setId(id(9)) + .setVectors(vectors(0.6f, 0.7f)))) + .build()); + + UpdateBatchResponse response = client.batchUpdateAsync(testName, operations).get(); + + response.getResultList().forEach(result -> assertEquals(UpdateStatus.Completed, result.getStatus())); + } + private void createAndSeedCollection(String collectionName) throws ExecutionException, InterruptedException { CreateCollection request = CreateCollection.newBuilder() .setCollectionName(collectionName) From ad7bf6b33501c083e2a17e4256ccdd335564c6c6 Mon Sep 17 00:00:00 2001 From: Anush008 Date: Sat, 30 Dec 2023 00:24:32 +0530 Subject: [PATCH 2/9] chore: update filter param doc comments --- src/main/java/io/qdrant/client/QdrantClient.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/qdrant/client/QdrantClient.java b/src/main/java/io/qdrant/client/QdrantClient.java index 6d02341..b34488a 100644 --- a/src/main/java/io/qdrant/client/QdrantClient.java +++ b/src/main/java/io/qdrant/client/QdrantClient.java @@ -1554,7 +1554,7 @@ public ListenableFuture overwritePayloadAsync( } /** - * Overwrites the payload for the given ids. + * Overwrites the payload for the filterd points. * * @param collectionName The name of the collection. * @param payload New payload values @@ -1699,7 +1699,7 @@ public ListenableFuture deletePayloadAsync( } /** - * Delete specified key payload for the given ids. + * Delete specified key payload for the filtered points. * * @param collectionName The name of the collection. * @param keys List of keys to delete. @@ -1835,7 +1835,7 @@ public ListenableFuture clearPayloadAsync( } /** - * Removes all payload for the given ids. + * Removes all payload for the filtered points. * * @param collectionName The name of the collection. * @param filter A filter selecting the points for which to remove the payload. From 5139936fcc272504e5044b76adbcdda9bbf00775 Mon Sep 17 00:00:00 2001 From: Anush008 Date: Sat, 30 Dec 2023 00:29:29 +0530 Subject: [PATCH 3/9] chore: upadate namedVectors() to accept Map --- src/main/java/io/qdrant/client/VectorsFactory.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/qdrant/client/VectorsFactory.java b/src/main/java/io/qdrant/client/VectorsFactory.java index 6fe9864..daf7489 100644 --- a/src/main/java/io/qdrant/client/VectorsFactory.java +++ b/src/main/java/io/qdrant/client/VectorsFactory.java @@ -7,6 +7,7 @@ import static io.qdrant.client.VectorFactory.vector; import static io.qdrant.client.grpc.Points.NamedVectors; +import io.qdrant.client.grpc.Points.Vector; import static io.qdrant.client.grpc.Points.Vectors; /** @@ -18,13 +19,13 @@ private VectorsFactory() { /** * Creates named vectors - * @param values A map of vector names to values + * @param values A map of vector names to {@link Vector} * @return a new instance of {@link Vectors} */ - public static Vectors namedVectors(Map> values) { + public static Vectors namedVectors(Map values) { return Vectors.newBuilder() .setVectors(NamedVectors.newBuilder() - .putAllVectors(Maps.transformValues(values, v -> vector(v))) + .putAllVectors(Maps.transformValues(values, v -> v)) ) .build(); } From ecd3c7c131c9e6d31d010eb2a731a31ee5eb1c8d Mon Sep 17 00:00:00 2001 From: Anush Date: Sat, 30 Dec 2023 01:15:41 +0530 Subject: [PATCH 4/9] chore: typo fix QdrantClient.java --- src/main/java/io/qdrant/client/QdrantClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/qdrant/client/QdrantClient.java b/src/main/java/io/qdrant/client/QdrantClient.java index b34488a..615a216 100644 --- a/src/main/java/io/qdrant/client/QdrantClient.java +++ b/src/main/java/io/qdrant/client/QdrantClient.java @@ -1554,7 +1554,7 @@ public ListenableFuture overwritePayloadAsync( } /** - * Overwrites the payload for the filterd points. + * Overwrites the payload for the filtered points. * * @param collectionName The name of the collection. * @param payload New payload values From 2754387938094eb528c24ac1cc581b0d24fae392 Mon Sep 17 00:00:00 2001 From: Anush Date: Sat, 6 Jan 2024 22:26:05 +0530 Subject: [PATCH 5/9] Apply suggestions from code review Co-authored-by: Russ Cam --- src/main/java/io/qdrant/client/QdrantClient.java | 7 +++++-- src/main/java/io/qdrant/client/VectorsFactory.java | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/qdrant/client/QdrantClient.java b/src/main/java/io/qdrant/client/QdrantClient.java index 615a216..1d59206 100644 --- a/src/main/java/io/qdrant/client/QdrantClient.java +++ b/src/main/java/io/qdrant/client/QdrantClient.java @@ -2257,13 +2257,16 @@ public ListenableFuture batchUpdateAsync( * * @return a new instance of {@link ListenableFuture} */ - public ListenableFuture batchUpdateAsync(UpdateBatchPoints request, @Nullable Duration timeout) { + public ListenableFuture> batchUpdateAsync(UpdateBatchPoints request, @Nullable Duration timeout) { String collectionName = request.getCollectionName(); Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty"); logger.debug("Batch update points on '{}'", collectionName); ListenableFuture future = getPoints(timeout).updateBatch(request); addLogFailureCallback(future, "Batch update points"); - return future; + return Futures.transform( + future, + UpdateBatchResponse::getResultList, + MoreExecutors.directExecutor()); } /** diff --git a/src/main/java/io/qdrant/client/VectorsFactory.java b/src/main/java/io/qdrant/client/VectorsFactory.java index daf7489..bd3e285 100644 --- a/src/main/java/io/qdrant/client/VectorsFactory.java +++ b/src/main/java/io/qdrant/client/VectorsFactory.java @@ -25,7 +25,7 @@ private VectorsFactory() { public static Vectors namedVectors(Map values) { return Vectors.newBuilder() .setVectors(NamedVectors.newBuilder() - .putAllVectors(Maps.transformValues(values, v -> v)) + .putAllVectors(values) ) .build(); } From ebef0318b21db73e12e338dd5090e870c1c15b09 Mon Sep 17 00:00:00 2001 From: Anush008 Date: Sat, 6 Jan 2024 22:30:57 +0530 Subject: [PATCH 6/9] chore: review updates --- src/main/java/io/qdrant/client/QdrantClient.java | 4 ++-- src/test/java/io/qdrant/client/PointsTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/qdrant/client/QdrantClient.java b/src/main/java/io/qdrant/client/QdrantClient.java index 1d59206..9c057ff 100644 --- a/src/main/java/io/qdrant/client/QdrantClient.java +++ b/src/main/java/io/qdrant/client/QdrantClient.java @@ -2215,7 +2215,7 @@ public ListenableFuture> recommendBatchAsync( * * @return a new instance of {@link ListenableFuture} */ - public ListenableFuture batchUpdateAsync(String collectionName, List operations) { + public ListenableFuture> batchUpdateAsync(String collectionName, List operations) { return batchUpdateAsync(collectionName, operations, null, null, null); } @@ -2230,7 +2230,7 @@ public ListenableFuture batchUpdateAsync(String collectionN * * @return a new instance of {@link ListenableFuture} */ - public ListenableFuture batchUpdateAsync( + public ListenableFuture> batchUpdateAsync( String collectionName, List operations, @Nullable Boolean wait, diff --git a/src/test/java/io/qdrant/client/PointsTest.java b/src/test/java/io/qdrant/client/PointsTest.java index 8bc7f5e..7dba74b 100644 --- a/src/test/java/io/qdrant/client/PointsTest.java +++ b/src/test/java/io/qdrant/client/PointsTest.java @@ -565,9 +565,9 @@ public void batchPointUpdate() throws ExecutionException, InterruptedException { .setVectors(vectors(0.6f, 0.7f)))) .build()); - UpdateBatchResponse response = client.batchUpdateAsync(testName, operations).get(); + List response = client.batchUpdateAsync(testName, operations).get(); - response.getResultList().forEach(result -> assertEquals(UpdateStatus.Completed, result.getStatus())); + response.forEach(result -> assertEquals(UpdateStatus.Completed, result.getStatus())); } private void createAndSeedCollection(String collectionName) throws ExecutionException, InterruptedException { From 2579be1af9a9c945dd928f5a8c32c842c9764761 Mon Sep 17 00:00:00 2001 From: Anush Date: Sat, 6 Jan 2024 23:03:45 +0530 Subject: [PATCH 7/9] chore: simplify examples README.md --- README.md | 57 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index d7fb804..20e5835 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,8 @@ implementation 'io.qdrant:client:1.7.1' ## 📖 Documentation -- [`QdrantClient` Reference](https://qdrant.github.io/java-client/io/qdrant/client/QdrantClient.html#constructor-detail) +- [JavaDoc Reference](https://qdrant.github.io/java-client/io/qdrant/client/QdrantClient.html#constructor-detail) +- Usage examples are available throughout the [Qdrant documentation](https://qdrant.tech/documentation/quick-start/) ## 🔌 Getting started @@ -127,20 +128,25 @@ import static io.qdrant.client.PointIdFactory.id; import static io.qdrant.client.ValueFactory.value; import static io.qdrant.client.VectorsFactory.vectors; -Random random = new Random(); -List points = IntStream.range(1, 101) - .mapToObj(i -> PointStruct.newBuilder() - .setId(id(i)) - .setVectors(vectors(IntStream.range(1, 101) - .mapToObj(v -> random.nextFloat()) - .collect(Collectors.toList()))) - .putAllPayload(ImmutableMap.of( - "color", value("red"), - "rand_number", value(i % 10)) - ) - .build() - ) - .collect(Collectors.toList()); +List points = + List.of( + PointStruct.newBuilder() + .setId(id(1)) + .setVectors(vectors(0.32f, 0.52f, 0.21f, 0.52f)) + .putAllPayload( + Map.of( + "color", value("red"), + "rand_number", value(32))) + .build(), + PointStruct.newBuilder() + .setId(id(2)) + .setVectors(vectors(0.42f, 0.52f, 0.67f, 0.632f)) + .putAllPayload( + Map.of( + "color", value("black"), + "rand_number", value(53), + "extra_field", value(true))) + .build()); UpdateResult updateResult = client.upsertAsync("my_collection", points).get(); ``` @@ -148,16 +154,15 @@ UpdateResult updateResult = client.upsertAsync("my_collection", points).get(); Search for similar vectors ```java -List queryVector = IntStream.range(1, 101) - .mapToObj(v -> random.nextFloat()) - .collect(Collectors.toList()); - -List points = client.searchAsync(SearchPoints.newBuilder() - .setCollectionName("my_collection") - .addAllVector(queryVector) - .setLimit(5) - .build() -).get(); +List anush = + client + .searchAsync( + SearchPoints.newBuilder() + .setCollectionName("my_collection") + .addAllVector(List.of(0.6235f, 0.123f, 0.532f, 0.123f)) + .setLimit(5) + .build()) + .get(); ``` Search for similar vectors with filtering condition @@ -168,7 +173,7 @@ import static io.qdrant.client.ConditionFactory.range; List points = client.searchAsync(SearchPoints.newBuilder() .setCollectionName("my_collection") - .addAllVector(queryVector) + .addAllVector(List.of(0.6235f, 0.123f, 0.532f, 0.123f)) .setFilter(Filter.newBuilder() .addMust(range("rand_number", Range.newBuilder().setGte(3).build())) .build()) From 626b430cb9a21ee7ffe9d5a9e963797dc884513e Mon Sep 17 00:00:00 2001 From: Anush Date: Sat, 6 Jan 2024 23:04:54 +0530 Subject: [PATCH 8/9] docs: simplify examples README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20e5835..961c4d9 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ To install the library, add the following lines to your build config file. ``` -#### Scala SBT +#### SBT ```sbt libraryDependencies += "io.qdrant" % "client" % "1.7.1" From d2ad03fe60187d125ed729e43bc0cbc5def995cc Mon Sep 17 00:00:00 2001 From: Anush Date: Sun, 7 Jan 2024 08:34:47 +0530 Subject: [PATCH 9/9] docs: Updated doc URLREADME.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 961c4d9..46c52d5 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ implementation 'io.qdrant:client:1.7.1' ## 📖 Documentation -- [JavaDoc Reference](https://qdrant.github.io/java-client/io/qdrant/client/QdrantClient.html#constructor-detail) +- [JavaDoc Reference](https://qdrant.github.io/java-client/) - Usage examples are available throughout the [Qdrant documentation](https://qdrant.tech/documentation/quick-start/) ## 🔌 Getting started