diff --git a/.gitignore b/.gitignore index b513607..761f16b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ build/ .idea/libraries/ .idea/uiDesigner.xml .idea/codeStyles/codeStyleConfig.xml +.idea/codeStyles/Project.xml *.iws *.iml *.ipr diff --git a/README.md b/README.md index e577abd..2082d3f 100644 --- a/README.md +++ b/README.md @@ -34,14 +34,14 @@ To install the library, add the following lines to your build config file. io.qdrant client - 1.11.0 + 1.12.0 ``` #### SBT ```sbt -libraryDependencies += "io.qdrant" % "client" % "1.11.0" +libraryDependencies += "io.qdrant" % "client" % "1.12.0" ``` #### Gradle diff --git a/gradle.properties b/gradle.properties index 01f86b6..32efcbd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ # The version of qdrant to use to download protos -qdrantProtosVersion=v1.11.0 +qdrantProtosVersion=v1.12.0 # The version of qdrant docker image to run integration tests against -qdrantVersion=v1.11.0 +qdrantVersion=v1.12.0 # The version of the client to generate -packageVersion=1.11.0 +packageVersion=1.12.0 diff --git a/src/main/java/io/qdrant/client/QdrantClient.java b/src/main/java/io/qdrant/client/QdrantClient.java index e525b0c..712f850 100644 --- a/src/main/java/io/qdrant/client/QdrantClient.java +++ b/src/main/java/io/qdrant/client/QdrantClient.java @@ -40,6 +40,7 @@ import io.qdrant.client.grpc.Collections.VectorsConfig; import io.qdrant.client.grpc.CollectionsGrpc; import io.qdrant.client.grpc.JsonWithInt.Value; +import io.qdrant.client.grpc.Points; import io.qdrant.client.grpc.Points.BatchResult; import io.qdrant.client.grpc.Points.ClearPayloadPoints; import io.qdrant.client.grpc.Points.CountPoints; @@ -2804,6 +2805,103 @@ public ListenableFuture> queryGroupsAsync( future, response -> response.getResult().getGroupsList(), MoreExecutors.directExecutor()); } + /** + * Perform facet counts. For each value in the field, count the number of points that have this + * value and match the conditions. + * + * @param request the facet counts request + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture> facetAsync(Points.FacetCounts request) { + return facetAsync(request, null); + } + + /** + * Perform facet counts. For each value in the field, count the number of points that have this + * value and match the conditions. + * + * @param request the facet counts request + * @param timeout the timeout for the call. + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture> facetAsync( + Points.FacetCounts request, @Nullable Duration timeout) { + Preconditions.checkArgument( + !request.getCollectionName().isEmpty(), "Collection name must not be empty"); + + logger.debug("Facet on '{}'", request.getCollectionName()); + ListenableFuture future = getPoints(timeout).facet(request); + addLogFailureCallback(future, "Facet"); + return Futures.transform( + future, Points.FacetResponse::getHitsList, MoreExecutors.directExecutor()); + } + + // region distance matrix + + /** + * Compute distance matrix for sampled points with a pair based output format. + * + * @param request the search matrix pairs request + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture searchMatrixPairsAsync( + Points.SearchMatrixPoints request) { + return searchMatrixPairsAsync(request, null); + } + + /** + * Compute distance matrix for sampled points with a pair based output format. + * + * @param request the search matrix pairs request + * @param timeout the timeout for the call. + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture searchMatrixPairsAsync( + Points.SearchMatrixPoints request, @Nullable Duration timeout) { + Preconditions.checkArgument( + !request.getCollectionName().isEmpty(), "Collection name must not be empty"); + + logger.debug("Search matrix pairs on '{}'", request.getCollectionName()); + ListenableFuture future = + getPoints(timeout).searchMatrixPairs(request); + addLogFailureCallback(future, "Search matrix pairs"); + return Futures.transform( + future, Points.SearchMatrixPairsResponse::getResult, MoreExecutors.directExecutor()); + } + + /** + * Compute distance matrix for sampled points with an offset based output format + * + * @param request the search matrix pairs request + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture searchMatrixOffsetsAsync( + Points.SearchMatrixPoints request) { + return searchMatrixOffsetsAsync(request, null); + } + + /** + * Compute distance matrix for sampled points with an offset based output format + * + * @param request the search matrix pairs request + * @param timeout the timeout for the call. + * @return a new instance of {@link ListenableFuture} + */ + public ListenableFuture searchMatrixOffsetsAsync( + Points.SearchMatrixPoints request, @Nullable Duration timeout) { + Preconditions.checkArgument( + !request.getCollectionName().isEmpty(), "Collection name must not be empty"); + + logger.debug("Search matrix offsets on '{}'", request.getCollectionName()); + ListenableFuture future = + getPoints(timeout).searchMatrixOffsets(request); + addLogFailureCallback(future, "Search matrix offsets"); + return Futures.transform( + future, Points.SearchMatrixOffsetsResponse::getResult, MoreExecutors.directExecutor()); + } + + // endregion + // region Snapshot Management /** diff --git a/src/test/java/io/qdrant/client/PointsTest.java b/src/test/java/io/qdrant/client/PointsTest.java index 9549a24..a999980 100644 --- a/src/test/java/io/qdrant/client/PointsTest.java +++ b/src/test/java/io/qdrant/client/PointsTest.java @@ -813,6 +813,80 @@ public void queryGroups() throws ExecutionException, InterruptedException { assertEquals(1, groups.stream().filter(g -> g.getHitsCount() == 1).count()); } + @Test + public void searchMatrixOffsets() throws ExecutionException, InterruptedException { + createAndSeedCollection(testName); + + Points.SearchMatrixOffsets offsets = + client + .searchMatrixOffsetsAsync( + Points.SearchMatrixPoints.newBuilder() + .setCollectionName(testName) + .setSample(3) + .setLimit(2) + .build()) + .get(); + + // Number of ids matches the limit + assertEquals(2, offsets.getIdsCount()); + } + + @Test + public void searchMatrixPairs() throws ExecutionException, InterruptedException { + createAndSeedCollection(testName); + + Points.SearchMatrixPairs pairs = + client + .searchMatrixPairsAsync( + Points.SearchMatrixPoints.newBuilder() + .setCollectionName(testName) + .setSample(3) + .setLimit(2) + .build()) + .get(); + + // Number of ids matches the limit + assertEquals(2, pairs.getPairsCount()); + } + + @Test + public void facets() throws ExecutionException, InterruptedException { + createAndSeedCollection(testName); + + // create payload index for "foo" field + UpdateResult result = + client + .createPayloadIndexAsync( + testName, "foo", PayloadSchemaType.Keyword, null, null, null, null) + .get(); + + assertEquals(UpdateStatus.Completed, result.getStatus()); + + List facets = + client + .facetAsync( + Points.FacetCounts.newBuilder() + .setCollectionName(testName) + .setKey("foo") + .setLimit(2) + .build()) + .get(); + + // Number of facets matches the limit + assertEquals(2, facets.size()); + // validate hits + assertEquals( + 1, + facets.stream() + .filter(f -> f.getValue().getStringValue().equals("hello") && f.getCount() == 1) + .count()); + assertEquals( + 1, + facets.stream() + .filter(f -> f.getValue().getStringValue().equals("goodbye") && f.getCount() == 1) + .count()); + } + private void createAndSeedCollection(String collectionName) throws ExecutionException, InterruptedException { CreateCollection request =