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 =