diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..d19e182d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,18 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = tab
+indent_size = 4
+
+[*.{md,markdown,json,js,xml,yml}]
+indent_style = space
+indent_size = 2
+
+[*.{md,markdown}]
+trim_trailing_whitespace = false
+max_line_length = 80
+
+[*.{sh,bat,ps1}]
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..3776e59c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,11 @@
+# Auto detect text files and perform LF normalization
+* text=auto eol=lf
+
+# Set default behavior for command prompt diff.
+# This gives output on command line taking java language constructs into consideration (e.g showing class name)
+*.java text diff=java
+
+# Set windows specific files explicitly to crlf line ending
+*.cmd eol=crlf
+*.bat eol=crlf
+*.ps1 eol=crlf
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..9aafe3a8
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,65 @@
+name: Build
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request_target:
+ branches: [ "master" ]
+
+permissions:
+ contents: read
+ checks: write
+
+jobs:
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+
+ - name: Build
+ uses: gradle/gradle-build-action@v2
+ with:
+ gradle-version: 8.5
+ arguments: build
+
+ - name: Test
+ uses: gradle/gradle-build-action@v2
+ with:
+ gradle-version: 8.5
+ arguments: test
+
+ - name: Test Results
+ uses: mikepenz/action-junit-report@v4
+ if: always()
+ with:
+ fail_on_failure: true
+ require_tests: true
+ report_paths: '**/build/test-results/test/TEST-*.xml'
+
+ - name: Upload Jars
+ uses: actions/upload-artifact@v3
+ with:
+ name: QdrantJava
+ path: build/libs
+
+ publish:
+ runs-on: ubuntu-latest
+ needs: test
+ steps:
+ - name: Deploy javadoc to Github Pages
+ uses: dev-vince/actions-publish-javadoc@v1.0.1
+ with:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ java-version: "8"
+ java-distribution: "adopt" # The distributor of the target JDK. See https://github.com/actions/setup-java for more information.
+ project: maven # The project type.
+ branch: "gh-pages" # The branch for the javadoc contents.
diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
deleted file mode 100644
index 4b345eba..00000000
--- a/.github/workflows/cd.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: Test And Deploy
-
-permissions:
- contents: write
-
-on:
- push:
- branches:
- - master
-
-jobs:
- test:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- java-version: ["8", "11", "16", "17", "21"]
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-java@v3
- with:
- java-version: ${{ matrix.java-version }}
- distribution: "temurin"
- - name: Set execute permission
- run: |
- chmod +x ./tools/mvn_test.sh
- - name: Maven tests
- run: |
- ./tools/mvn_test.sh
- shell: bash
-
- publish:
- runs-on: ubuntu-latest
- needs: test
- steps:
- - name: Deploy javadoc to Github Pages
- uses: dev-vince/actions-publish-javadoc@v1.0.1
- with:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- java-version: "8"
- java-distribution: "adopt" # The distributor of the target JDK. See https://github.com/actions/setup-java for more information.
- project: maven # The project type.
- branch: "gh-pages" # The branch for the javadoc contents.
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index cd540363..00000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-name: Maven Tests
-
-on:
- pull_request:
- types:
- - opened
- - edited
- - synchronize
- - reopened
-
-jobs:
- test:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- java-version: ["8", "11", "16", "17", "21"]
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-java@v3
- with:
- java-version: ${{ matrix.java-version }}
- distribution: "temurin"
- - name: Set execute permission
- run: |
- chmod +x ./tools/mvn_test.sh
- - name: Maven tests
- run: |
- ./tools/mvn_test.sh
- shell: bash
diff --git a/.gitignore b/.gitignore
index 94c668b5..b5136074 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,47 @@
-target/
-pom.xml.tag
-pom.xml.releaseBackup
-pom.xml.versionsBackup
-pom.xml.next
-release.properties
-dependency-reduced-pom.xml
-buildNumber.properties
-.mvn/timing.properties
-# https://github.com/takari/maven-wrapper#usage-without-binary-jar
-.mvn/wrapper/maven-wrapper.jar
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
-# Eclipse m2e generated files
-# Eclipse Core
-.project
-# JDT-specific (Eclipse Java Development Tools)
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+.idea/uiDesigner.xml
+.idea/codeStyles/codeStyleConfig.xml
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### Eclipse ###
+.apt_generated
.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
-# VS Code
-.vscode/*
+### Mac OS ###
+.DS_Store
-# Others
-.DS_Store
\ No newline at end of file
+### Project specific ###
+protos
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..13566b81
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 00000000..2a11f8b9
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+client
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 00000000..e7c302f9
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..11303e26
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..94a25f7f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..05b20cb8
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,124 @@
+# Contributing to Java client for Qdrant
+
+We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
+
+- Reporting a bug
+- Discussing the current state of the code
+- Submitting a fix
+- Proposing new features
+
+## We Develop with GitHub
+
+We use github to host code, to track issues and feature requests, as well as accept pull requests.
+
+We Use [GitHub Flow](https://docs.github.com/en/get-started/quickstart/github-flow), so all code changes
+happen through Pull Requests. Pull requests are the best way to propose changes to the codebase.
+
+It's usually best to open an issue first to discuss a feature or bug before opening a pull request.
+Doing so can save time and help further ascertain the crux of an issue.
+
+1. See if there is an existing issue
+2. Fork the repo and create your branch from `master`.
+3. If you've added code that should be tested, add tests.
+4. Ensure the test suite passes.
+5. Issue that pull request!
+
+### Any contributions you make will be under the Apache License 2.0
+
+In short, when you submit code changes, your submissions are understood to be under the
+same [Apache License 2.0](https://choosealicense.com/licenses/apache-2.0/) that covers the project.
+Feel free to contact the maintainers if that's a concern.
+
+## Report bugs using GitHub's [issues](https://github.com/qdrant/java-client/issues)
+
+We use GitHub issues to track public bugs. Report a bug by
+[opening a new issue](https://github.com/qdrant/java-client/issues/new); it's that easy!
+
+**Great Bug Reports** tend to have:
+
+- A quick summary and/or background
+- Steps to reproduce
+ - Be specific!
+ - Give sample code if you can.
+- What you expected would happen
+- What actually happens
+- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
+
+## Coding Styleguide
+
+If you are modifying code, make sure it has no warnings when building.
+
+## License
+
+By contributing, you agree that your contributions will be licensed under its Apache License 2.0.
+
+# Building the solution
+
+The solution uses several open source software tools:
+
+## Docker
+
+Qdrant docker image is used to run integration tests. Be sure to
+[install docker](https://docs.docker.com/engine/install/) and have it running when running tests.
+
+## Gradle
+
+[Gradle](https://docs.gradle.org/current/userguide/userguide.html) is used as the build automation tool for the solution.
+To get started after cloning the solution, it's best to run the gradlew wrapper script in the root
+
+for Windows
+
+```
+.\gradlew.bat build
+```
+
+for OSX/Linux
+
+```
+./gradlew build
+```
+
+This will
+
+- Pull down all the dependencies for the build process as well as the solution
+- Run the default build task for the solution
+
+You can also compile the solution within IntelliJ or other IDEs if you prefer.
+
+## Tests
+
+jUnit5 tests are run as part of the default build task. These can also be run with
+
+```
+./gradlew test
+```
+
+## Updating the client
+
+A large portion of the client is generated from the upstream qdrant proto definitions, which are
+downloaded locally as needed, based on the version defined by `qdrantProtosVersion` in gradle.properties
+in the root directory.
+
+When a new qdrant version is released upstream, update the `qdrantProtosVersion` value to the new version,
+then run the build script
+
+for Windows
+
+```
+.\gradlew.bat build
+```
+
+for OSX/Linux
+
+```
+./gradlew build
+```
+
+A specific version of the qdrant docker image can be targeted by modifying the `qdrantVersion`
+in gradle.properties.
+
+The generated files do not form part of the checked in source code. Instead, they are generated
+and emitted into the build/generated/source directory, and included in compilation.
+
+If upstream changes to proto definitions change the API of generated code, you may need
+to fix compilation errors in code that relies on that generated code.
\ No newline at end of file
diff --git a/README.md b/README.md
index 12b4950c..2c8757c5 100644
--- a/README.md
+++ b/README.md
@@ -31,133 +31,153 @@ Java client library with handy utility methods and overloads for interfacing wit
To install the library, add the following lines to your build config file.
#### Maven
+
```xml
io.qdrant
client
- 1.0
+ 1.7-SNAPSHOT
```
#### Scala SBT
+
```sbt
-libraryDependencies += "io.qdrant" % "client" % "1.0"
+libraryDependencies += "io.qdrant" % "client" % "1.7-SNAPSHOT"
```
#### Gradle
+
```gradle
-implementation 'io.qdrant:client:1.0'
+implementation 'io.qdrant:client:1.7-SNAPSHOT'
```
## 📖 Documentation
+
- [`QdrantClient` Reference](https://qdrant.github.io/java-client/io/qdrant/client/QdrantClient.html#constructor-detail)
-- [Utility Methods Reference](https://qdrant.github.io/java-client/io/qdrant/client/utils/package-summary.html)
-## 🔌 Connecting to Qdrant
+## 🔌 Getting started
-> [!NOTE]
-> The library uses Qdrant's GRPC interface. The default port being `6334`.
->
-> Uses `TLS` if the URL protocol is `https`, plaintext otherwise.
+### Creating a client
-#### Connecting to a local Qdrant instance
-```java
-import io.qdrant.client.QdrantClient;
+A client can be instantiated with
-QdrantClient client = new QdrantClient("http://localhost:6334");
+```java
+QdrantClient client =
+ new QdrantClient(QdrantGrpcClient.newBuilder("localhost").build());
```
+which creates a client that will connect to Qdrant on https://localhost:6334.
-#### Connecting to Qdrant cloud
-```java
-import io.qdrant.client.QdrantClient;
+Internally, the high level client uses a low level gRPC client to interact with
+Qdrant. Additional constructor overloads provide more control over how the gRPC
+client is configured. The following example configures a client to use TLS,
+validating the certificate using the root CA to verify the server's identity
+instead of the system's default, and also configures API key authentication:
-QdrantClient client = new QdrantClient("https://xyz-eg.eu-central.aws.cloud.qdrant.io:6334", "");
+```java
+ManagedChannel channel = Grpc.newChannelBuilder(
+ "localhost:6334",
+ TlsChannelCredentials.newBuilder()
+ .trustManager(new File("ssl/ca.crt"))
+ .build())
+.build();
+
+QdrantClient client = new QdrantClient(
+ QdrantGrpcClient.newBuilder(channel)
+ .withApiKey("")
+ .build());
```
-## 🧪 Example Usage
-
-Click to expand example
-
+The client implements [`AutoCloseable`](https://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html),
+though a client will typically be created once and used for the lifetime of the
+application. When a client is constructed by passing a `ManagedChannel`, the
+client does not shut down the channel on close by default. The client can be
+configured to shut down the channel on close with
-#### You can connect to Qdrant by instantiating a [QdrantClient](https://qdrant.github.io/java-client/io/qdrant/client/QdrantClient.html) instance.
```java
-import io.qdrant.client.QdrantClient;
+ManagedChannel channel = Grpc.newChannelBuilder(
+ "localhost:6334",
+ TlsChannelCredentials.create())
+.build();
+
+QdrantClient client = new QdrantClient(
+ QdrantGrpcClient.newBuilder(channel, true)
+ .withApiKey("")
+ .build());
+```
-QdrantClient client = new QdrantClient("http://localhost:6334");
+All client methods return `ListenableFuture`.
-System.out.println(client.listCollections());
-```
-*Output*:
-```
-collections {
-name: "Documents"
-}
-collections {
-name: "some_collection"
-}
-time: 7.04541E-4
-```
+### Working with collections
-#### We can now perform operations on the DB. Like creating a collection, adding a point.
-The library offers handy utility methods for constructing GRPC structures.
+Once a client has been created, create a new collection
```java
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import io.qdrant.client.utils.*;
+client.createCollectionAsync("my_collection",
+ VectorParams.newBuilder()
+ .setDistance(Distance.Cosine)
+ .setSize(4)
+ .build())
+ .get();
+```
-String collectionName = "Documents";
+Insert vectors into a collection
-client.recreateCollection(collectionName, 6, Distance.Cosine);
+```java
+// 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;
+
+Random random = new Random();
+List points = IntStream.range(1, 101)
+ .mapToObj(i -> PointStruct.newBuilder()
+ .setId(id(i))
+ .setVectors(vector(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());
+
+UpdateResult updateResult = client.upsertAsync("my_collection", points).get();
+```
-Map map = new HashMap<>();
-map.put("name", "John Doe");
-map.put("age", 42);
-map.put("married", true);
+Search for similar vectors
-PointStruct point =
- PointUtil.point(
- 0,
- VectorUtil.toVector(0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f),
- PayloadUtil.toPayload(map));
-List points = Arrays.asList(point);
-client.upsertPoints(collectionName, points, null);
+```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();
```
-#### Performing a search on the vectors with filtering
+Search for similar vectors with filtering condition
+
```java
-import io.qdrant.client.grpc.Points.Filter;
-import io.qdrant.client.grpc.Points.SearchPoints;
-import io.qdrant.client.grpc.Points.SearchResponse;
-
-import io.qdrant.client.utils.*;
-
-Filter filter = FilterUtil.must(FilterUtil.fieldCondition("age", FilterUtil.match(42)));
-
-SearchPoints request = SearchPoints.newBuilder()
- .setCollectionName(collectionName)
- .addAllVector(Arrays.asList(0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f))
- .setFilter(filter)
- .setWithPayload(SelectorUtil.withPayload())
- .setLimit(10)
- .build();
-SearchResponse result = client.searchPoints(request);
-
-ScoredPoint result = results.getResult(0);
-
-System.out.println("Similarity: " + result.getScore());
-System.out.println("Payload: " + PayloadUtil.toMap(result.getPayload()));
-```
-*Output*:
+// import static convenience methods
+import static io.qdrant.client.ConditionFactory.range;
+
+List points = client.searchAsync(SearchPoints.newBuilder()
+ .setCollectionName("my_collection")
+ .addAllVector(queryVector)
+ .setFilter(Filter.newBuilder()
+ .addMust(range("rand_number", Range.newBuilder().setGte(3).build()))
+ .build())
+ .setLimit(5)
+ .build()
+).get();
```
-Similarity: 0.9999999
-Payload: {name=John Doe, married=true, age=42}
-```
-
-
## ⚖️ LICENSE
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..722d6c0e
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,231 @@
+import net.ltgt.gradle.errorprone.CheckSeverity
+import org.ajoberstar.grgit.Grgit
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
+import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream
+import org.apache.http.client.methods.HttpGet
+import org.apache.http.impl.client.HttpClients
+
+import java.nio.file.Files
+import java.nio.file.Paths
+import java.nio.file.StandardOpenOption
+import java.time.ZoneOffset
+import java.util.regex.Pattern
+
+plugins {
+ id 'java-library'
+ id 'idea'
+ id 'signing'
+ id 'maven-publish'
+
+ id 'com.google.protobuf' version '0.9.4'
+ id "net.ltgt.errorprone" version '3.1.0'
+}
+
+group = 'io.qdrant'
+version = packageVersion
+description = 'Official Java client for Qdrant vector database'
+
+repositories {
+ // google mirror for maven
+ maven {
+ url 'https://maven-central.storage-download.googleapis.com/maven2/'
+ }
+ mavenCentral()
+ mavenLocal()
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+
+ withJavadocJar()
+ withSourcesJar()
+}
+
+tasks.withType(JavaCompile).configureEach {
+ // exclude generated code from error prone checks
+ options.errorprone.excludedPaths.set(".*/build/generated/.*")
+ options.errorprone {
+ //noinspection GroovyAssignabilityCheck
+ check("NullAway", CheckSeverity.ERROR)
+ option("NullAway:AnnotatedPackages", "com.uber")
+ }
+}
+
+javadoc {
+ // exclude code generated from protos
+ exclude 'io/qdrant/client/grpc/**'
+ exclude 'grpc/**'
+}
+
+sourcesJar {
+ // exclude generated duplicate of com/qdrant/client/grpc/CollectionsGrpc.java
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+}
+
+jar {
+ doFirst {
+ def git = Grgit.open(Map.of('currentDir', rootProject.rootDir))
+ // add qdrant version from which client is generated.
+ jar.manifest.attributes['X-Qdrant-Version'] = qdrantProtosVersion
+ // add git revision and commit time to jar manifest
+ jar.manifest.attributes['X-Git-Revision'] = git.head().id
+ jar.manifest.attributes['X-Git-Commit-Time'] = git.head().dateTime.withZoneSameLocal(ZoneOffset.UTC)
+ git.close()
+ }
+}
+
+def grpcVersion = '1.59.0'
+def protobufVersion = '3.24.0'
+def protocVersion = protobufVersion
+def testContainersVersion = '1.19.2'
+def jUnitVersion = '5.8.1'
+
+dependencies {
+ errorprone "com.uber.nullaway:nullaway:0.10.18"
+
+ implementation "io.grpc:grpc-protobuf:${grpcVersion}"
+ implementation "io.grpc:grpc-services:${grpcVersion}"
+ implementation "io.grpc:grpc-stub:${grpcVersion}"
+ implementation "com.google.guava:guava:30.1-jre"
+ implementation "org.slf4j:slf4j-api:2.0.7"
+
+ compileOnly "org.apache.tomcat:annotations-api:6.0.53"
+ compileOnly "com.google.code.findbugs:jsr305:3.0.2"
+
+ errorprone "com.google.errorprone:error_prone_core:2.23.0"
+
+ runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
+
+ testImplementation "io.grpc:grpc-testing:${grpcVersion}"
+ testImplementation "org.junit.jupiter:junit-jupiter-api:${jUnitVersion}"
+ testImplementation "org.mockito:mockito-core:3.4.0"
+ testImplementation "org.testcontainers:testcontainers:${testContainersVersion}"
+ testImplementation "org.testcontainers:junit-jupiter:${testContainersVersion}"
+
+ testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${jUnitVersion}"
+}
+
+tasks.register('downloadProtos') {
+ // gradle detects changes to this output dir since last run to determine whether to run this task
+ outputs.dir(new File(rootProject.rootDir, "protos/${qdrantProtosVersion}"))
+
+ doLast {
+ 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}"
+ logger.debug("downloading protos from {}", url)
+ def response = httpClient.execute(new HttpGet(url))
+ try (InputStream tarballStream = response.getEntity().getContent()) {
+ try (TarArchiveInputStream tarInput = new TarArchiveInputStream(new GzipCompressorInputStream(tarballStream))) {
+ TarArchiveEntry entry
+ while ((entry = tarInput.getNextTarEntry()) != null) {
+ if (!entry.isDirectory() && protoFileRegex.matcher(entry.getName()).matches()) {
+ def lines = new ArrayList()
+ def lineNum = -1
+ def seenJavaPackage = false
+ def br = new BufferedReader(new InputStreamReader(tarInput))
+ String line
+ while ((line = br.readLine()) != null) {
+ lines.add(line)
+ if (line == "package qdrant;") {
+ lineNum = lines.size()
+ } else if (line.startsWith("option java_package")) {
+ seenJavaPackage = true
+ }
+ }
+ // patch in java package to qdrant protos
+ if (!seenJavaPackage && lineNum != -1) {
+ lines.add(lineNum, "option java_package = \"io.qdrant.client.grpc\";")
+ }
+
+ def fileName = Paths.get(entry.getName()).getFileName().toString()
+ def dest = java.nio.file.Path.of(outputDirectory, fileName)
+ logger.debug("writing {} to {}", fileName, dest)
+ Files.write(dest, lines, StandardOpenOption.CREATE)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+processResources {
+ dependsOn downloadProtos
+}
+
+extractIncludeProto {
+ dependsOn downloadProtos
+}
+
+protobuf {
+ protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
+ plugins {
+ grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
+ }
+ generateProtoTasks {
+ all()*.plugins { grpc {} }
+ }
+}
+
+// Inform IDEs like IntelliJ IDEA, Eclipse or NetBeans about the generated code.
+sourceSets {
+ main {
+ proto {
+ // include protos from outside the sourceSets
+ //noinspection GroovyAssignabilityCheck
+ srcDir "protos/${qdrantProtosVersion}"
+ }
+ java {
+ srcDirs 'build/generated/source/proto/main/grpc'
+ srcDirs 'build/generated/source/proto/main/java'
+ }
+ }
+}
+
+test {
+ useJUnitPlatform()
+
+ // Set system property to use as docker image version for integration tests
+ systemProperty 'qdrantVersion', qdrantVersion
+}
+
+def organization = 'qdrant'
+def repository = 'java-client'
+
+publishing {
+ publications {
+ mavenJava(MavenPublication) {
+ from components.java
+ pom {
+ description = "${project.description}"
+ url = "https://github.com/${organization}/${repository}"
+ licenses {
+ license {
+ name = 'The Apache License, Version 2.0'
+ url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+ distribution = 'repo'
+ }
+ }
+ developers {
+ developer {
+ id = 'qdrant'
+ name = 'Qdrant and Contributors'
+ email = 'info@qdrant.com'
+ }
+ }
+ scm {
+ connection = "scm:git:git://github.com/${organization}/${repository}.git"
+ developerConnection = "scm:git:ssh://github.com/${organization}/${repository}.git"
+ url = "https://github.com/${organization}/${repository}"
+ }
+ }
+ }
+ }
+ repositories {
+ mavenLocal()
+ }
+}
\ No newline at end of file
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 00000000..d90e0954
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,9 @@
+dependencies {
+ implementation 'org.ajoberstar.grgit:grgit-gradle:5.0.0'
+ implementation 'org.apache.httpcomponents:httpclient:4.5.14'
+ implementation 'org.apache.commons:commons-compress:1.23.0'
+}
+
+repositories {
+ mavenCentral()
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..44e0858b
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,8 @@
+# The version of qdrant to use to download protos
+qdrantProtosVersion=v1.7.0
+
+# The version of qdrant docker image to run integration tests against
+qdrantVersion=v1.7.0
+
+# The version of the client to generate
+packageVersion=1.7-SNAPSHOT
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..033e24c4
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..1af9e093
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 00000000..fcb6fca1
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..93e3f59f
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index b728c7c6..00000000
--- a/pom.xml
+++ /dev/null
@@ -1,136 +0,0 @@
-
-
- 4.0.0
-
- io.qdrant
- client
-
- 1.0
-
-
-
- io.grpc
- grpc-netty-shaded
- 1.59.0
- runtime
-
-
- io.grpc
- grpc-protobuf
- 1.59.0
-
-
- io.grpc
- grpc-stub
- 1.59.0
-
-
- org.apache.tomcat
- annotations-api
- 6.0.53
- provided
-
-
-
- org.junit.jupiter
- junit-jupiter-engine
- 5.5.2
- test
-
-
-
-
-
-
-
- kr.motd.maven
- os-maven-plugin
- 1.7.1
-
-
-
-
- org.xolstice.maven.plugins
- protobuf-maven-plugin
- 0.6.1
-
- com.google.protobuf:protoc:3.24.0:exe:${os.detected.classifier}
- grpc-java
-
- io.grpc:protoc-gen-grpc-java:1.59.0:exe:${os.detected.classifier}
-
-
-
-
- compile
- compile-custom
-
-
-
-
-
- org.codehaus.mojo
- exec-maven-plugin
- 1.2.1
-
- io.qdrant.client.Main
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 3.0.0-M3
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 3.6.2
-
-
- maven-compiler-plugin
- 3.11.0
-
- 1.8
- 1.8
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 2.2.1
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- com.coveo
- fmt-maven-plugin
- 2.9
-
-
-
- format
-
-
-
-
-
-
-
-
- ossrh
- https://s01.oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 00000000..dd1a777f
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'client'
+
diff --git a/src/main/java/io/qdrant/client/ApiKeyCredentials.java b/src/main/java/io/qdrant/client/ApiKeyCredentials.java
new file mode 100644
index 00000000..d3a02e4f
--- /dev/null
+++ b/src/main/java/io/qdrant/client/ApiKeyCredentials.java
@@ -0,0 +1,35 @@
+package io.qdrant.client;
+
+import io.grpc.CallCredentials;
+import io.grpc.Metadata;
+import io.grpc.Status;
+
+import java.util.concurrent.Executor;
+
+/**
+ * API key authentication credentials
+ */
+public class ApiKeyCredentials extends CallCredentials {
+ private final String apiKey;
+
+ /**
+ * Instantiates a new instance of {@link ApiKeyCredentials}
+ * @param apiKey The API key to use for authentication
+ */
+ public ApiKeyCredentials(String apiKey) {
+ this.apiKey = apiKey;
+ }
+
+ @Override
+ public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) {
+ appExecutor.execute(() -> {
+ try {
+ Metadata headers = new Metadata();
+ headers.put(Metadata.Key.of("api-key", Metadata.ASCII_STRING_MARSHALLER), apiKey);
+ applier.apply(headers);
+ } catch (Throwable e) {
+ applier.fail(Status.UNAUTHENTICATED.withCause(e));
+ }
+ });
+ }
+}
diff --git a/src/main/java/io/qdrant/client/ConditionFactory.java b/src/main/java/io/qdrant/client/ConditionFactory.java
new file mode 100644
index 00000000..5af72f55
--- /dev/null
+++ b/src/main/java/io/qdrant/client/ConditionFactory.java
@@ -0,0 +1,373 @@
+package io.qdrant.client;
+
+import java.util.List;
+
+import static io.qdrant.client.grpc.Points.Condition;
+import static io.qdrant.client.grpc.Points.FieldCondition;
+import static io.qdrant.client.grpc.Points.Filter;
+import static io.qdrant.client.grpc.Points.GeoBoundingBox;
+import static io.qdrant.client.grpc.Points.GeoLineString;
+import static io.qdrant.client.grpc.Points.GeoPoint;
+import static io.qdrant.client.grpc.Points.GeoPolygon;
+import static io.qdrant.client.grpc.Points.GeoRadius;
+import static io.qdrant.client.grpc.Points.HasIdCondition;
+import static io.qdrant.client.grpc.Points.IsEmptyCondition;
+import static io.qdrant.client.grpc.Points.IsNullCondition;
+import static io.qdrant.client.grpc.Points.Match;
+import static io.qdrant.client.grpc.Points.NestedCondition;
+import static io.qdrant.client.grpc.Points.PointId;
+import static io.qdrant.client.grpc.Points.Range;
+import static io.qdrant.client.grpc.Points.RepeatedIntegers;
+import static io.qdrant.client.grpc.Points.RepeatedStrings;
+import static io.qdrant.client.grpc.Points.ValuesCount;
+
+/**
+ * Convenience methods for constructing {@link Condition}
+ */
+public final class ConditionFactory {
+ private ConditionFactory() {
+ }
+
+ /**
+ * Match all records with the provided id
+ * @param id The id to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition hasId(PointId id) {
+ return Condition.newBuilder()
+ .setHasId(HasIdCondition.newBuilder()
+ .addHasId(id)
+ .build())
+ .build();
+ }
+
+ /**
+ * Match all records with the provided ids
+ * @param ids The ids to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition hasId(List ids) {
+ return Condition.newBuilder()
+ .setHasId(HasIdCondition.newBuilder()
+ .addAllHasId(ids)
+ .build())
+ .build();
+ }
+
+ /**
+ * Match all records where the given field either does not exist, or has null or empty value.
+ * @param field The name of the field
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition isEmpty(String field) {
+ return Condition.newBuilder()
+ .setIsEmpty(IsEmptyCondition.newBuilder()
+ .setKey(field)
+ .build())
+ .build();
+ }
+
+ /**
+ * Match all records where the given field is null.
+ * @param field The name of the field
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition isNull(String field) {
+ return Condition.newBuilder()
+ .setIsNull(IsNullCondition.newBuilder()
+ .setKey(field)
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field matches the given keyword
+ * @param field The name of the field
+ * @param keyword The keyword to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition matchKeyword(String field, String keyword) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setMatch(Match.newBuilder()
+ .setKeyword(keyword)
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field matches the given text.
+ * @param field The name of the field
+ * @param text The text to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition matchText(String field, String text) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setMatch(Match.newBuilder()
+ .setText(text)
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field matches the given boolean value.
+ * @param field The name of the field
+ * @param value The value to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition match(String field, boolean value) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setMatch(Match.newBuilder()
+ .setBoolean(value)
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field matches the given long value.
+ * @param field The name of the field
+ * @param value The value to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition match(String field, long value) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setMatch(Match.newBuilder()
+ .setInteger(value)
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field matches any of the given keywords.
+ * @param field The name of the field
+ * @param keywords The keywords to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition matchKeywords(String field, List keywords) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setMatch(Match.newBuilder()
+ .setKeywords(RepeatedStrings.newBuilder().addAllStrings(keywords).build())
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field matches any of the given values.
+ * @param field The name of the field
+ * @param values The values to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition matchValues(String field, List values) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setMatch(Match.newBuilder()
+ .setIntegers(RepeatedIntegers.newBuilder().addAllIntegers(values).build())
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field does not match any of the given keywords.
+ * @param field The name of the field
+ * @param keywords The keywords not to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition matchExceptKeywords(String field, List keywords) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setMatch(Match.newBuilder()
+ .setExceptKeywords(RepeatedStrings.newBuilder().addAllStrings(keywords).build())
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field does not match any of the given values.
+ * @param field The name of the field
+ * @param values The values not to match
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition matchExceptValues(String field, List values) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setMatch(Match.newBuilder()
+ .setExceptIntegers(RepeatedIntegers.newBuilder().addAllIntegers(values).build())
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given nested field matches the given condition.
+ * @param field The name of the nested field.
+ * @param condition The condition to match.
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition nested(String field, Condition condition) {
+ return Condition.newBuilder()
+ .setNested(NestedCondition.newBuilder()
+ .setKey(field)
+ .setFilter(Filter.newBuilder()
+ .addMust(condition)
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given nested field matches the given filter.
+ * @param field The name of the nested field.
+ * @param filter The filter to match.
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition nested(String field, Filter filter) {
+ return Condition.newBuilder()
+ .setNested(NestedCondition.newBuilder()
+ .setKey(field)
+ .setFilter(filter))
+ .build();
+ }
+
+ /**
+ * Match records where the given field matches the given range.
+ * @param field The name of the nested field.
+ * @param range The range to match.
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition range(String field, Range range) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setRange(range)
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field has values inside a circle centred at a given latitude and longitude
+ * with a given radius.
+ * @param field The name of the field.
+ * @param latitude The latitude of the center.
+ * @param longitude The longitude of the center.
+ * @param radius The radius in meters.
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition geoRadius(String field, double latitude, double longitude, float radius) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setGeoRadius(GeoRadius.newBuilder()
+ .setCenter(GeoPoint.newBuilder()
+ .setLat(latitude)
+ .setLon(longitude)
+ .build())
+ .setRadius(radius)
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Match records where the given field has values inside a bounding box specified by the top left and
+ * bottom right coordinates.
+ * @param field The name of the field.
+ * @param topLeftLatitude The latitude of the top left point.
+ * @param topLeftLongitude The longitude of the top left point.
+ * @param bottomRightLatitude The latitude of the bottom right point.
+ * @param bottomRightLongitude The longitude of the bottom right point.
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition geoBoundingBox(
+ String field,
+ double topLeftLatitude,
+ double topLeftLongitude,
+ double bottomRightLatitude,
+ double bottomRightLongitude) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setGeoBoundingBox(GeoBoundingBox.newBuilder()
+ .setTopLeft(GeoPoint.newBuilder()
+ .setLat(topLeftLatitude)
+ .setLon(topLeftLongitude)
+ .build())
+ .setBottomRight(GeoPoint.newBuilder()
+ .setLat(bottomRightLatitude)
+ .setLon(bottomRightLongitude)
+ .build())
+ .build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Matches records where the given field has values inside the provided polygon. A polygon always has an exterior
+ * ring and may optionally have interior rings, which represent independent areas or holes.
+ * When defining a ring, you must pick either a clockwise or counterclockwise ordering for your points.
+ * The first and last point of the polygon must be the same.
+ * @param field The name of the field.
+ * @param exterior The exterior ring of the polygon.
+ * @param interiors The interior rings of the polygon.
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition geoPolygon(String field, GeoLineString exterior, List interiors) {
+ GeoPolygon.Builder geoPolygonBuilder = GeoPolygon.newBuilder()
+ .setExterior(exterior);
+
+ if (!interiors.isEmpty()) {
+ geoPolygonBuilder.addAllInteriors(interiors);
+ }
+
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setGeoPolygon(geoPolygonBuilder.build())
+ .build())
+ .build();
+ }
+
+ /**
+ * Matches records where the given field has a count of values within the specified count range
+ * @param field The name of the field.
+ * @param valuesCount The count range to match.
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition valuesCount(String field, ValuesCount valuesCount) {
+ return Condition.newBuilder()
+ .setField(FieldCondition.newBuilder()
+ .setKey(field)
+ .setValuesCount(valuesCount)
+ .build())
+ .build();
+ }
+
+ /**
+ * Nests a filter
+ * @param filter The filter to nest.
+ * @return a new instance of {@link Condition}
+ */
+ public static Condition filter(Filter filter) {
+ return Condition.newBuilder()
+ .setFilter(filter)
+ .build();
+ }
+}
diff --git a/src/main/java/io/qdrant/client/PointIdFactory.java b/src/main/java/io/qdrant/client/PointIdFactory.java
new file mode 100644
index 00000000..ef5cad05
--- /dev/null
+++ b/src/main/java/io/qdrant/client/PointIdFactory.java
@@ -0,0 +1,31 @@
+package io.qdrant.client;
+
+import java.util.UUID;
+
+import static io.qdrant.client.grpc.Points.PointId;
+
+/**
+ * Convenience methods for constructing {@link PointId}
+ */
+public final class PointIdFactory {
+ private PointIdFactory() {
+ }
+
+ /**
+ * Creates a point id from a {@link long}
+ * @param id The id
+ * @return a new instance of {@link PointId}
+ */
+ public static PointId id(long id) {
+ return PointId.newBuilder().setNum(id).build();
+ }
+
+ /**
+ * Creates a point id from a {@link UUID}
+ * @param id The id
+ * @return a new instance of {@link PointId}
+ */
+ public static PointId id(UUID id) {
+ return PointId.newBuilder().setUuid(id.toString()).build();
+ }
+}
diff --git a/src/main/java/io/qdrant/client/QdrantClient.java b/src/main/java/io/qdrant/client/QdrantClient.java
index 7bd73bab..680f29ed 100644
--- a/src/main/java/io/qdrant/client/QdrantClient.java
+++ b/src/main/java/io/qdrant/client/QdrantClient.java
@@ -1,1391 +1,2421 @@
package io.qdrant.client;
-import io.grpc.Deadline;
-import io.grpc.ManagedChannel;
-import io.grpc.ManagedChannelBuilder;
-import io.qdrant.client.grpc.Collections;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
import io.qdrant.client.grpc.CollectionsGrpc;
-import io.qdrant.client.grpc.JsonWithInt.Value;
-import io.qdrant.client.grpc.Points;
import io.qdrant.client.grpc.PointsGrpc;
-import io.qdrant.client.grpc.QdrantGrpc;
-import io.qdrant.client.grpc.QdrantOuterClass;
import io.qdrant.client.grpc.SnapshotsGrpc;
-import io.qdrant.client.grpc.SnapshotsService;
-import io.qdrant.client.grpc.SnapshotsService.SnapshotDescription;
-import io.qdrant.client.utils.PointUtil;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.nio.file.Path;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
-import javax.annotation.Nullable;
-
-/** Client for interfacing with the Qdrant service. */
+import java.util.stream.Collectors;
+
+import static io.qdrant.client.grpc.Collections.AliasDescription;
+import static io.qdrant.client.grpc.Collections.AliasOperations;
+import static io.qdrant.client.grpc.Collections.ChangeAliases;
+import static io.qdrant.client.grpc.Collections.CollectionDescription;
+import static io.qdrant.client.grpc.Collections.CollectionInfo;
+import static io.qdrant.client.grpc.Collections.CollectionOperationResponse;
+import static io.qdrant.client.grpc.Collections.CreateAlias;
+import static io.qdrant.client.grpc.Collections.CreateCollection;
+import static io.qdrant.client.grpc.Collections.DeleteAlias;
+import static io.qdrant.client.grpc.Collections.DeleteCollection;
+import static io.qdrant.client.grpc.Collections.GetCollectionInfoRequest;
+import static io.qdrant.client.grpc.Collections.GetCollectionInfoResponse;
+import static io.qdrant.client.grpc.Collections.ListAliasesRequest;
+import static io.qdrant.client.grpc.Collections.ListAliasesResponse;
+import static io.qdrant.client.grpc.Collections.ListCollectionAliasesRequest;
+import static io.qdrant.client.grpc.Collections.ListCollectionsRequest;
+import static io.qdrant.client.grpc.Collections.ListCollectionsResponse;
+import static io.qdrant.client.grpc.Collections.PayloadIndexParams;
+import static io.qdrant.client.grpc.Collections.PayloadSchemaType;
+import static io.qdrant.client.grpc.Collections.RenameAlias;
+import static io.qdrant.client.grpc.Collections.UpdateCollection;
+import static io.qdrant.client.grpc.Collections.VectorParams;
+import static io.qdrant.client.grpc.Collections.VectorParamsMap;
+import static io.qdrant.client.grpc.Collections.VectorsConfig;
+import static io.qdrant.client.grpc.JsonWithInt.Value;
+import static io.qdrant.client.grpc.Points.BatchResult;
+import static io.qdrant.client.grpc.Points.ClearPayloadPoints;
+import static io.qdrant.client.grpc.Points.CountPoints;
+import static io.qdrant.client.grpc.Points.CountResponse;
+import static io.qdrant.client.grpc.Points.CreateFieldIndexCollection;
+import static io.qdrant.client.grpc.Points.DeleteFieldIndexCollection;
+import static io.qdrant.client.grpc.Points.DeletePayloadPoints;
+import static io.qdrant.client.grpc.Points.DeletePointVectors;
+import static io.qdrant.client.grpc.Points.DeletePoints;
+import static io.qdrant.client.grpc.Points.FieldType;
+import static io.qdrant.client.grpc.Points.Filter;
+import static io.qdrant.client.grpc.Points.GetPoints;
+import static io.qdrant.client.grpc.Points.GetResponse;
+import static io.qdrant.client.grpc.Points.PointGroup;
+import static io.qdrant.client.grpc.Points.PointId;
+import static io.qdrant.client.grpc.Points.PointStruct;
+import static io.qdrant.client.grpc.Points.PointVectors;
+import static io.qdrant.client.grpc.Points.PointsIdsList;
+import static io.qdrant.client.grpc.Points.PointsOperationResponse;
+import static io.qdrant.client.grpc.Points.PointsSelector;
+import static io.qdrant.client.grpc.Points.ReadConsistency;
+import static io.qdrant.client.grpc.Points.RecommendBatchPoints;
+import static io.qdrant.client.grpc.Points.RecommendBatchResponse;
+import static io.qdrant.client.grpc.Points.RecommendGroupsResponse;
+import static io.qdrant.client.grpc.Points.RecommendPointGroups;
+import static io.qdrant.client.grpc.Points.RecommendPoints;
+import static io.qdrant.client.grpc.Points.RecommendResponse;
+import static io.qdrant.client.grpc.Points.RetrievedPoint;
+import static io.qdrant.client.grpc.Points.ScoredPoint;
+import static io.qdrant.client.grpc.Points.ScrollPoints;
+import static io.qdrant.client.grpc.Points.ScrollResponse;
+import static io.qdrant.client.grpc.Points.SearchBatchPoints;
+import static io.qdrant.client.grpc.Points.SearchBatchResponse;
+import static io.qdrant.client.grpc.Points.SearchGroupsResponse;
+import static io.qdrant.client.grpc.Points.SearchPointGroups;
+import static io.qdrant.client.grpc.Points.SearchPoints;
+import static io.qdrant.client.grpc.Points.SearchResponse;
+import static io.qdrant.client.grpc.Points.SetPayloadPoints;
+import static io.qdrant.client.grpc.Points.UpdatePointVectors;
+import static io.qdrant.client.grpc.Points.UpdateResult;
+import static io.qdrant.client.grpc.Points.UpsertPoints;
+import static io.qdrant.client.grpc.Points.VectorsSelector;
+import static io.qdrant.client.grpc.Points.WithPayloadSelector;
+import static io.qdrant.client.grpc.Points.WithVectorsSelector;
+import static io.qdrant.client.grpc.Points.WriteOrdering;
+import static io.qdrant.client.grpc.Points.WriteOrderingType;
+import static io.qdrant.client.grpc.QdrantGrpc.QdrantFutureStub;
+import static io.qdrant.client.grpc.QdrantOuterClass.HealthCheckReply;
+import static io.qdrant.client.grpc.QdrantOuterClass.HealthCheckRequest;
+import static io.qdrant.client.grpc.SnapshotsService.CreateFullSnapshotRequest;
+import static io.qdrant.client.grpc.SnapshotsService.CreateSnapshotRequest;
+import static io.qdrant.client.grpc.SnapshotsService.CreateSnapshotResponse;
+import static io.qdrant.client.grpc.SnapshotsService.DeleteFullSnapshotRequest;
+import static io.qdrant.client.grpc.SnapshotsService.DeleteSnapshotRequest;
+import static io.qdrant.client.grpc.SnapshotsService.DeleteSnapshotResponse;
+import static io.qdrant.client.grpc.SnapshotsService.ListFullSnapshotsRequest;
+import static io.qdrant.client.grpc.SnapshotsService.ListSnapshotsRequest;
+import static io.qdrant.client.grpc.SnapshotsService.ListSnapshotsResponse;
+import static io.qdrant.client.grpc.SnapshotsService.SnapshotDescription;
+
+/**
+ * Client for the Qdrant vector database.
+ */
public class QdrantClient implements AutoCloseable {
- private QdrantGrpc.QdrantBlockingStub qdrantStub;
- private CollectionsGrpc.CollectionsBlockingStub collectionsStub;
- private PointsGrpc.PointsBlockingStub pointsStub;
- private SnapshotsGrpc.SnapshotsBlockingStub snapshotsStub;
- private ManagedChannel channel;
-
- /**
- * Constructs a new QdrantClient with the specified URL and API key
- * Uses TLS if the URL is https, otherwise uses plaintext.
- *
- * @param url The URL of the Qdrant service.
- * @param apiKey The API key for authentication.
- * @throws MalformedURLException If the URL is malformed.
- * @throws IllegalArgumentException If the protocol is invalid.
- */
- public QdrantClient(String url, String apiKey)
- throws MalformedURLException, IllegalArgumentException {
- TokenInterceptor interceptor = new TokenInterceptor(apiKey);
- ManagedChannel channel = createManagedChannel(url, interceptor);
- initializeStubs(channel, null);
- }
-
- /**
- * Constructs a new QdrantClient with the specified URL and API key
- * Uses TLS if the URL is https, otherwise uses plaintext.
- *
- * @param url The URL of the Qdrant service.
- * @param apiKey The API key for authentication.
- * @param timeout The timeout for the gRPC requests.
- * @throws MalformedURLException If the URL is malformed.
- * @throws IllegalArgumentException If the protocol is invalid.
- */
- public QdrantClient(String url, String apiKey, Duration timeout)
- throws MalformedURLException, IllegalArgumentException {
- TokenInterceptor interceptor = new TokenInterceptor(apiKey);
- ManagedChannel channel = createManagedChannel(url, interceptor);
- initializeStubs(channel, timeout);
- }
-
- /**
- * Constructs a new QdrantClient with the specified URL
- * Uses TLS if the URL is https, otherwise uses plaintext.
- *
- * @param url the URL of the Qdrant service
- * @throws MalformedURLException If the URL is malformed
- * @throws IllegalArgumentException If the protocol is invalid.
- */
- public QdrantClient(String url) throws MalformedURLException, IllegalArgumentException {
- ManagedChannel channel = createManagedChannel(url, null);
- initializeStubs(channel, null);
- }
-
- /**
- * Constructs a new QdrantClient with the specified URL
- * Uses TLS if the URL is https, otherwise uses plaintext.
- *
- * @param url the URL of the Qdrant service
- * @param timeout The timeout for the gRPC requests.
- * @throws MalformedURLException If the URL is malformed
- * @throws IllegalArgumentException If the protocol is invalid.
- */
- public QdrantClient(String url, Duration timeout)
- throws MalformedURLException, IllegalArgumentException {
- ManagedChannel channel = createManagedChannel(url, null);
- initializeStubs(channel, timeout);
- }
-
- /**
- * Creates a managed channel based on the provided URL and interceptor.
- *
- * @param url The URL of the gRPC server.
- * @param interceptor The token interceptor to be added to the channel.
- * @return The created managed channel.
- * @throws MalformedURLException If the provided URL is malformed.
- * @throws IllegalArgumentException If the provided protocol is invalid.
- */
- private ManagedChannel createManagedChannel(String url, @Nullable TokenInterceptor interceptor)
- throws MalformedURLException, IllegalArgumentException {
- URL parsedUrl = new URL(url);
-
- ManagedChannelBuilder> channelBuilder =
- ManagedChannelBuilder.forAddress(parsedUrl.getHost(), parsedUrl.getPort());
-
- switch (parsedUrl.getProtocol().toUpperCase()) {
- case "HTTPS":
- // TLS is enabled by default
- // Specifying explicitly for clarity
- channelBuilder.useTransportSecurity();
- break;
- case "HTTP":
- channelBuilder.usePlaintext();
- break;
- default:
- throw new IllegalArgumentException(
- "Invalid protocol. Only 'http' and 'https' are supported for gRPC URLs.");
- }
-
- if (interceptor != null) {
- // Add token interceptor if apiKey is provided
- channelBuilder.intercept(interceptor);
- }
-
- return channelBuilder.build();
- }
-
- /**
- * Initializes the gRPC stubs for Qdrant client.
- *
- * @param channel The managed channel used for communication.
- */
- private void initializeStubs(ManagedChannel channel, @Nullable Duration timeout) {
- this.qdrantStub =
- QdrantGrpc.newBlockingStub(channel)
- .withDeadline(
- timeout == null ? null : Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS));
- this.collectionsStub =
- CollectionsGrpc.newBlockingStub(channel)
- .withDeadline(
- timeout == null ? null : Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS));
- this.pointsStub =
- PointsGrpc.newBlockingStub(channel)
- .withDeadline(
- timeout == null ? null : Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS));
- this.snapshotsStub =
- SnapshotsGrpc.newBlockingStub(channel)
- .withDeadline(
- timeout == null ? null : Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS));
- this.channel = channel;
- }
-
- /**
- * Retrieves a list of collections.
- *
- * @return The response containing the list of collections.
- */
- public Collections.ListCollectionsResponse listCollections() {
- Collections.ListCollectionsRequest request =
- Collections.ListCollectionsRequest.newBuilder().build();
- return collectionsStub.list(request);
- }
-
- /**
- * Performs a health check on the Qdrant service.
- *
- * @return The health check reply from the Qdrant service.
- */
- public QdrantOuterClass.HealthCheckReply healthCheck() {
- QdrantOuterClass.HealthCheckRequest request =
- QdrantOuterClass.HealthCheckRequest.newBuilder().build();
- return qdrantStub.healthCheck(request);
- }
-
- /**
- * Checks if a collection with the given name exists.
- *
- * @param collectionName The name of the collection to check.
- * @return True if the collection exists, false otherwise.
- */
- public boolean hasCollection(String collectionName) {
- return listCollections().getCollectionsList().stream()
- .anyMatch(c -> c.getName().equals(collectionName));
- }
-
- /**
- * Creates a new collection with the specified name, vector size, and distance metric.
- *
- * @param collectionName The name of the collection to be created.
- * @param vectorSize The size of the vectors in the collection.
- * @param distance The distance metric to be used for vector comparison.
- * @return The response containing the operation status.
- */
- public Collections.CollectionOperationResponse createCollection(
- String collectionName, long vectorSize, Collections.Distance distance) {
- Collections.VectorParams.Builder params =
- Collections.VectorParams.newBuilder().setDistance(distance).setSize(vectorSize);
- Collections.VectorsConfig config =
- Collections.VectorsConfig.newBuilder().setParams(params).build();
- Collections.CreateCollection details =
- Collections.CreateCollection.newBuilder()
- .setVectorsConfig(config)
- .setCollectionName(collectionName)
- .build();
- return createCollection(details);
- }
-
- /**
- * Creates a new collection with the specified name, vector size, and distance metric.
- *
- * @param collectionName The name of the collection to be created.
- * @param vectorsConfig The vectors configuration of the collection.
- * @return The response containing the operation status.
- */
- public Collections.CollectionOperationResponse createCollection(
- String collectionName, Collections.VectorsConfig vectorsConfig) {
-
- Collections.CreateCollection details =
- Collections.CreateCollection.newBuilder()
- .setVectorsConfig(vectorsConfig)
- .setCollectionName(collectionName)
- .build();
- return createCollection(details);
- }
-
- /**
- * Creates a new collection with the specified details.
- *
- * @param details The details of the collection to be created.
- * @return The response containing the operation status.
- */
- public Collections.CollectionOperationResponse createCollection(
- Collections.CreateCollection details) {
- return collectionsStub.create(details);
- }
-
- /**
- * Deletes and creates a new collection with the specified name, vector size, and distance metric.
- *
- * @param collectionName The name of the collection to be created.
- * @param vectorSize The size of the vectors in the collection.
- * @param distance The distance metric to be used for vector comparison.
- * @return The response containing the operation status.
- */
- public Collections.CollectionOperationResponse recreateCollection(
- String collectionName, long vectorSize, Collections.Distance distance) {
-
- Collections.VectorParams.Builder params =
- Collections.VectorParams.newBuilder().setDistance(distance).setSize(vectorSize);
- Collections.VectorsConfig config =
- Collections.VectorsConfig.newBuilder().setParams(params).build();
- Collections.CreateCollection details =
- Collections.CreateCollection.newBuilder()
- .setVectorsConfig(config)
- .setCollectionName(collectionName)
- .build();
- return recreateCollection(details);
- }
-
- /**
- * Deletes and creates a new collection with the specified details.
- *
- * @param details The details of the collection to be created.
- * @return The response containing the operation status.
- */
- public Collections.CollectionOperationResponse recreateCollection(
- Collections.CreateCollection details) {
- deleteCollection(details.getCollectionName());
- return collectionsStub.create(details);
- }
-
- /**
- * Updates a collection with the specified details.
- *
- * @param details The details of the update operation.
- * @return The response containing the operation status.
- */
- public Collections.CollectionOperationResponse updateCollection(
- Collections.UpdateCollection details) {
- return collectionsStub.update(details);
- }
-
- /**
- * Deletes a collection with the specified name.
- *
- * @param collectionName the name of the collection to be deleted
- * @return the response of the collection deletion operation
- */
- public Collections.CollectionOperationResponse deleteCollection(String collectionName) {
- Collections.DeleteCollection request =
- Collections.DeleteCollection.newBuilder().setCollectionName(collectionName).build();
- return collectionsStub.delete(request);
- }
-
- /**
- * Retrieves information about a collection.
- *
- * @param collectionName The name of the collection.
- * @return The response containing the collection information.
- */
- public Collections.GetCollectionInfoResponse getCollectionInfo(String collectionName) {
- Collections.GetCollectionInfoRequest request =
- Collections.GetCollectionInfoRequest.newBuilder().setCollectionName(collectionName).build();
- return collectionsStub.get(request);
- }
-
- /**
- * Creates an alias for a collection.
- *
- * @param collectionName The name of the collection.
- * @param aliasName The name of the alias.
- * @return The response of the collection operation.
- */
- public Collections.CollectionOperationResponse createAlias(
- String collectionName, String aliasName) {
- Collections.CreateAlias createAlias =
- Collections.CreateAlias.newBuilder()
- .setCollectionName(collectionName)
- .setAliasName(aliasName)
- .build();
- Collections.AliasOperations operations =
- Collections.AliasOperations.newBuilder().setCreateAlias(createAlias).build();
- Collections.ChangeAliases changeAliases =
- Collections.ChangeAliases.newBuilder().addActions(operations).build();
-
- return this.updateAliases(changeAliases);
- }
-
- /**
- * Deletes an alias with the specified name.
- *
- * @param aliasName the name of the alias to be deleted
- * @return the response of the collection operation
- */
- public Collections.CollectionOperationResponse deleteAlias(String aliasName) {
- Collections.DeleteAlias deleteAlias =
- Collections.DeleteAlias.newBuilder().setAliasName(aliasName).build();
- Collections.AliasOperations operations =
- Collections.AliasOperations.newBuilder().setDeleteAlias(deleteAlias).build();
- Collections.ChangeAliases changeAliases =
- Collections.ChangeAliases.newBuilder().addActions(operations).build();
-
- return this.updateAliases(changeAliases);
- }
-
- /**
- * Renames an alias in the Qdrant collection.
- *
- * @param oldAliasName The current name of the alias.
- * @param newAliasName The new name for the alias.
- * @return The response containing the result of the alias rename operation.
- */
- public Collections.CollectionOperationResponse renameAlias(
- String oldAliasName, String newAliasName) {
- Collections.RenameAlias renameAlias =
- Collections.RenameAlias.newBuilder()
- .setOldAliasName(oldAliasName)
- .setNewAliasName(newAliasName)
- .build();
- Collections.AliasOperations operations =
- Collections.AliasOperations.newBuilder().setRenameAlias(renameAlias).build();
- Collections.ChangeAliases changeAliases =
- Collections.ChangeAliases.newBuilder().addActions(operations).build();
- return this.updateAliases(changeAliases);
- }
-
- /**
- * Updates the aliases for collections.
- *
- * @param details The details of the aliases to be changed.
- * @return The response of the collection operation.
- */
- public Collections.CollectionOperationResponse updateAliases(Collections.ChangeAliases details) {
- return collectionsStub.updateAliases(details);
- }
-
- /**
- * Retrieves the list of aliases for a given collection.
- *
- * @param collectionName The name of the collection.
- * @return The response containing the list of aliases.
- */
- public Collections.ListAliasesResponse listCollectionAliases(String collectionName) {
- Collections.ListCollectionAliasesRequest request =
- Collections.ListCollectionAliasesRequest.newBuilder()
- .setCollectionName(collectionName)
- .build();
- return collectionsStub.listCollectionAliases(request);
- }
-
- /**
- * Retrieves a list of aliases.
- *
- * @return The response containing the list of aliases.
- */
- public Collections.ListAliasesResponse listAliases() {
- Collections.ListAliasesRequest request = Collections.ListAliasesRequest.newBuilder().build();
- return collectionsStub.listAliases(request);
- }
-
- /**
- * Retrieves the cluster information for a specific collection.
- *
- * @param collectionName The name of the collection.
- * @return The cluster information for the collection.
- */
- public Collections.CollectionClusterInfoResponse getCollectionClusterInfo(String collectionName) {
- Collections.CollectionClusterInfoRequest request =
- Collections.CollectionClusterInfoRequest.newBuilder()
- .setCollectionName(collectionName)
- .build();
- return collectionsStub.collectionClusterInfo(request);
- }
-
- /**
- * Updates the cluster setup for a collection.
- *
- * @param collectionName The name of the collection.
- * @param request The request object containing the updated cluster setup.
- * @return The response object indicating the success or failure of the update operation.
- */
- public Collections.UpdateCollectionClusterSetupResponse updateCollectionClusterSetup(
- String collectionName, Collections.UpdateCollectionClusterSetupRequest request) {
- return collectionsStub.updateCollectionClusterSetup(request);
- }
-
- /** Internal batch update method */
- private Points.UpdateBatchResponse batchUpdate(
- String collectionName,
- List operations,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.UpdateBatchPoints.Builder request =
- Points.UpdateBatchPoints.newBuilder()
- .setCollectionName(collectionName)
- .setWait(wait)
- .addAllOperations(operations);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.updateBatch(request.build());
- }
-
- /**
- * Performs a batch update operation on a collection. Does not wait for the operation to complete
- * before returning.
- *
- * @param collectionName The name of the collection.
- * @param operations The list of update operations to be performed.
- * @return The response of the batch update operation.
- */
- public Points.UpdateBatchResponse batchUpdate(
- String collectionName,
- List operations,
- @Nullable Points.WriteOrderingType ordering) {
- return batchUpdate(collectionName, operations, ordering, false);
- }
-
- /**
- * Performs a batch update operation on a collection. Waits for the operation to complete before
- * returning.
- *
- * @param collectionName The name of the collection.
- * @param operations The list of update operations to be performed.
- * @return The response of the batch update operation.
- */
- public Points.UpdateBatchResponse batchUpdateBlocking(
- String collectionName,
- List operations,
- @Nullable Points.WriteOrderingType ordering) {
- return batchUpdate(collectionName, operations, ordering, true);
- }
-
- /** Internal upsert method */
- private Points.PointsOperationResponse upsertPoints(
- String collectionName,
- List points,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.UpsertPoints.Builder request =
- Points.UpsertPoints.newBuilder()
- .setCollectionName(collectionName)
- .addAllPoints(points)
- .setWait(wait);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.upsert(request.build());
- }
-
- /**
- * Upserts the given points into the specified collection. Does not wait for the operation to
- * complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The list of points to be upserted.
- * @param ordering The write ordering for the upsert operation.
- * @return The response of the upsert operation.
- */
- public Points.PointsOperationResponse upsertPoints(
- String collectionName,
- List points,
- @Nullable Points.WriteOrderingType ordering) {
- return upsertPoints(collectionName, points, ordering, false);
- }
-
- /**
- * Upserts the given points into the specified collection. Waits for the operation to complete
- * before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The list of points to be upserted.
- * @param ordering The write ordering for the upsert operation.
- * @return The response of the upsert operation.
- */
- public Points.PointsOperationResponse upsertPointsBlocking(
- String collectionName,
- List points,
- @Nullable Points.WriteOrderingType ordering) {
- return upsertPoints(collectionName, points, ordering, true);
- }
-
- /** Internal batch upsert method */
- private Points.PointsOperationResponse upsertPointsBatch(
- String collectionName,
- List points,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait,
- int chunkSize) {
- int listSize = points.size();
- double timeTaken = 0;
- Points.UpdateResult result = null;
-
- for (int i = 0; i < listSize; i += chunkSize) {
- int end = Math.min(i + chunkSize, listSize);
- List chunk = points.subList(i, end);
- Points.PointsOperationResponse response = upsertPoints(collectionName, chunk, ordering, wait);
- timeTaken += response.getTime();
- result = response.getResult();
- }
- return Points.PointsOperationResponse.newBuilder().setTime(timeTaken).setResult(result).build();
- }
-
- /**
- * Upserts a batch of points in the specified collection. Does not wait for the operation to
- * complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The list of points to upsert.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse upsertPointsBatch(
- String collectionName,
- List points,
- @Nullable Points.WriteOrderingType ordering,
- int chunkSize) {
- return upsertPointsBatch(collectionName, points, ordering, false, chunkSize);
- }
-
- /**
- * Upserts a batch of points in the specified collection. Waits for the operation to complete
- * before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The list of points to upsert.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse upsertPointsBatchBlocking(
- String collectionName,
- List points,
- @Nullable Points.WriteOrderingType ordering,
- int chunkSize) {
- return upsertPointsBatch(collectionName, points, ordering, true, chunkSize);
- }
-
- /** Internal update method */
- private Points.PointsOperationResponse setPayload(
- String collectionName,
- Points.PointsSelector points,
- Map payload,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.SetPayloadPoints.Builder request =
- Points.SetPayloadPoints.newBuilder()
- .setCollectionName(collectionName)
- .setPointsSelector(points)
- .putAllPayload(payload)
- .setWait(wait);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.setPayload(request.build());
- }
-
- /**
- * Sets the payload of the specified points in a collection. Does not wait for the operation to
- * complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The selector for the points to be updated.
- * @param payload The new payload to be assigned to the points.
- * @param ordering The ordering of the write operation.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse setPayload(
- String collectionName,
- Points.PointsSelector points,
- Map payload,
- @Nullable Points.WriteOrderingType ordering) {
- return setPayload(collectionName, points, payload, ordering, false);
- }
-
- /**
- * Sets the payload of the specified points in a collection. Waits for the operation to complete
- * before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The selector for the points to be updated.
- * @param payload The new payload to be assigned to the points.
- * @param ordering The ordering of the write operation.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse setPayloadBlocking(
- String collectionName,
- Points.PointsSelector points,
- Map payload,
- @Nullable Points.WriteOrderingType ordering) {
- return setPayload(collectionName, points, payload, ordering, true);
- }
-
- /** Internal payload overwrite method */
- private Points.PointsOperationResponse overwritePayload(
- String collectionName,
- Points.PointsSelector points,
- Map payload,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.SetPayloadPoints.Builder request =
- Points.SetPayloadPoints.newBuilder()
- .setCollectionName(collectionName)
- .setPointsSelector(points)
- .putAllPayload(payload)
- .setWait(wait);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.overwritePayload(request.build());
- }
-
- /**
- * Overwrites the payload of the specified points in a collection. Does not wait for the operation
- * to complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The selector for the points to be overwritten.
- * @param payload The new payload to be assigned to the points.
- * @param ordering The ordering of the write operation.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse overwritePayload(
- String collectionName,
- Points.PointsSelector points,
- Map payload,
- @Nullable Points.WriteOrderingType ordering) {
- return overwritePayload(collectionName, points, payload, ordering, false);
- }
-
- /**
- * Overwrites the payload of the specified points in a collection. Waits for the operation to
- * complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The selector for the points to be overwritten.
- * @param payload The new payload to be assigned to the points.
- * @param ordering The ordering of the write operation.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse overwritePayloadBlocking(
- String collectionName,
- Points.PointsSelector points,
- Map payload,
- @Nullable Points.WriteOrderingType ordering) {
- return overwritePayload(collectionName, points, payload, ordering, true);
- }
-
- /** Internal payload delete method */
- private Points.PointsOperationResponse deletePayload(
- String collectionName,
- Points.PointsSelector points,
- List keys,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.DeletePayloadPoints.Builder request =
- Points.DeletePayloadPoints.newBuilder()
- .setCollectionName(collectionName)
- .addAllKeys(keys)
- .setWait(wait)
- .setPointsSelector(points);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.deletePayload(request.build());
- }
-
- /**
- * Deletes the payload associated with the specified collection, points, keys, and ordering. Does
- * not wait for the operation to complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The points selector.
- * @param keys The list of keys.
- * @param ordering The write ordering.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse deletePayload(
- String collectionName,
- Points.PointsSelector points,
- List keys,
- @Nullable Points.WriteOrderingType ordering) {
- return deletePayload(collectionName, points, keys, ordering, false);
- }
-
- /**
- * Deletes the payload associated with the specified collection, points, keys, and ordering. Waits
- * for the operation to complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The points selector.
- * @param keys The list of keys.
- * @param ordering The write ordering.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse deletePayloadBlocking(
- String collectionName,
- Points.PointsSelector points,
- List keys,
- @Nullable Points.WriteOrderingType ordering) {
- return deletePayload(collectionName, points, keys, ordering, true);
- }
-
- /** Internal payload clear method */
- private Points.PointsOperationResponse clearPayload(
- String collectionName,
- Points.PointsSelector points,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.ClearPayloadPoints.Builder request =
- Points.ClearPayloadPoints.newBuilder()
- .setCollectionName(collectionName)
- .setPoints(points)
- .setWait(wait);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
-
- return pointsStub.clearPayload(request.build());
- }
-
- /**
- * Clears the payload associated with the specified collection, points and ordering. Does not wait
- * for the operation to complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The points to be cleared.
- * @return The response of the clearPayload operation.
- */
- public Points.PointsOperationResponse clearPayload(
- String collectionName,
- Points.PointsSelector points,
- @Nullable Points.WriteOrderingType ordering) {
- return clearPayload(collectionName, points, ordering, false);
- }
-
- /**
- * Clears the payload associated with the specified collection, points and ordering. Waits for the
- * operation to complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The points to be cleared.
- * @return The response of the clearPayload operation.
- */
- public Points.PointsOperationResponse clearPayloadBlocking(
- String collectionName,
- Points.PointsSelector points,
- @Nullable Points.WriteOrderingType ordering) {
- return clearPayload(collectionName, points, ordering, true);
- }
-
- /**
- * Retrieves points from a collection.
- *
- * @param collectionName The name of the collection.
- * @param points The IDs of the points to retrieve.
- * @param withVectors The selector for including vectors in the response.
- * @param withPayload The selector for including payload in the response.
- * @param readConsistency The read consistency level for the operation.
- * @return The response containing the retrieved points.
- */
- public Points.GetResponse getPoints(
- String collectionName,
- Iterable extends Points.PointId> points,
- Points.WithVectorsSelector withVectors,
- Points.WithPayloadSelector withPayload,
- @Nullable Points.ReadConsistencyType readConsistency) {
- Points.GetPoints.Builder request =
- Points.GetPoints.newBuilder()
- .setCollectionName(collectionName)
- .addAllIds(points)
- .setWithVectors(withVectors)
- .setWithPayload(withPayload);
-
- if (readConsistency != null) {
- request.setReadConsistency(PointUtil.consistency(readConsistency));
- }
-
- return pointsStub.get(request.build());
- }
-
- /**
- * Performs a search operation on the points.
- *
- * @param request The search request containing the query parameters.
- * @return The response containing the search results.
- */
- public Points.SearchResponse searchPoints(Points.SearchPoints request) {
- return pointsStub.search(request);
- }
-
- /**
- * Performs a batch search for points.
- *
- * @param request The search request containing the batch points to search for.
- * @return The response containing the search results.
- */
- public Points.SearchBatchResponse searchBatchPoints(Points.SearchBatchPoints request) {
- return pointsStub.searchBatch(request);
- }
-
- /**
- * Searches for point groups based on the given request.
- *
- * @param request The search request containing the criteria for searching point groups.
- * @return The response containing the search results for point groups.
- */
- public Points.SearchGroupsResponse searchGroups(Points.SearchPointGroups request) {
- return pointsStub.searchGroups(request);
- }
-
- /** Internal delete method */
- private Points.PointsOperationResponse deletePoints(
- String collectionName,
- Points.PointsSelector points,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.DeletePoints.Builder request =
- Points.DeletePoints.newBuilder()
- .setCollectionName(collectionName)
- .setPoints(points)
- .setWait(wait);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.delete(request.build());
- }
-
- /**
- * Deletes points from a collection. Does not wait for the operation to complete before returning.
- *
- * @param collectionName The name of the collection from which points will be deleted.
- * @param points The selector for the points to be deleted.
- * @param ordering The ordering of the write operation.
- * @return The response of the points deletion operation.
- */
- public Points.PointsOperationResponse deletePoints(
- String collectionName,
- Points.PointsSelector points,
- @Nullable Points.WriteOrderingType ordering) {
- return deletePoints(collectionName, points, ordering, false);
- }
-
- /**
- * Deletes points from a collection. Waits for the operation to complete before returning.
- *
- * @param collectionName The name of the collection from which points will be deleted.
- * @param points The selector for the points to be deleted.
- * @param ordering The ordering of the write operation.
- * @return The response of the points deletion operation.
- */
- public Points.PointsOperationResponse deletePointsBlocking(
- String collectionName,
- Points.PointsSelector points,
- @Nullable Points.WriteOrderingType ordering) {
- return deletePoints(collectionName, points, ordering, true);
- }
-
- /** Internal delete vectors method */
- private Points.PointsOperationResponse deleteVectors(
- String collectionName,
- Points.PointsSelector points,
- Points.VectorsSelector vectors,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.DeletePointVectors.Builder requests =
- Points.DeletePointVectors.newBuilder()
- .setCollectionName(collectionName)
- .setPointsSelector(points)
- .setVectors(vectors)
- .setWait(wait);
-
- if (ordering != null) {
- requests.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.deleteVectors(requests.build());
- }
-
- /**
- * Deletes vectors from a collection. Does not wait for the operation to complete before
- * returning.
- *
- * @param collectionName The name of the collection.
- * @param points The selector for points to delete.
- * @param vectors The selector for vectors to delete.
- * @param ordering The write ordering for the operation.
- * @return The response of the delete operation.
- */
- public Points.PointsOperationResponse deleteVectors(
- String collectionName,
- Points.PointsSelector points,
- Points.VectorsSelector vectors,
- @Nullable Points.WriteOrderingType ordering) {
- return deleteVectors(collectionName, points, vectors, ordering, false);
- }
-
- /**
- * Deletes vectors from a collection. Waits for the operation to complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param points The selector for points to delete.
- * @param vectors The selector for vectors to delete.
- * @param ordering The write ordering for the operation.
- * @return The response of the delete operation.
- */
- public Points.PointsOperationResponse deleteVectorsBlocking(
- String collectionName,
- Points.PointsSelector points,
- Points.VectorsSelector vectors,
- @Nullable Points.WriteOrderingType ordering) {
- return deleteVectors(collectionName, points, vectors, ordering, true);
- }
-
- /** Internal update vectors method */
- private Points.PointsOperationResponse updateVectors(
- String collectionName,
- Iterable extends Points.PointVectors> points,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.UpdatePointVectors.Builder request =
- Points.UpdatePointVectors.newBuilder()
- .setCollectionName(collectionName)
- .addAllPoints(points)
- .setWait(wait);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.updateVectors(request.build());
- }
-
- /**
- * Updates the vectors of points in a collection. Does not wait for the operation to complete
- * before returning.
- *
- * @param collectionName The name of the collection.
- * @param points An iterable of point vectors to update.
- * @param ordering The write ordering for the update operation.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse updateVectors(
- String collectionName,
- Iterable extends Points.PointVectors> points,
- @Nullable Points.WriteOrderingType ordering) {
- return updateVectors(collectionName, points, ordering, false);
- }
-
- /**
- * Updates the vectors of points in a collection. Waits for the operation to complete before
- * returning.
- *
- * @param collectionName The name of the collection.
- * @param points An iterable of point vectors to update.
- * @param ordering The write ordering for the update operation.
- * @return The response of the points operation.
- */
- public Points.PointsOperationResponse updateVectorsBlocking(
- String collectionName,
- Iterable extends Points.PointVectors> points,
- @Nullable Points.WriteOrderingType ordering) {
- return updateVectors(collectionName, points, ordering, true);
- }
-
- /**
- * Retrieve points from a collection based on filters.
- *
- * @param request The search request containing the query parameters.
- * @return The response containing the scroll results.
- */
- public Points.ScrollResponse scroll(Points.ScrollPoints request) {
- return pointsStub.scroll(request);
- }
-
- /**
- * Recommends points based on the given positive/negative points recommendation request.
- *
- * @param request The points recommendation request.
- * @return The recommendation response.
- */
- public Points.RecommendResponse recommend(Points.RecommendPoints request) {
- return pointsStub.recommend(request);
- }
-
- /**
- * Recommends points batch based on the given positive/negative points recommendation request.
- *
- * @param request The batch recommendation points request.
- * @return The response containing the recommended points.
- */
- public Points.RecommendBatchResponse recommendBatch(Points.RecommendBatchPoints request) {
- return pointsStub.recommendBatch(request);
- }
-
- /**
- * Recommends groups based on the given positive/negative points recommendation request.
- *
- * @param request The request containing the point groups to recommend.
- * @return The response containing the recommended groups.
- */
- public Points.RecommendGroupsResponse recommendGroups(Points.RecommendPointGroups request) {
- return pointsStub.recommendGroups(request);
- }
-
- /**
- * Counts the number of points in a collection based on the given filters.
- *
- * @param collectionName The name of the collection.
- * @param filter The filter to be applied.
- * @return The response containing the points count result.
- */
- public Points.CountResponse count(String collectionName, Points.Filter filter) {
- Points.CountPoints request =
- Points.CountPoints.newBuilder().setCollectionName(collectionName).setFilter(filter).build();
- return pointsStub.count(request);
- }
-
- /**
- * Counts the number of points in a collection based on the given filters.
- *
- * @param request The request containing the filters and options.
- * @return The response containing the points count result.
- */
- public Points.CountResponse count(Points.CountPoints request) {
- return pointsStub.count(request);
- }
-
- /** Internal update batch method */
- private Points.UpdateBatchResponse updateBatchPoints(
- String collecionName,
- Iterable extends Points.PointsUpdateOperation> operations,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.UpdateBatchPoints.Builder request =
- Points.UpdateBatchPoints.newBuilder()
- .setCollectionName(collecionName)
- .addAllOperations(operations)
- .setWait(wait);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.updateBatch(request.build());
- }
-
- /**
- * Updates a batch of points in a collection. Does not wait for the operation to complete before
- * returning.
- *
- * @param collecionName The name of the collection.
- * @param operations The operations to be performed on the points.
- * @param ordering The ordering of the write operations.
- * @return The response of the batch points update operation.
- */
- public Points.UpdateBatchResponse updateBatchPoints(
- String collecionName,
- Iterable extends Points.PointsUpdateOperation> operations,
- @Nullable Points.WriteOrderingType ordering) {
- return updateBatchPoints(collecionName, operations, ordering, false);
- }
-
- /**
- * Updates a batch of points in a collection. Waits for the operation to complete before
- * returning.
- *
- * @param collectionName The name of the collection.
- * @param operations The operations to be performed on the points.
- * @param ordering The ordering of the write operations.
- * @return The response of the batch points update operation.
- */
- public Points.UpdateBatchResponse updateBatchPointsBlocking(
- String collectionName,
- Iterable extends Points.PointsUpdateOperation> operations,
- @Nullable Points.WriteOrderingType ordering) {
- return updateBatchPoints(collectionName, operations, ordering, true);
- }
-
- /** Internal create field index method */
- private Points.PointsOperationResponse createFieldIndex(
- String collectionName,
- String fieldName,
- Points.FieldType fieldType,
- Collections.PayloadIndexParams fieldIndexParams,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.CreateFieldIndexCollection.Builder request =
- Points.CreateFieldIndexCollection.newBuilder()
- .setCollectionName(collectionName)
- .setFieldName(fieldName)
- .setFieldType(fieldType)
- .setFieldIndexParams(fieldIndexParams)
- .setWait(wait);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.createFieldIndex(request.build());
- }
-
- /**
- * Creates a field index in the specified collection with the given parameters. Does not wait for
- * the operation to complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param fieldName The name of the field.
- * @param fieldType The type of the field.
- * @param fieldIndexParams The index parameters for the field.
- * @param ordering The write ordering for the field.
- * @return The response of the field index creation operation.
- */
- public Points.PointsOperationResponse createFieldIndex(
- String collectionName,
- String fieldName,
- Points.FieldType fieldType,
- Collections.PayloadIndexParams fieldIndexParams,
- @Nullable Points.WriteOrderingType ordering) {
- return createFieldIndex(
- collectionName, fieldName, fieldType, fieldIndexParams, ordering, false);
- }
-
- /**
- * Creates a field index in the specified collection with the given parameters. Waits for the
- * operation to complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param fieldName The name of the field.
- * @param fieldType The type of the field.
- * @param fieldIndexParams The index parameters for the field.
- * @param ordering The write ordering for the field.
- * @return The response of the field index creation operation.
- */
- public Points.PointsOperationResponse createFieldIndexBlocking(
- String collectionName,
- String fieldName,
- Points.FieldType fieldType,
- Collections.PayloadIndexParams fieldIndexParams,
- @Nullable Points.WriteOrderingType ordering) {
- return createFieldIndex(collectionName, fieldName, fieldType, fieldIndexParams, ordering, true);
- }
-
- /** Internal delete field index method */
- private Points.PointsOperationResponse deleteFieldIndex(
- String collectionName,
- String fieldName,
- @Nullable Points.WriteOrderingType ordering,
- Boolean wait) {
- Points.DeleteFieldIndexCollection.Builder request =
- Points.DeleteFieldIndexCollection.newBuilder()
- .setCollectionName(collectionName)
- .setFieldName(fieldName)
- .setWait(wait);
-
- if (ordering != null) {
- request.setOrdering(PointUtil.ordering(ordering));
- }
- return pointsStub.deleteFieldIndex(request.build());
- }
-
- /**
- * Deletes the field index for a given collection and field name. Does not wait for the operation
- * to complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param fieldName The name of the field.
- * @param ordering The write ordering for the operation.
- * @return The response of the delete operation.
- */
- public Points.PointsOperationResponse deleteFieldIndex(
- String collectionName, String fieldName, @Nullable Points.WriteOrderingType ordering) {
- return deleteFieldIndex(collectionName, fieldName, ordering, false);
- }
-
- /**
- * Deletes the field index for a given collection and field name. Waits for the operation to
- * complete before returning.
- *
- * @param collectionName The name of the collection.
- * @param fieldName The name of the field.
- * @param ordering The write ordering for the operation.
- * @return The response of the delete operation.
- */
- public Points.PointsOperationResponse deleteFieldIndexBlocking(
- String collectionName, String fieldName, @Nullable Points.WriteOrderingType ordering) {
- return deleteFieldIndex(collectionName, fieldName, ordering, true);
- }
-
- /**
- * Creates a snapshot of a collection.
- *
- * @param collectionName the name of the collection
- * @return The response containing information about the created snapshot
- */
- public SnapshotsService.CreateSnapshotResponse createSnapshot(String collectionName) {
- SnapshotsService.CreateSnapshotRequest request =
- SnapshotsService.CreateSnapshotRequest.newBuilder()
- .setCollectionName(collectionName)
- .build();
- return snapshotsStub.create(request);
- }
-
- /**
- * Retrieves a list of snapshots for a given collection.
- *
- * @param collectionName the name of the collection
- * @return The response containing the list of snapshots
- */
- public SnapshotsService.ListSnapshotsResponse listSnapshots(String collectionName) {
- SnapshotsService.ListSnapshotsRequest request =
- SnapshotsService.ListSnapshotsRequest.newBuilder()
- .setCollectionName(collectionName)
- .build();
- return snapshotsStub.list(request);
- }
-
- /**
- * Deletes a snapshot with the specified name from the given collection.
- *
- * @param collectionName The name of the collection.
- * @param snapshotName The name of the snapshot to be deleted.
- * @return The response indicating the success or failure of the snapshot deletion.
- */
- public SnapshotsService.DeleteSnapshotResponse deleteSnapshot(
- String collectionName, String snapshotName) {
- SnapshotsService.DeleteSnapshotRequest request =
- SnapshotsService.DeleteSnapshotRequest.newBuilder()
- .setCollectionName(collectionName)
- .setSnapshotName(snapshotName)
- .build();
- return snapshotsStub.delete(request);
- }
-
- /**
- * Creates a full snapshot of the Qdrant database.
- *
- * @return The response indicating the status of the snapshot creation.
- */
- public SnapshotsService.CreateSnapshotResponse createFullSnapshot() {
- SnapshotsService.CreateFullSnapshotRequest request =
- SnapshotsService.CreateFullSnapshotRequest.newBuilder().build();
- return snapshotsStub.createFull(request);
- }
-
- /**
- * Retrieves a list of full snapshots for a given collection.
- *
- * @return The response containing the list of full snapshots.
- */
- public SnapshotsService.ListSnapshotsResponse listFullSnapshots() {
- SnapshotsService.ListFullSnapshotsRequest request =
- SnapshotsService.ListFullSnapshotsRequest.newBuilder().build();
- return snapshotsStub.listFull(request);
- }
-
- /**
- * Deletes a full snapshot.
- *
- * @param snapshotName the name of the snapshot to delete.
- * @return The response indicating the status of the snapshot deletion.
- */
- public SnapshotsService.DeleteSnapshotResponse deleteFullSnapshot(String snapshotName) {
- SnapshotsService.DeleteFullSnapshotRequest request =
- SnapshotsService.DeleteFullSnapshotRequest.newBuilder()
- .setSnapshotName(snapshotName)
- .build();
- return snapshotsStub.deleteFull(request);
- }
-
- /**
- * Downloads a snapshot of a collection from the specified REST API URI and saves it to the given
- * output path.
- *
- * @param outPath The path where the snapshot will be saved.
- * @param collectionName The name of the collection.
- * @param snapshotName The name of the snapshot. If null, the latest snapshot will be downloaded.
- * @param restApiUri The URI of the REST API. If null, the default URI "http://localhost:6333"
- * will be used.
- * @throws RuntimeException If an error occurs while downloading the snapshot.
- */
- public void downloadSnapshot(
- Path outPath,
- String collectionName,
- @Nullable String snapshotName,
- @Nullable String restApiUri) {
- try {
- String resolvedSnapshotName;
-
- if (snapshotName != null) {
- resolvedSnapshotName = snapshotName;
- } else {
- // Get the latest(0th) snapshot of the collection
- List snapshots =
- listSnapshots(collectionName).getSnapshotDescriptionsList();
- if (snapshots.isEmpty()) {
- throw new RuntimeException("No snapshots found");
- }
- resolvedSnapshotName =
- listSnapshots(collectionName).getSnapshotDescriptionsList().get(0).getName();
- }
-
- String uri;
- if (restApiUri != null) {
- uri =
- String.format(
- "%s/collections/%s/snapshots/%s", restApiUri, collectionName, resolvedSnapshotName);
- } else {
- uri =
- String.format(
- "http://localhost:6333/collections/%s/snapshots/%s",
- collectionName, resolvedSnapshotName);
- }
-
- URL url = new URL(uri);
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-
- if (connection.getResponseCode() == 200) {
- try (InputStream in = connection.getInputStream();
- FileOutputStream fileOut = new FileOutputStream(outPath.toFile())) {
-
- byte[] buffer = new byte[8192];
- int bytesRead;
- while ((bytesRead = in.read(buffer)) != -1) {
- fileOut.write(buffer, 0, bytesRead);
- }
-
- System.out.println("Downloaded successfully");
- }
- } else {
- System.err.println("Download failed. HTTP Status Code: " + connection.getResponseCode());
- }
- } catch (IOException e) {
- throw new RuntimeException("Error downloading snapshot " + e.getMessage());
- }
- }
-
- @Override
- public void close() throws InterruptedException {
- this.channel.shutdown().awaitTermination(10, TimeUnit.SECONDS);
- }
+ private static final Logger logger = LoggerFactory.getLogger(QdrantClient.class);
+ private final QdrantGrpcClient grpcClient;
+
+ /**
+ * Creates a new instance of {@link QdrantClient}
+ *
+ * @param grpcClient The low-level gRPC client to use.
+ */
+ public QdrantClient(QdrantGrpcClient grpcClient) {
+ this.grpcClient = grpcClient;
+ }
+
+ /**
+ * Gets detailed information about the qdrant cluster.
+ *
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture healthCheckAsync() {
+ return healthCheckAsync(null);
+ }
+
+ /**
+ * Gets detailed information about the qdrant cluster.
+ *
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture healthCheckAsync(@Nullable Duration timeout) {
+ QdrantFutureStub qdrant = timeout != null
+ ? this.grpcClient.qdrant().withDeadlineAfter(timeout.toMillis(), TimeUnit.MILLISECONDS)
+ : this.grpcClient.qdrant();
+ return qdrant.healthCheck(HealthCheckRequest.getDefaultInstance());
+ }
+
+ //region Collections
+
+ /**
+ * Creates a new collection with the given parameters
+ *
+ * @param collectionName The name of the collection.
+ * @param vectorParams The vector parameters
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createCollectionAsync(
+ String collectionName,
+ VectorParams vectorParams) {
+ return createCollectionAsync(collectionName, vectorParams, null);
+ }
+
+ /**
+ * Creates a new collection with the given parameters
+ *
+ * @param collectionName The name of the collection.
+ * @param vectorParams The vector parameters
+ * @param timeout The timeout for the call
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createCollectionAsync(
+ String collectionName,
+ VectorParams vectorParams,
+ @Nullable Duration timeout) {
+ return createCollectionAsync(CreateCollection.newBuilder()
+ .setCollectionName(collectionName)
+ .setVectorsConfig(VectorsConfig.newBuilder()
+ .setParams(vectorParams)
+ .build())
+ .build(),
+ timeout);
+ }
+
+ /**
+ * Creates a new collection with the given parameters
+ *
+ * @param collectionName The name of the collection.
+ * @param namedVectorParams The named vector parameters
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createCollectionAsync(
+ String collectionName,
+ Map namedVectorParams) {
+ return createCollectionAsync(collectionName, namedVectorParams, null);
+ }
+
+ /**
+ * Creates a new collection with the given parameters
+ *
+ * @param collectionName The name of the collection.
+ * @param namedVectorParams The named vector parameters
+ * @param timeout The timeout for the call
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createCollectionAsync(
+ String collectionName,
+ Map namedVectorParams,
+ @Nullable Duration timeout) {
+ return createCollectionAsync(CreateCollection.newBuilder()
+ .setCollectionName(collectionName)
+ .setVectorsConfig(VectorsConfig.newBuilder()
+ .setParamsMap(VectorParamsMap.newBuilder().putAllMap(namedVectorParams).build())
+ .build())
+ .build(),
+ timeout);
+ }
+
+ /**
+ * Creates a new collection with the given parameters
+ *
+ * @param createCollection The collection creation parameters
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createCollectionAsync(CreateCollection createCollection) {
+ return createCollectionAsync(createCollection, null);
+ }
+
+ /**
+ * Creates a new collection with the given parameters
+ *
+ * @param createCollection The collection creation parameters
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createCollectionAsync(CreateCollection createCollection, @Nullable Duration timeout) {
+ String collectionName = createCollection.getCollectionName();
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ logger.debug("Create collection '{}'", collectionName);
+ ListenableFuture future = getCollections(timeout).create(createCollection);
+ addLogFailureCallback(future, "Create collection");
+ return Futures.transform(future, response -> {
+ if (!response.getResult()) {
+ logger.error("Collection '{}' could not be created", collectionName);
+ throw new QdrantException("Collection '" + collectionName + "' could not be created");
+ }
+ return response;
+ }, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Deletes a collection if one exists, and creates a new collection with the given parameters.
+ *
+ * @param collectionName The name of the collection.
+ * @param vectorParams The vector parameters
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture recreateCollectionAsync(
+ String collectionName,
+ VectorParams vectorParams) {
+ return recreateCollectionAsync(collectionName, vectorParams, null);
+ }
+
+ /**
+ * Deletes a collection if one exists, and creates a new collection with the given parameters.
+ *
+ * @param collectionName The name of the collection.
+ * @param vectorParams The vector parameters
+ * @param timeout The timeout for the call
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture recreateCollectionAsync(
+ String collectionName,
+ VectorParams vectorParams,
+ @Nullable Duration timeout) {
+ return recreateCollectionAsync(CreateCollection.newBuilder()
+ .setCollectionName(collectionName)
+ .setVectorsConfig(VectorsConfig.newBuilder()
+ .setParams(vectorParams)
+ .build())
+ .build(),
+ timeout);
+ }
+
+ /**
+ * Deletes a collection if one exists, and creates a new collection with the given parameters.
+ *
+ * @param collectionName The name of the collection.
+ * @param namedVectorParams The named vector parameters
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture recreateCollectionAsync(
+ String collectionName,
+ Map namedVectorParams) {
+ return recreateCollectionAsync(collectionName, namedVectorParams, null);
+ }
+
+ /**
+ * Deletes a collection if one exists, and creates a new collection with the given parameters.
+ *
+ * @param collectionName The name of the collection.
+ * @param namedVectorParams The named vector parameters
+ * @param timeout The timeout for the call
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture recreateCollectionAsync(
+ String collectionName,
+ Map namedVectorParams,
+ @Nullable Duration timeout) {
+ return recreateCollectionAsync(CreateCollection.newBuilder()
+ .setCollectionName(collectionName)
+ .setVectorsConfig(VectorsConfig.newBuilder()
+ .setParamsMap(VectorParamsMap.newBuilder().putAllMap(namedVectorParams).build())
+ .build())
+ .build(),
+ timeout);
+ }
+
+ /**
+ * Deletes a collection if one exists, and creates a new collection with the given parameters.
+ *
+ * @param createCollection The collection creation parameters
+ * @return a new instance of {@link CollectionOperationResponse}
+ */
+ public ListenableFuture recreateCollectionAsync(CreateCollection createCollection) {
+ return recreateCollectionAsync(createCollection, null);
+ }
+
+ /**
+ * Deletes a collection if one exists, and creates a new collection with the given parameters.
+ *
+ * @param createCollection The collection creation parameters
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link CollectionOperationResponse}
+ */
+ public ListenableFuture recreateCollectionAsync(CreateCollection createCollection, @Nullable Duration timeout) {
+ return Futures.transformAsync(
+ deleteCollectionAsync(createCollection.getCollectionName(), timeout),
+ input -> createCollectionAsync(createCollection, timeout),
+ MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Gets detailed information about an existing collection.
+ *
+ * @param collectionName The name of the collection.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture getCollectionInfoAsync(String collectionName) {
+ return getCollectionInfoAsync(collectionName, null);
+ }
+
+ /**
+ * Gets detailed information about an existing collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture getCollectionInfoAsync(String collectionName, @Nullable Duration timeout) {
+ logger.debug("Get collection info for '{}'", collectionName);
+ GetCollectionInfoRequest request = GetCollectionInfoRequest.newBuilder()
+ .setCollectionName(collectionName)
+ .build();
+ ListenableFuture future = getCollections(timeout).get(request);
+ addLogFailureCallback(future, "Get collection info");
+ return Futures.transform(future, GetCollectionInfoResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Deletes a collection and all its associated data.
+ *
+ * @param collectionName The name of the collection
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteCollectionAsync(String collectionName) {
+ return deleteCollectionAsync(collectionName, null);
+ }
+
+ /**
+ * Deletes a collection and all its associated data.
+ *
+ * @param collectionName The name of the collection
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteCollectionAsync(String collectionName, @Nullable Duration timeout) {
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ logger.debug("Delete collection '{}'", collectionName);
+
+ DeleteCollection deleteCollection = DeleteCollection.newBuilder()
+ .setCollectionName(collectionName)
+ .build();
+ ListenableFuture future = getCollections(timeout).delete(deleteCollection);
+ addLogFailureCallback(future, "Delete collection");
+
+ return Futures.transform(future, response -> {
+ if (!response.getResult()) {
+ logger.error("Collection '{}' could not be deleted", collectionName);
+ throw new QdrantException("Collection '" + collectionName + "' could not be deleted");
+ }
+ return response;
+ }, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Gets the names of all existing collections
+ *
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listCollectionsAsync() {
+ return listCollectionsAsync(null);
+ }
+
+ /**
+ * Gets the names of all existing collections
+ *
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listCollectionsAsync(@Nullable Duration timeout) {
+ logger.debug("List collections");
+
+ ListenableFuture future =
+ getCollections(timeout).list(ListCollectionsRequest.getDefaultInstance());
+
+ addLogFailureCallback(future, "List collection");
+ return Futures.transform(future, response ->
+ response.getCollectionsList()
+ .stream()
+ .map(CollectionDescription::getName)
+ .collect(Collectors.toList()), MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Update parameters of the collection
+ *
+ * @param updateCollection The update parameters.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture updateCollectionAsync(UpdateCollection updateCollection) {
+ return updateCollectionAsync(updateCollection, null);
+ }
+
+ /**
+ * Update parameters of the collection
+ *
+ * @param updateCollection The update parameters.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture updateCollectionAsync(UpdateCollection updateCollection, @Nullable Duration timeout) {
+ String collectionName = updateCollection.getCollectionName();
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ logger.debug("Update collection '{}'", collectionName);
+
+ ListenableFuture future = getCollections(timeout).update(updateCollection);
+ addLogFailureCallback(future, "Update collection");
+ return Futures.transform(future, response -> {
+ if (!response.getResult()) {
+ logger.error("Collection '{}' could not be updated", collectionName);
+ throw new QdrantException("Collection '" + collectionName + "' could not be updated");
+ }
+ return response;
+ }, MoreExecutors.directExecutor());
+ }
+
+ //endregion
+
+ //region Alias Management
+
+ /**
+ * Creates an alias for a given collection.
+ *
+ * @param aliasName The alias to be created.
+ * @param collectionName The collection for which the alias is to be created.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createAliasAsync(String aliasName, String collectionName) {
+ return createAliasAsync(aliasName, collectionName, null);
+ }
+
+ /**
+ * Creates an alias for a given collection.
+ *
+ * @param aliasName The alias to be created.
+ * @param collectionName The collection for which the alias is to be created.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createAliasAsync(String aliasName, String collectionName, @Nullable Duration timeout) {
+ return updateAliasesAsync(ImmutableList.of(AliasOperations.newBuilder()
+ .setCreateAlias(CreateAlias.newBuilder()
+ .setAliasName(aliasName)
+ .setCollectionName(collectionName)
+ .build())
+ .build()),
+ timeout);
+ }
+
+ /**
+ * Renames an existing alias.
+ *
+ * @param oldAliasName The old alias name.
+ * @param newAliasName The new alias name.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture renameAliasAsync(String oldAliasName, String newAliasName) {
+ return renameAliasAsync(oldAliasName, newAliasName, null);
+ }
+
+ /**
+ * Renames an existing alias.
+ *
+ * @param oldAliasName The old alias name.
+ * @param newAliasName The new alias name.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture renameAliasAsync(String oldAliasName, String newAliasName, @Nullable Duration timeout) {
+ return updateAliasesAsync(ImmutableList.of(AliasOperations.newBuilder()
+ .setRenameAlias(RenameAlias.newBuilder()
+ .setOldAliasName(oldAliasName)
+ .setNewAliasName(newAliasName)
+ .build())
+ .build()),
+ timeout);
+ }
+
+ /**
+ * Deletes an alias.
+ *
+ * @param aliasName The alias to be deleted.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteAliasAsync(String aliasName) {
+ return deleteAliasAsync(aliasName, null);
+ }
+
+ /**
+ * Deletes an alias.
+ *
+ * @param aliasName The alias to be deleted.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteAliasAsync(String aliasName, @Nullable Duration timeout) {
+ return updateAliasesAsync(ImmutableList.of(AliasOperations.newBuilder()
+ .setDeleteAlias(DeleteAlias.newBuilder()
+ .setAliasName(aliasName)
+ .build())
+ .build()),
+ timeout);
+ }
+
+ /**
+ * Update the aliases of existing collections.
+ *
+ * @param aliasOperations The list of operations to perform.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture updateAliasesAsync(List aliasOperations) {
+ return updateAliasesAsync(aliasOperations, null);
+ }
+
+ /**
+ * Update the aliases of existing collections.
+ *
+ * @param aliasOperations The list of operations to perform.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture updateAliasesAsync(List aliasOperations, @Nullable Duration timeout) {
+ ChangeAliases request = ChangeAliases.newBuilder()
+ .addAllActions(aliasOperations)
+ .build();
+
+ if (logger.isDebugEnabled()) {
+ for (AliasOperations aliasOperation : aliasOperations) {
+ switch (aliasOperation.getActionCase()) {
+ case CREATE_ALIAS:
+ CreateAlias createAlias = aliasOperation.getCreateAlias();
+ logger.debug("Create alias '{}' for collection '{}'",
+ createAlias.getAliasName(),
+ createAlias.getCollectionName());
+ break;
+ case RENAME_ALIAS:
+ RenameAlias renameAlias = aliasOperation.getRenameAlias();
+ logger.debug("Rename alias '{}' to '{}'",
+ renameAlias.getOldAliasName(),
+ renameAlias.getNewAliasName());
+ break;
+ case DELETE_ALIAS:
+ DeleteAlias deleteAlias = aliasOperation.getDeleteAlias();
+ logger.debug("Delete alias '{}'", deleteAlias.getAliasName());
+ break;
+ case ACTION_NOT_SET:
+ break;
+ }
+ }
+ }
+
+ ListenableFuture future = getCollections(timeout).updateAliases(request);
+ addLogFailureCallback(future, "Update aliases");
+ return Futures.transform(future, response -> {
+ if (!response.getResult()) {
+ logger.error("Alias update operation could not be performed");
+ throw new QdrantException("Alias update could not be performed");
+ }
+ return response;
+ }, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Gets a list of all aliases for a collection.
+ *
+ * @param collectionName The name of the collection.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listCollectionAliasesAsync(String collectionName) {
+ return listCollectionAliasesAsync(collectionName, null);
+ }
+
+ /**
+ * Gets a list of all aliases for a collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listCollectionAliasesAsync(String collectionName, @Nullable Duration timeout) {
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ logger.debug("List aliases for collection '{}'", collectionName);
+
+ ListCollectionAliasesRequest request = ListCollectionAliasesRequest.newBuilder()
+ .setCollectionName(collectionName)
+ .build();
+
+ ListenableFuture future = getCollections(timeout).listCollectionAliases(request);
+ addLogFailureCallback(future, "List collection aliases");
+ return Futures.transform(future, response -> response.getAliasesList()
+ .stream()
+ .map(AliasDescription::getAliasName)
+ .collect(Collectors.toList()), MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Gets a list of all aliases for all existing collections.
+ *
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listAliasesAsync() {
+ return listAliasesAsync(null);
+ }
+
+ /**
+ * Gets a list of all aliases for all existing collections.
+ *
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listAliasesAsync(@Nullable Duration timeout) {
+ logger.debug("List all aliases");
+ ListenableFuture future = getCollections(timeout).listAliases(ListAliasesRequest.getDefaultInstance());
+ addLogFailureCallback(future, "List aliases");
+ return Futures.transform(future, ListAliasesResponse::getAliasesList, MoreExecutors.directExecutor());
+ }
+
+ //endregion
+
+ //region Point Management
+
+ /**
+ * Perform insert and updates on points. If a point with a given ID already exists, it will be overwritten.
+ *
+ * @param collectionName The name of the collection.
+ * @param points The points to be upserted
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture upsertAsync(
+ String collectionName,
+ List points) {
+ return upsertAsync(collectionName, points, null);
+ }
+
+ /**
+ * Perform insert and updates on points. If a point with a given ID already exists, it will be overwritten.
+ * The call waits for the changes to be applied.
+ *
+ * @param collectionName The name of the collection.
+ * @param points The points to be upserted
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture upsertAsync(
+ String collectionName,
+ List points,
+ @Nullable Duration timeout) {
+ return upsertAsync(
+ UpsertPoints.newBuilder()
+ .setCollectionName(collectionName)
+ .addAllPoints(points)
+ .setWait(true)
+ .build(),
+ timeout);
+ }
+
+ /**
+ * Perform insert and updates on points. If a point with a given ID already exists, it will be overwritten.
+ *
+ * @param request The upsert points request
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture upsertAsync(UpsertPoints request) {
+ return upsertAsync(request, null);
+ }
+
+ /**
+ * Perform insert and updates on points. If a point with a given ID already exists, it will be overwritten.
+ *
+ * @param request The upsert points request
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture upsertAsync(
+ UpsertPoints request,
+ @Nullable Duration timeout) {
+ String collectionName = request.getCollectionName();
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ logger.debug("Upsert {} points into '{}'", request.getPointsList().size(), collectionName);
+ ListenableFuture future = getPoints(timeout).upsert(request);
+ addLogFailureCallback(future, "Upsert");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Deletes points.
+ * The call waits for the changes to be applied.
+ *
+ * @param collectionName The name of the collection.
+ * @param ids The ids of points to delete.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteAsync(
+ String collectionName,
+ List ids) {
+ return deleteAsync(collectionName, ids, null);
+ }
+
+ /**
+ * Deletes points.
+ * The call waits for the changes to be applied.
+ *
+ * @param collectionName The name of the collection.
+ * @param ids The ids of points to delete.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteAsync(
+ String collectionName,
+ List ids,
+ @Nullable Duration timeout) {
+ return deleteAsync(DeletePoints.newBuilder()
+ .setCollectionName(collectionName)
+ .setPoints(PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addAllIds(ids).build()).build())
+ .setWait(true)
+ .build(),
+ timeout);
+ }
+
+ /**
+ * Deletes points.
+ * The call waits for the changes to be applied.
+ *
+ * @param collectionName The name of the collection.
+ * @param filter A filter selecting the points to be deleted.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteAsync(
+ String collectionName,
+ Filter filter) {
+ return deleteAsync(collectionName, filter, null);
+ }
+
+ /**
+ * Deletes points.
+ *
+ * @param collectionName The name of the collection.
+ * @param filter A filter selecting the points to be deleted.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteAsync(
+ String collectionName,
+ Filter filter,
+ @Nullable Duration timeout) {
+ return deleteAsync(DeletePoints.newBuilder()
+ .setCollectionName(collectionName)
+ .setPoints(PointsSelector.newBuilder().setFilter(filter).build())
+ .setWait(true)
+ .build(),
+ timeout);
+ }
+
+ /**
+ * Deletes points.
+ *
+ * @param request The delete points request
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteAsync(DeletePoints request) {
+ return deleteAsync(request, null);
+ }
+
+ /**
+ * Deletes points.
+ *
+ * @param request The delete points request
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteAsync(
+ DeletePoints request,
+ @Nullable Duration timeout) {
+ String collectionName = request.getCollectionName();
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ logger.debug("Delete from '{}'", collectionName);
+ ListenableFuture future = getPoints(timeout).delete(request);
+ addLogFailureCallback(future, "Delete");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Retrieves points. Includes all payload, excludes vectors.
+ *
+ * @param collectionName The name of the collection.
+ * @param id The id of a point to retrieve
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> retrieveAsync(
+ String collectionName,
+ PointId id,
+ @Nullable ReadConsistency readConsistency
+ ) {
+ return retrieveAsync(
+ collectionName,
+ id,
+ true,
+ false,
+ readConsistency
+ );
+ }
+
+ /**
+ * Retrieves points.
+ *
+ * @param collectionName The name of the collection.
+ * @param id The id of a point to retrieve
+ * @param withPayload Whether to include the payload or not.
+ * @param withVectors Whether to include the vectors or not.
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> retrieveAsync(
+ String collectionName,
+ PointId id,
+ boolean withPayload,
+ boolean withVectors,
+ @Nullable ReadConsistency readConsistency
+ ) {
+ return retrieveAsync(
+ collectionName,
+ ImmutableList.of(id),
+ WithPayloadSelectorFactory.enable(withPayload),
+ WithVectorsSelectorFactory.enable(withVectors),
+ readConsistency
+ );
+ }
+
+ /**
+ * Retrieves points. Includes all payload, excludes vectors.
+ *
+ * @param collectionName The name of the collection.
+ * @param ids The list of ids of points to retrieve
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> retrieveAsync(
+ String collectionName,
+ List ids,
+ @Nullable ReadConsistency readConsistency
+ ) {
+ return retrieveAsync(
+ collectionName,
+ ids,
+ true,
+ false,
+ readConsistency
+ );
+ }
+
+ /**
+ * Retrieves points.
+ *
+ * @param collectionName The name of the collection.
+ * @param ids The list of ids of points to retrieve
+ * @param withPayload Whether to include the payload or not.
+ * @param withVectors Whether to include the vectors or not.
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> retrieveAsync(
+ String collectionName,
+ List ids,
+ boolean withPayload,
+ boolean withVectors,
+ @Nullable ReadConsistency readConsistency
+ ) {
+ return retrieveAsync(
+ collectionName,
+ ids,
+ WithPayloadSelectorFactory.enable(withPayload),
+ WithVectorsSelectorFactory.enable(withVectors),
+ readConsistency
+ );
+ }
+
+ /**
+ * Retrieves points.
+ *
+ * @param collectionName The name of the collection.
+ * @param ids The list of ids of points to retrieve
+ * @param payloadSelector Options for specifying which payload to include or not.
+ * @param vectorsSelector Options for specifying which vectors to include into response.
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> retrieveAsync(
+ String collectionName,
+ List ids,
+ WithPayloadSelector payloadSelector,
+ WithVectorsSelector vectorsSelector,
+ @Nullable ReadConsistency readConsistency
+ ) {
+ return retrieveAsync(collectionName, ids, payloadSelector, vectorsSelector, readConsistency, null);
+ }
+
+ /**
+ * Retrieves points.
+ *
+ * @param collectionName The name of the collection.
+ * @param ids The list of ids of points to retrieve
+ * @param payloadSelector Options for specifying which payload to include or not.
+ * @param vectorsSelector Options for specifying which vectors to include into response.
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> retrieveAsync(
+ String collectionName,
+ List ids,
+ WithPayloadSelector payloadSelector,
+ WithVectorsSelector vectorsSelector,
+ @Nullable ReadConsistency readConsistency,
+ @Nullable Duration timeout
+ ) {
+ logger.debug("Retrieve points from '{}'", collectionName);
+ GetPoints.Builder requestBuilder = GetPoints.newBuilder()
+ .setCollectionName(collectionName)
+ .addAllIds(ids)
+ .setWithPayload(payloadSelector)
+ .setWithVectors(vectorsSelector);
+
+ if (readConsistency != null) {
+ requestBuilder.setReadConsistency(readConsistency);
+ }
+
+ ListenableFuture future = getPoints(timeout).get(requestBuilder.build());
+ addLogFailureCallback(future, "Retrieve");
+ return Futures.transform(future, GetResponse::getResultList, MoreExecutors.directExecutor());
+ }
+
+ //region Update Vectors
+
+ /**
+ * Update named vectors for point.
+ *
+ * @param collectionName The name of the collection.
+ * @param points The list of points and vectors to update.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture updateVectorsAsync(
+ String collectionName,
+ List points
+ ) {
+ return updateVectorsAsync(collectionName, points, null, null, null);
+ }
+
+ /**
+ * Update named vectors for point.
+ *
+ * @param collectionName The name of the collection.
+ * @param points The list of points and vectors to update.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture updateVectorsAsync(
+ String collectionName,
+ List points,
+ @Nullable Duration timeout
+ ) {
+ return updateVectorsAsync(collectionName, points, null, null, timeout);
+ }
+
+ /**
+ * Update named vectors for point.
+ *
+ * @param collectionName The name of the collection.
+ * @param points The list of points and vectors to update.
+ * @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 updateVectorsAsync(
+ String collectionName,
+ List points,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ logger.debug("Update vectors in '{}'", collectionName);
+ UpdatePointVectors.Builder requestBuilder = UpdatePointVectors.newBuilder()
+ .setCollectionName(collectionName)
+ .addAllPoints(points)
+ .setWait(wait == null || wait);
+
+ if (ordering != null) {
+ requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build());
+ }
+
+ ListenableFuture future = getPoints(timeout).updateVectors(requestBuilder.build());
+ addLogFailureCallback(future, "Update vectors");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ //endregion
+
+ //region Delete Vectors
+
+ /**
+ * Delete named vectors for points.
+ *
+ * @param collectionName The name of the collection.
+ * @param vectors The list of vector names to delete.
+ * @param filter A filter selecting the points to be deleted.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteVectorsAsync(
+ String collectionName,
+ List vectors,
+ Filter filter
+ ) {
+ return deleteVectorsAsync(
+ collectionName,
+ vectors,
+ PointsSelector.newBuilder().setFilter(filter).build(),
+ null,
+ null,
+ null
+ );
+ }
+
+ /**
+ * Delete named vectors for points.
+ *
+ * @param collectionName The name of the collection.
+ * @param vectors The list of vector names to delete.
+ * @param filter A filter selecting the points to be deleted.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteVectorsAsync(
+ String collectionName,
+ List vectors,
+ Filter filter,
+ @Nullable Duration timeout
+ ) {
+ return deleteVectorsAsync(
+ collectionName,
+ vectors,
+ PointsSelector.newBuilder().setFilter(filter).build(),
+ null,
+ null,
+ timeout
+ );
+ }
+
+ /**
+ * Delete named vectors for points.
+ *
+ * @param collectionName The name of the collection.
+ * @param vectors The list of vector names to delete.
+ * @param filter A filter selecting the points to be deleted.
+ * @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 deleteVectorsAsync(
+ String collectionName,
+ List vectors,
+ Filter filter,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return deleteVectorsAsync(
+ collectionName,
+ vectors,
+ PointsSelector.newBuilder().setFilter(filter).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Delete named vectors for points.
+ *
+ * @param collectionName The name of the collection.
+ * @param vectors The list of vector names to delete.
+ * @param ids The list of ids to delete.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteVectorsAsync(
+ String collectionName,
+ List vectors,
+ List ids
+ ) {
+ return deleteVectorsAsync(
+ collectionName,
+ vectors,
+ PointsSelector.newBuilder()
+ .setPoints(PointsIdsList.newBuilder().addAllIds(ids).build())
+ .build(),
+ null,
+ null,
+ null
+ );
+ }
+
+ /**
+ * Delete named vectors for points.
+ *
+ * @param collectionName The name of the collection.
+ * @param vectors The list of vector names to delete.
+ * @param ids The list of ids to delete.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteVectorsAsync(
+ String collectionName,
+ List vectors,
+ List ids,
+ @Nullable Duration timeout
+ ) {
+ return deleteVectorsAsync(
+ collectionName,
+ vectors,
+ PointsSelector.newBuilder()
+ .setPoints(PointsIdsList.newBuilder().addAllIds(ids).build())
+ .build(),
+ null,
+ null,
+ timeout
+ );
+ }
+
+ /**
+ * Delete named vectors for points.
+ *
+ * @param collectionName The name of the collection.
+ * @param vectors The list of vector names to delete.
+ * @param ids The list of ids to delete.
+ * @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 deleteVectorsAsync(
+ String collectionName,
+ List vectors,
+ List ids,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return deleteVectorsAsync(
+ collectionName,
+ vectors,
+ PointsSelector.newBuilder()
+ .setPoints(PointsIdsList.newBuilder().addAllIds(ids).build())
+ .build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ private ListenableFuture deleteVectorsAsync(
+ String collectionName,
+ List vectors,
+ PointsSelector pointsSelector,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ logger.debug("Delete vectors in '{}'", collectionName);
+ DeletePointVectors.Builder requestBuilder = DeletePointVectors.newBuilder()
+ .setCollectionName(collectionName)
+ .setVectors(VectorsSelector.newBuilder()
+ .addAllNames(vectors)
+ .build())
+ .setPointsSelector(pointsSelector)
+ .setWait(wait == null || wait);
+
+ if (ordering != null) {
+ requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build());
+ }
+
+ ListenableFuture future = getPoints(timeout).deleteVectors(requestBuilder.build());
+ addLogFailureCallback(future, "Delete vectors");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ //endregion
+
+ //endregion
+
+ //region Set Payload
+
+ /**
+ * Sets the payload for all points in the collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param payload New payload values
+ * @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 setPayloadAsync(
+ String collectionName,
+ Map payload,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return setPayloadAsync(
+ collectionName,
+ payload,
+ (PointsSelector) null,
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Sets the payload for the given id.
+ *
+ * @param collectionName The name of the collection.
+ * @param payload New payload values
+ * @param id The id for which to set the payload.
+ * @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 setPayloadAsync(
+ String collectionName,
+ Map payload,
+ PointId id,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return setPayloadAsync(
+ collectionName,
+ payload,
+ PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addIds(id).build()).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Sets the payload for the given ids.
+ *
+ * @param collectionName The name of the collection.
+ * @param payload New payload values
+ * @param ids The ids for which to set the payload.
+ * @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 setPayloadAsync(
+ String collectionName,
+ Map payload,
+ List ids,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return setPayloadAsync(
+ collectionName,
+ payload,
+ PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addAllIds(ids).build()).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Sets the payload for the given ids.
+ *
+ * @param collectionName The name of the collection.
+ * @param payload New payload values
+ * @param filter A filter selecting the points to be set.
+ * @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 setPayloadAsync(
+ String collectionName,
+ Map payload,
+ Filter filter,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return setPayloadAsync(
+ collectionName,
+ payload,
+ PointsSelector.newBuilder().setFilter(filter).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ private ListenableFuture setPayloadAsync(
+ String collectionName,
+ Map payload,
+ @Nullable PointsSelector pointsSelector,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ SetPayloadPoints.Builder requestBuilder = SetPayloadPoints.newBuilder()
+ .setCollectionName(collectionName)
+ .setWait(wait == null || wait)
+ .putAllPayload(payload);
+
+ if (pointsSelector != null) {
+ requestBuilder.setPointsSelector(pointsSelector);
+ }
+
+ if (ordering != null) {
+ requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build());
+ }
+
+ logger.debug("Set payload in '{}'", collectionName);
+ ListenableFuture future = getPoints(timeout).setPayload(requestBuilder.build());
+ addLogFailureCallback(future, "Set payload");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ //endregion
+
+ //region Overwrite payload
+
+ /**
+ * Overwrites the payload for all points in the collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param payload New payload values
+ * @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 overwritePayloadAsync(
+ String collectionName,
+ Map payload,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return overwritePayloadAsync(
+ collectionName,
+ payload,
+ (PointsSelector) null,
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Overwrites the payload for the given id.
+ *
+ * @param collectionName The name of the collection.
+ * @param payload New payload values
+ * @param id The id for which to overwrite the payload.
+ * @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 overwritePayloadAsync(
+ String collectionName,
+ Map payload,
+ PointId id,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return overwritePayloadAsync(
+ collectionName,
+ payload,
+ PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addIds(id).build()).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Overwrites the payload for the given ids.
+ *
+ * @param collectionName The name of the collection.
+ * @param payload New payload values
+ * @param ids The ids for which to overwrite the payload.
+ * @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 overwritePayloadAsync(
+ String collectionName,
+ Map payload,
+ List ids,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return overwritePayloadAsync(
+ collectionName,
+ payload,
+ PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addAllIds(ids).build()).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Overwrites the payload for the given ids.
+ *
+ * @param collectionName The name of the collection.
+ * @param payload New payload values
+ * @param filter A filter selecting the points for which to overwrite the payload.
+ * @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 overwritePayloadAsync(
+ String collectionName,
+ Map payload,
+ Filter filter,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return overwritePayloadAsync(
+ collectionName,
+ payload,
+ PointsSelector.newBuilder().setFilter(filter).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ private ListenableFuture overwritePayloadAsync(
+ String collectionName,
+ Map payload,
+ @Nullable PointsSelector pointsSelector,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ SetPayloadPoints.Builder requestBuilder = SetPayloadPoints.newBuilder()
+ .setCollectionName(collectionName)
+ .setWait(wait == null || wait)
+ .putAllPayload(payload);
+
+ if (pointsSelector != null) {
+ requestBuilder.setPointsSelector(pointsSelector);
+ }
+
+ if (ordering != null) {
+ requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build());
+ }
+
+ logger.debug("Overwrite payload in '{}'", collectionName);
+ ListenableFuture future = getPoints(timeout).overwritePayload(requestBuilder.build());
+ addLogFailureCallback(future, "Overwrite payload");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ //endregion
+
+ //region Delete Payload
+
+ /**
+ * Delete specified key payload for all points.
+ *
+ * @param collectionName The name of the collection.
+ * @param keys List of keys to delete.
+ * @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 deletePayloadAsync(
+ String collectionName,
+ List keys,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return deletePayloadAsync(
+ collectionName,
+ keys,
+ (PointsSelector) null,
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Delete specified key payload for the given id.
+ *
+ * @param collectionName The name of the collection.
+ * @param keys List of keys to delete.
+ * @param id The id for which to delete the payload.
+ * @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 deletePayloadAsync(
+ String collectionName,
+ List keys,
+ PointId id,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return deletePayloadAsync(
+ collectionName,
+ keys,
+ PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addIds(id).build()).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Delete specified key payload for the given ids.
+ *
+ * @param collectionName The name of the collection.
+ * @param keys List of keys to delete.
+ * @param ids The ids for which to delete the payload.
+ * @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 deletePayloadAsync(
+ String collectionName,
+ List keys,
+ List ids,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return deletePayloadAsync(
+ collectionName,
+ keys,
+ PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addAllIds(ids).build()).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Delete specified key payload for the given ids.
+ *
+ * @param collectionName The name of the collection.
+ * @param keys List of keys to delete.
+ * @param filter A filter selecting the points to for which to delete the payload.
+ * @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 deletePayloadAsync(
+ String collectionName,
+ List keys,
+ Filter filter,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return deletePayloadAsync(
+ collectionName,
+ keys,
+ PointsSelector.newBuilder().setFilter(filter).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ private ListenableFuture deletePayloadAsync(
+ String collectionName,
+ List keys,
+ @Nullable PointsSelector pointsSelector,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ DeletePayloadPoints.Builder requestBuilder = DeletePayloadPoints.newBuilder()
+ .setCollectionName(collectionName)
+ .setWait(wait == null || wait)
+ .addAllKeys(keys);
+
+ if (pointsSelector != null) {
+ requestBuilder.setPointsSelector(pointsSelector);
+ }
+
+ if (ordering != null) {
+ requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build());
+ }
+
+ logger.debug("Delete payload in '{}'", collectionName);
+ ListenableFuture future = getPoints(timeout).deletePayload(requestBuilder.build());
+ addLogFailureCallback(future, "Delete payload");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ //endregion
+
+ //region Clear Payload
+
+ /**
+ * Remove all payload for all points.
+ *
+ * @param collectionName The name of the collection.
+ * @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 clearPayloadAsync(
+ String collectionName,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return clearPayloadAsync(
+ collectionName,
+ (PointsSelector) null,
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Removes all payload for the given id.
+ *
+ * @param collectionName The name of the collection.
+ * @param id The id for which to remove the payload.
+ * @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 clearPayloadAsync(
+ String collectionName,
+ PointId id,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return clearPayloadAsync(
+ collectionName,
+ PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addIds(id).build()).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Removes all payload for the given ids.
+ *
+ * @param collectionName The name of the collection.
+ * @param ids The ids for which to remove the payload.
+ * @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 clearPayloadAsync(
+ String collectionName,
+ List ids,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return clearPayloadAsync(
+ collectionName,
+ PointsSelector.newBuilder().setPoints(PointsIdsList.newBuilder().addAllIds(ids).build()).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ /**
+ * Removes all payload for the given ids.
+ *
+ * @param collectionName The name of the collection.
+ * @param filter A filter selecting the points for which to remove the payload.
+ * @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 clearPayloadAsync(
+ String collectionName,
+ Filter filter,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ return clearPayloadAsync(
+ collectionName,
+ PointsSelector.newBuilder().setFilter(filter).build(),
+ wait,
+ ordering,
+ timeout
+ );
+ }
+
+ private ListenableFuture clearPayloadAsync(
+ String collectionName,
+ @Nullable PointsSelector pointsSelector,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ ClearPayloadPoints.Builder requestBuilder = ClearPayloadPoints.newBuilder()
+ .setCollectionName(collectionName)
+ .setWait(wait == null || wait);
+
+ if (pointsSelector != null) {
+ requestBuilder.setPoints(pointsSelector);
+ }
+
+ if (ordering != null) {
+ requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build());
+ }
+
+ logger.debug("Clear payload in '{}'", collectionName);
+ ListenableFuture future = getPoints(timeout).clearPayload(requestBuilder.build());
+ addLogFailureCallback(future, "Clear payload");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ //endregion
+
+ /**
+ * Creates a payload field index in a collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param field The field name to index.
+ * @param schemaType The schema type of the field.
+ * @param indexParams Payload index parameters.
+ * @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 createPayloadIndexAsync(
+ String collectionName,
+ String field,
+ PayloadSchemaType schemaType,
+ @Nullable PayloadIndexParams indexParams,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ CreateFieldIndexCollection.Builder requestBuilder = CreateFieldIndexCollection.newBuilder()
+ .setCollectionName(collectionName)
+ .setFieldName(field)
+ .setWait(wait == null || wait);
+
+ switch (schemaType) {
+ case Keyword:
+ requestBuilder.setFieldType(FieldType.FieldTypeKeyword);
+ break;
+ case Integer:
+ requestBuilder.setFieldType(FieldType.FieldTypeInteger);
+ break;
+ case Float:
+ requestBuilder.setFieldType(FieldType.FieldTypeFloat);
+ break;
+ case Geo:
+ requestBuilder.setFieldType(FieldType.FieldTypeGeo);
+ break;
+ case Text:
+ requestBuilder.setFieldType(FieldType.FieldTypeText);
+ break;
+ case Bool:
+ requestBuilder.setFieldType(FieldType.FieldTypeBool);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid schemaType: '" + schemaType + "'");
+ }
+
+ if (indexParams != null) {
+ requestBuilder.setFieldIndexParams(indexParams);
+ }
+
+ if (ordering != null) {
+ requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build());
+ }
+
+ logger.debug("Create payload field index for '{}' in '{}'", field, collectionName);
+ ListenableFuture future = getPoints(timeout).createFieldIndex(requestBuilder.build());
+ addLogFailureCallback(future, "Create payload field index");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Deletes a payload field index in a collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param field The field name to index.
+ * @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 deletePayloadIndexAsync(
+ String collectionName,
+ String field,
+ @Nullable Boolean wait,
+ @Nullable WriteOrderingType ordering,
+ @Nullable Duration timeout
+ ) {
+ DeleteFieldIndexCollection.Builder requestBuilder = DeleteFieldIndexCollection.newBuilder()
+ .setCollectionName(collectionName)
+ .setFieldName(field)
+ .setWait(wait == null || wait);
+
+ if (ordering != null) {
+ requestBuilder.setOrdering(WriteOrdering.newBuilder().setType(ordering).build());
+ }
+
+ logger.debug("Delete payload field index for '{}' in '{}'", field, collectionName);
+ ListenableFuture future = getPoints(timeout).deleteFieldIndex(requestBuilder.build());
+ addLogFailureCallback(future, "Delete payload field index");
+ return Futures.transform(future, PointsOperationResponse::getResult, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Retrieves closest points based on vector similarity and the given filtering conditions.
+ *
+ * @param request the search request
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> searchAsync(SearchPoints request) {
+ return searchAsync(request, null);
+ }
+
+ /**
+ * Retrieves closest points based on vector similarity and the given filtering conditions.
+ *
+ * @param request the search request
+ * @param timeout the timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> searchAsync(SearchPoints request, @Nullable Duration timeout) {
+ Preconditions.checkArgument(
+ !request.getCollectionName().isEmpty(),
+ "Collection name must not be empty");
+ Preconditions.checkArgument(
+ !request.getVectorList().isEmpty(),
+ "Vector must not be empty");
+
+ logger.debug("Search on '{}'", request.getCollectionName());
+ ListenableFuture future = getPoints(timeout).search(request);
+ addLogFailureCallback(future, "Search");
+ return Futures.transform(future, SearchResponse::getResultList, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Retrieves closest points based on vector similarity and the given filtering conditions.
+ *
+ * @param collectionName The name of the collection
+ * @param searches The searches to be performed in the batch.
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> searchBatchAsync(
+ String collectionName,
+ List searches,
+ @Nullable ReadConsistency readConsistency
+ ) {
+ return searchBatchAsync(collectionName, searches, readConsistency, null);
+ }
+
+ /**
+ * Retrieves closest points based on vector similarity and the given filtering conditions.
+ *
+ * @param collectionName The name of the collection
+ * @param searches The searches to be performed in the batch.
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> searchBatchAsync(
+ String collectionName,
+ List searches,
+ @Nullable ReadConsistency readConsistency,
+ @Nullable Duration timeout
+ ) {
+ // TODO: Workaround for https://github.com/qdrant/qdrant/issues/2880
+ searches = Lists.transform(
+ searches,
+ searchPoints -> searchPoints.toBuilder().setCollectionName(collectionName).build());
+
+ SearchBatchPoints.Builder requestBuilder = SearchBatchPoints.newBuilder()
+ .setCollectionName(collectionName)
+ .addAllSearchPoints(searches);
+
+ if (readConsistency != null) {
+ requestBuilder.setReadConsistency(readConsistency);
+ }
+
+ logger.debug("Search batch on '{}'", collectionName);
+ ListenableFuture future = getPoints(timeout).searchBatch(requestBuilder.build());
+ addLogFailureCallback(future, "Search batch");
+ return Futures.transform(future, SearchBatchResponse::getResultList, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Retrieves closest points based on vector similarity and the given filtering conditions, grouped by a given field.
+ *
+ * @param request The search group request
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> searchGroupsAsync(SearchPointGroups request) {
+ return searchGroupsAsync(request, null);
+ }
+
+ /**
+ * Retrieves closest points based on vector similarity and the given filtering conditions, grouped by a given field.
+ *
+ * @param request The search group request
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> searchGroupsAsync(SearchPointGroups request, @Nullable Duration timeout) {
+ Preconditions.checkArgument(
+ !request.getCollectionName().isEmpty(),
+ "Collection name must not be empty");
+ logger.debug("Search groups on '{}'", request.getCollectionName());
+ ListenableFuture future = getPoints(timeout).searchGroups(request);
+ addLogFailureCallback(future, "Search groups");
+ return Futures.transform(
+ future,
+ response -> response.getResult().getGroupsList(),
+ MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Iterates over all or filtered points.
+ *
+ * @param request The scroll request
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture scrollAsync(ScrollPoints request) {
+ return scrollAsync(request, null);
+ }
+
+ /**
+ * Iterates over all or filtered points.
+ *
+ * @param request The scroll request.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture scrollAsync(ScrollPoints request, @Nullable Duration timeout) {
+ Preconditions.checkArgument(
+ !request.getCollectionName().isEmpty(),
+ "Collection name must not be empty");
+ logger.debug("Scroll on '{}'", request.getCollectionName());
+ ListenableFuture future = getPoints(timeout).scroll(request);
+ addLogFailureCallback(future, "Scroll");
+ return future;
+ }
+
+ /**
+ * Look for the points which are closer to stored positive examples and at the same time further to negative
+ * examples.
+ *
+ * @param request The recommend request
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> recommendAsync(RecommendPoints request) {
+ return recommendAsync(request, null);
+ }
+
+ /**
+ * Look for the points which are closer to stored positive examples and at the same time further to negative
+ * examples.
+ *
+ * @param request The recommend request.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> recommendAsync(RecommendPoints request, @Nullable Duration timeout) {
+ Preconditions.checkArgument(
+ !request.getCollectionName().isEmpty(),
+ "Collection name must not be empty");
+ logger.debug("Recommend on '{}'", request.getCollectionName());
+ ListenableFuture future = getPoints(timeout).recommend(request);
+ addLogFailureCallback(future, "Recommend");
+ return Futures.transform(
+ future,
+ RecommendResponse::getResultList,
+ MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Look for the points which are closer to stored positive examples and at the same time further to negative
+ * examples.
+ *
+ * @param collectionName The name of the collection.
+ * @param recommendSearches The list of recommendation searches.
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> recommendBatchAsync(
+ String collectionName,
+ List recommendSearches,
+ @Nullable ReadConsistency readConsistency) {
+ return recommendBatchAsync(collectionName, recommendSearches, readConsistency, null);
+ }
+
+ /**
+ * Look for the points which are closer to stored positive examples and at the same time further to negative
+ * examples.
+ *
+ * @param collectionName The name of the collection.
+ * @param recommendSearches The list of recommendation searches.
+ * @param readConsistency Options for specifying read consistency guarantees.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> recommendBatchAsync(
+ String collectionName,
+ List recommendSearches,
+ @Nullable ReadConsistency readConsistency,
+ @Nullable Duration timeout) {
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+
+ // TODO: Workaround for https://github.com/qdrant/qdrant/issues/2880
+ recommendSearches = Lists.transform(
+ recommendSearches,
+ recommendPoints -> recommendPoints.toBuilder().setCollectionName(collectionName).build());
+
+ RecommendBatchPoints.Builder requestBuilder = RecommendBatchPoints.newBuilder()
+ .setCollectionName(collectionName)
+ .addAllRecommendPoints(recommendSearches);
+
+ if (readConsistency != null) {
+ requestBuilder.setReadConsistency(readConsistency);
+ }
+
+ logger.debug("Recommend batch on '{}'", collectionName);
+ ListenableFuture future = getPoints(timeout).recommendBatch(requestBuilder.build());
+ addLogFailureCallback(future, "Recommend batch");
+ return Futures.transform(
+ future,
+ RecommendBatchResponse::getResultList,
+ MoreExecutors.directExecutor());
+ }
+
+ /**
+ * 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
+ *
+ * @param request The recommend groups request
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> recommendGroupsAsync(RecommendPointGroups request) {
+ return recommendGroupsAsync(request, null);
+ }
+
+ /**
+ * 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
+ *
+ * @param request The recommend groups request
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> recommendGroupsAsync(RecommendPointGroups request, @Nullable Duration timeout) {
+ String collectionName = request.getCollectionName();
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ logger.debug("Recommend groups on '{}'", collectionName);
+ ListenableFuture future = getPoints(timeout).recommendGroups(request);
+ addLogFailureCallback(future, "Recommend groups");
+ return Futures.transform(
+ future,
+ response -> response.getResult().getGroupsList(),
+ MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Count the points in a collection. The count is exact
+ *
+ * @param collectionName The name of the collection.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture countAsync(String collectionName) {
+ return countAsync(collectionName, null, null, null);
+ }
+
+ /**
+ * Count the points in a collection. The count is exact
+ *
+ * @param collectionName The name of the collection.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture countAsync(String collectionName, @Nullable Duration timeout) {
+ return countAsync(collectionName, null, null, timeout);
+ }
+
+ /**
+ * Count the points in a collection with the given filtering conditions.
+ *
+ * @param collectionName The name of the collection.
+ * @param filter Filter conditions - return only those points that satisfy the specified conditions.
+ * @param exact If true, returns the exact count,
+ * if false, returns an approximate count. Defaults to true.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture countAsync(
+ String collectionName,
+ @Nullable Filter filter,
+ @Nullable Boolean exact) {
+ return countAsync(collectionName, filter, exact, null);
+ }
+
+ /**
+ * Count the points in a collection with the given filtering conditions.
+ *
+ * @param collectionName The name of the collection.
+ * @param filter Filter conditions - return only those points that satisfy the specified conditions.
+ * @param exact If true, returns the exact count,
+ * if false, returns an approximate count. Defaults to true.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture countAsync(
+ String collectionName,
+ @Nullable Filter filter,
+ @Nullable Boolean exact,
+ @Nullable Duration timeout) {
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ CountPoints.Builder requestBuilder = CountPoints.newBuilder()
+ .setCollectionName(collectionName)
+ .setExact(exact == null || exact);
+
+ if (filter != null) {
+ requestBuilder.setFilter(filter);
+ }
+
+ logger.debug("Count on '{}'", collectionName);
+ ListenableFuture future = getPoints(timeout).count(requestBuilder.build());
+ addLogFailureCallback(future, "Count");
+ return Futures.transform(future, response -> response.getResult().getCount(), MoreExecutors.directExecutor());
+ }
+
+ //region Snapshot Management
+
+ /**
+ * Create snapshot for a given collection.
+ *
+ * @param collectionName The name of the collection.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createSnapshotAsync(String collectionName) {
+ return createSnapshotAsync(collectionName, null);
+ }
+
+ /**
+ * Create snapshot for a given collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createSnapshotAsync(String collectionName, @Nullable Duration timeout) {
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ logger.debug("Create snapshot of '{}'", collectionName);
+ ListenableFuture future = getSnapshots(timeout).create(
+ CreateSnapshotRequest.newBuilder()
+ .setCollectionName(collectionName)
+ .build());
+ addLogFailureCallback(future, "Create snapshot");
+ return Futures.transform(future, CreateSnapshotResponse::getSnapshotDescription, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Get list of snapshots for a collection.
+ *
+ * @param collectionName The name of the collection.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listSnapshotAsync(String collectionName) {
+ return listSnapshotAsync(collectionName, null);
+ }
+
+ /**
+ * Get list of snapshots for a collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listSnapshotAsync(String collectionName, @Nullable Duration timeout) {
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ logger.debug("List snapshots of '{}'", collectionName);
+ ListenableFuture future = getSnapshots(timeout).list(ListSnapshotsRequest.newBuilder()
+ .setCollectionName(collectionName)
+ .build());
+ addLogFailureCallback(future, "List snapshots");
+ return Futures.transform(future, ListSnapshotsResponse::getSnapshotDescriptionsList, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Delete snapshot for a given collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param snapshotName The name of the snapshot.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteSnapshotAsync(String collectionName, String snapshotName) {
+ return deleteSnapshotAsync(collectionName, snapshotName, null);
+ }
+
+ /**
+ * Delete snapshot for a given collection.
+ *
+ * @param collectionName The name of the collection.
+ * @param snapshotName The name of the snapshot.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteSnapshotAsync(String collectionName, String snapshotName, @Nullable Duration timeout) {
+ Preconditions.checkArgument(!collectionName.isEmpty(), "Collection name must not be empty");
+ Preconditions.checkArgument(!snapshotName.isEmpty(), "Snapshot name must not be empty");
+ logger.debug("Delete snapshot '{}' of '{}'", snapshotName, collectionName);
+ ListenableFuture future = getSnapshots(timeout).delete(DeleteSnapshotRequest.newBuilder()
+ .setCollectionName(collectionName)
+ .setSnapshotName(snapshotName)
+ .build());
+ addLogFailureCallback(future, "Delete snapshot");
+ return future;
+ }
+
+ /**
+ * Create snapshot for a whole storage.
+ *
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createFullSnapshotAsync() {
+ return createFullSnapshotAsync(null);
+ }
+
+ /**
+ * Create snapshot for a whole storage.
+ *
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture createFullSnapshotAsync(@Nullable Duration timeout) {
+ logger.debug("Create full snapshot for a whole storage");
+ ListenableFuture future =
+ getSnapshots(timeout).createFull(CreateFullSnapshotRequest.getDefaultInstance());
+ addLogFailureCallback(future, "Create full snapshot");
+ return Futures.transform(future, CreateSnapshotResponse::getSnapshotDescription, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Get list of snapshots for a whole storage.
+ *
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listFullSnapshotAsync() {
+ return listFullSnapshotAsync(null);
+ }
+
+ /**
+ * Get list of snapshots for a whole storage.
+ *
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture> listFullSnapshotAsync(@Nullable Duration timeout) {
+ logger.debug("List full snapshots for a whole storage");
+ ListenableFuture future =
+ getSnapshots(timeout).listFull(ListFullSnapshotsRequest.getDefaultInstance());
+ addLogFailureCallback(future, "List full snapshots");
+ return Futures.transform(future, ListSnapshotsResponse::getSnapshotDescriptionsList, MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Delete snapshot for a whole storage.
+ *
+ * @param snapshotName The name of the snapshot.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteFullSnapshotAsync(String snapshotName) {
+ return deleteFullSnapshotAsync(snapshotName, null);
+ }
+
+ /**
+ * Delete snapshot for a whole storage.
+ *
+ * @param snapshotName The name of the snapshot.
+ * @param timeout The timeout for the call.
+ * @return a new instance of {@link ListenableFuture}
+ */
+ public ListenableFuture deleteFullSnapshotAsync(String snapshotName, @Nullable Duration timeout) {
+ Preconditions.checkArgument(!snapshotName.isEmpty(), "Snapshot name must not be empty");
+ logger.debug("Delete full snapshot '{}'", snapshotName);
+ ListenableFuture future = getSnapshots(timeout).deleteFull(DeleteFullSnapshotRequest.newBuilder()
+ .setSnapshotName(snapshotName)
+ .build());
+ addLogFailureCallback(future, "Delete full snapshot");
+ return future;
+ }
+
+ //endregion
+
+ @Override
+ public void close() {
+ grpcClient.close();
+ }
+
+ private void addLogFailureCallback(ListenableFuture future, String message) {
+ Futures.addCallback(future, new FutureCallback() {
+ @Override
+ public void onSuccess(V result) {
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ logger.error(message + " operation failed", t);
+ }
+ }, MoreExecutors.directExecutor());
+ }
+
+ private CollectionsGrpc.CollectionsFutureStub getCollections(@Nullable Duration timeout) {
+ return timeout != null
+ ? this.grpcClient.collections().withDeadlineAfter(timeout.toMillis(), TimeUnit.MILLISECONDS)
+ : this.grpcClient.collections();
+ }
+
+ private PointsGrpc.PointsFutureStub getPoints(@Nullable Duration timeout) {
+ return timeout != null
+ ? this.grpcClient.points().withDeadlineAfter(timeout.toMillis(), TimeUnit.MILLISECONDS)
+ : this.grpcClient.points();
+ }
+
+ private SnapshotsGrpc.SnapshotsFutureStub getSnapshots(@Nullable Duration timeout) {
+ return timeout != null
+ ? this.grpcClient.snapshots().withDeadlineAfter(timeout.toMillis(), TimeUnit.MILLISECONDS)
+ : this.grpcClient.snapshots();
+ }
}
diff --git a/src/main/java/io/qdrant/client/QdrantException.java b/src/main/java/io/qdrant/client/QdrantException.java
new file mode 100644
index 00000000..740b0bf5
--- /dev/null
+++ b/src/main/java/io/qdrant/client/QdrantException.java
@@ -0,0 +1,14 @@
+package io.qdrant.client;
+
+/**
+ * An exception when interacting with qdrant
+ */
+public class QdrantException extends RuntimeException {
+ /**
+ * Instantiates a new instance of {@link QdrantException}
+ * @param message The exception message
+ */
+ public QdrantException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/io/qdrant/client/QdrantGrpcClient.java b/src/main/java/io/qdrant/client/QdrantGrpcClient.java
new file mode 100644
index 00000000..0015ddb6
--- /dev/null
+++ b/src/main/java/io/qdrant/client/QdrantGrpcClient.java
@@ -0,0 +1,219 @@
+package io.qdrant.client;
+
+import io.qdrant.client.grpc.CollectionsGrpc;
+import io.qdrant.client.grpc.PointsGrpc;
+import io.qdrant.client.grpc.QdrantGrpc;
+import io.qdrant.client.grpc.QdrantGrpc.QdrantFutureStub;
+import io.qdrant.client.grpc.SnapshotsGrpc;
+import io.grpc.CallCredentials;
+import io.grpc.Deadline;
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
+import static io.qdrant.client.grpc.CollectionsGrpc.CollectionsFutureStub;
+import static io.qdrant.client.grpc.PointsGrpc.PointsFutureStub;
+import static io.qdrant.client.grpc.SnapshotsGrpc.SnapshotsFutureStub;
+
+/**
+ * Low-level gRPC client for qdrant vector database.
+ */
+public class QdrantGrpcClient implements AutoCloseable {
+ private static final Logger logger = LoggerFactory.getLogger(QdrantGrpcClient.class);
+ @Nullable
+ private final CallCredentials callCredentials;
+ private final ManagedChannel channel;
+ private final boolean shutdownChannelOnClose;
+ @Nullable
+ private final Duration timeout;
+
+ QdrantGrpcClient(
+ ManagedChannel channel,
+ boolean shutdownChannelOnClose,
+ @Nullable CallCredentials callCredentials,
+ @Nullable Duration timeout) {
+ this.callCredentials = callCredentials;
+ this.channel = channel;
+ this.shutdownChannelOnClose = shutdownChannelOnClose;
+ this.timeout = timeout;
+ }
+
+ /**
+ * Creates a new builder to build a client.
+ * @param channel The channel for communication. This channel is not shutdown by the client and must be managed
+ * by the caller.
+ * @return a new instance of {@link Builder}
+ */
+ public static Builder newBuilder(ManagedChannel channel) {
+ return new Builder(channel, false);
+ }
+
+ /**
+ * Creates a new builder to build a client.
+ * @param channel The channel for communication.
+ * @param shutdownChannelOnClose Whether the channel is shutdown on client close.
+ * @return a new instance of {@link Builder}
+ */
+ public static Builder newBuilder(ManagedChannel channel, boolean shutdownChannelOnClose) {
+ return new Builder(channel, shutdownChannelOnClose);
+ }
+
+ /**
+ * Creates a new builder to build a client.
+ * @param host The host to connect to. The default gRPC port 6334 is used.
+ * @return a new instance of {@link Builder}
+ */
+ public static Builder newBuilder(String host) {
+ return new Builder(host, 6334, true);
+ }
+
+ /**
+ * Creates a new builder to build a client. The client uses Transport Layer Security by default.
+ * @param host The host to connect to.
+ * @param port The port to connect to.
+ * @return a new instance of {@link Builder}
+ */
+ public static Builder newBuilder(String host, int port) {
+ return new Builder(host, port, true);
+ }
+
+ /**
+ * Creates a new builder to build a client.
+ * @param host The host to connect to.
+ * @param port The port to connect to.
+ * @param useTransportLayerSecurity Whether the client uses Transport Layer Security (TLS) to secure communications.
+ * Running without TLS should only be used for testing purposes.
+ * @return a new instance of {@link Builder}
+ */
+ public static Builder newBuilder(String host, int port, boolean useTransportLayerSecurity) {
+ return new Builder(host, port, useTransportLayerSecurity);
+ }
+
+ /**
+ * Gets the client for qdrant services
+ * @return a new instance of {@link QdrantFutureStub}
+ */
+ public QdrantGrpc.QdrantFutureStub qdrant() {
+ return QdrantGrpc.newFutureStub(channel)
+ .withCallCredentials(callCredentials)
+ .withDeadline(timeout != null ? Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS) : null);
+ }
+
+ /**
+ * Gets the client for points
+ * @return a new instance of {@link PointsFutureStub}
+ */
+ public PointsFutureStub points() {
+ return PointsGrpc.newFutureStub(channel)
+ .withCallCredentials(callCredentials)
+ .withDeadline(timeout != null ? Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS) : null);
+ }
+
+ /**
+ * Gets the client for collections
+ * @return a new instance of {@link CollectionsFutureStub}
+ */
+ public CollectionsFutureStub collections() {
+ return CollectionsGrpc.newFutureStub(channel)
+ .withCallCredentials(callCredentials)
+ .withDeadline(timeout != null ? Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS) : null);
+ }
+
+ /**
+ * Gets the client for snapshots
+ * @return a new instance of {@link SnapshotsFutureStub}
+ */
+ public SnapshotsFutureStub snapshots() {
+ return SnapshotsGrpc.newFutureStub(channel)
+ .withCallCredentials(callCredentials)
+ .withDeadline(timeout != null ? Deadline.after(timeout.toMillis(), TimeUnit.MILLISECONDS) : null);
+ }
+
+ @Override
+ public void close() {
+ if (shutdownChannelOnClose && !channel.isShutdown() && !channel.isTerminated()) {
+ try {
+ channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ logger.warn("exception thrown when shutting down channel", e);
+ }
+ }
+ }
+
+ /**
+ * builder for {@link QdrantGrpcClient}
+ */
+ public static class Builder {
+ private final ManagedChannel channel;
+ private final boolean shutdownChannelOnClose;
+ @Nullable
+ private CallCredentials callCredentials;
+ @Nullable
+ private Duration timeout;
+
+ Builder(ManagedChannel channel, boolean shutdownChannelOnClose) {
+ this.channel = channel;
+ this.shutdownChannelOnClose = shutdownChannelOnClose;
+ }
+
+ Builder(String host, int port, boolean useTransportLayerSecurity) {
+ this.channel = createChannel(host, port, useTransportLayerSecurity);
+ this.shutdownChannelOnClose = true;
+ }
+
+ /**
+ * Sets the API key to use for authentication
+ * @param apiKey The API key to use.
+ * @return this
+ */
+ public Builder withApiKey(String apiKey) {
+ this.callCredentials = new ApiKeyCredentials(apiKey);
+ return this;
+ }
+
+ /**
+ * Sets a default timeout for all requests.
+ * @param timeout The timeout.
+ * @return this
+ */
+ public Builder withTimeout(@Nullable Duration timeout) {
+ this.timeout = timeout;
+ return this;
+ }
+
+ /**
+ * Sets the credential data that will be propagated to the server via request metadata for each RPC.
+ * @param callCredentials The call credentials to use.
+ * @return this
+ */
+ public Builder withCallCredentials(@Nullable CallCredentials callCredentials) {
+ this.callCredentials = callCredentials;
+ return this;
+ }
+
+ /**
+ * Builds a new instance of {@link QdrantGrpcClient}
+ * @return a new instance of {@link QdrantGrpcClient}
+ */
+ public QdrantGrpcClient build() {
+ return new QdrantGrpcClient(channel, shutdownChannelOnClose, callCredentials, timeout);
+ }
+
+ private static ManagedChannel createChannel(String host, int port, boolean useTransportLayerSecurity) {
+ ManagedChannelBuilder> channelBuilder = ManagedChannelBuilder.forAddress(host, port);
+
+ if (useTransportLayerSecurity) {
+ channelBuilder.useTransportSecurity();
+ } else {
+ channelBuilder.usePlaintext();
+ }
+
+ return channelBuilder.build();
+ }
+ }
+}
diff --git a/src/main/java/io/qdrant/client/TokenInterceptor.java b/src/main/java/io/qdrant/client/TokenInterceptor.java
deleted file mode 100644
index f735ed42..00000000
--- a/src/main/java/io/qdrant/client/TokenInterceptor.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package io.qdrant.client;
-
-import io.grpc.CallOptions;
-import io.grpc.Channel;
-import io.grpc.ClientCall;
-import io.grpc.ClientInterceptor;
-import io.grpc.ForwardingClientCall.SimpleForwardingClientCall;
-import io.grpc.Metadata;
-import io.grpc.MethodDescriptor;
-
-/** Interceptor for adding an API key to the headers of gRPC requests. */
-final class TokenInterceptor implements ClientInterceptor {
- private final String apiKey;
- private final Metadata.Key API_KEY =
- Metadata.Key.of("api-key", Metadata.ASCII_STRING_MARSHALLER);
-
- /**
- * Constructs a new TokenInterceptor with the specified API key.
- *
- * @param apiKey the API key to be added to the headers
- */
- TokenInterceptor(String apiKey) {
- this.apiKey = apiKey;
- }
-
- @Override
- public ClientCall interceptCall(
- MethodDescriptor method, CallOptions callOptions, Channel next) {
- return new SimpleForwardingClientCall(next.newCall(method, callOptions)) {
- @Override
- public void start(Listener responseListener, Metadata headers) {
- headers.put(API_KEY, apiKey);
- super.start(responseListener, headers);
- }
- };
- }
-}
diff --git a/src/main/java/io/qdrant/client/ValueFactory.java b/src/main/java/io/qdrant/client/ValueFactory.java
new file mode 100644
index 00000000..f2264cd2
--- /dev/null
+++ b/src/main/java/io/qdrant/client/ValueFactory.java
@@ -0,0 +1,70 @@
+package io.qdrant.client;
+
+import java.util.List;
+
+import static io.qdrant.client.grpc.JsonWithInt.ListValue;
+import static io.qdrant.client.grpc.JsonWithInt.NullValue;
+import static io.qdrant.client.grpc.JsonWithInt.Value;
+
+/**
+ * Convenience methods for constructing {@link Value}
+ */
+public final class ValueFactory {
+ private ValueFactory() {
+ }
+
+ /**
+ * Creates a value from a {@link String}
+ * @param value The value
+ * @return a new instance of {@link io.qdrant.client.grpc.JsonWithInt.Value}
+ */
+ public static Value value(String value) {
+ return Value.newBuilder().setStringValue(value).build();
+ }
+
+ /**
+ * Creates a value from a {@link long}
+ * @param value The value
+ * @return a new instance of {@link io.qdrant.client.grpc.JsonWithInt.Value}
+ */
+ public static Value value(long value) {
+ return Value.newBuilder().setIntegerValue(value).build();
+ }
+
+ /**
+ * Creates a value from a {@link double}
+ * @param value The value
+ * @return a new instance of {@link io.qdrant.client.grpc.JsonWithInt.Value}
+ */
+ public static Value value(double value) {
+ return Value.newBuilder().setDoubleValue(value).build();
+ }
+
+ /**
+ * Creates a value from a {@link boolean}
+ * @param value The value
+ * @return a new instance of {@link io.qdrant.client.grpc.JsonWithInt.Value}
+ */
+ public static Value value(boolean value) {
+ return Value.newBuilder().setBoolValue(value).build();
+ }
+
+ /**
+ * Creates a null value
+ * @return a new instance of {@link io.qdrant.client.grpc.JsonWithInt.Value}
+ */
+ public static Value nullValue() {
+ return Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build();
+ }
+
+ /**
+ * Creates a value from a list of values
+ * @param values The list of values
+ * @return a new instance of {@link io.qdrant.client.grpc.JsonWithInt.Value}
+ */
+ public static Value list(List values) {
+ return Value.newBuilder()
+ .setListValue(ListValue.newBuilder().addAllValues(values).build())
+ .build();
+ }
+}
diff --git a/src/main/java/io/qdrant/client/VectorsFactory.java b/src/main/java/io/qdrant/client/VectorsFactory.java
new file mode 100644
index 00000000..0e2c3486
--- /dev/null
+++ b/src/main/java/io/qdrant/client/VectorsFactory.java
@@ -0,0 +1,60 @@
+package io.qdrant.client;
+
+import com.google.common.collect.Maps;
+import com.google.common.primitives.Floats;
+
+import java.util.List;
+import java.util.Map;
+
+import static io.qdrant.client.grpc.Points.NamedVectors;
+import static io.qdrant.client.grpc.Points.Vector;
+import static io.qdrant.client.grpc.Points.Vectors;
+
+/**
+ * Convenience methods for constructing {@link Vectors}
+ */
+public final class VectorsFactory {
+ private VectorsFactory() {
+ }
+
+ /**
+ * Creates named vectors
+ * @param values A map of vector names to values
+ * @return a new instance of {@link Vectors}
+ */
+ public static Vectors namedVectors(Map> values) {
+ return Vectors.newBuilder()
+ .setVectors(NamedVectors.newBuilder()
+ .putAllVectors(Maps.transformValues(values, v -> Vector.newBuilder()
+ .addAllData(v)
+ .build()))
+ )
+ .build();
+ }
+
+ /**
+ * Creates a vector
+ * @param values A list of values
+ * @return a new instance of {@link Vectors}
+ */
+ public static Vectors vector(List values) {
+ return Vectors.newBuilder()
+ .setVector(Vector.newBuilder()
+ .addAllData(values)
+ .build())
+ .build();
+ }
+
+ /**
+ * Creates a vector
+ * @param values A list of values
+ * @return a new instance of {@link Vectors}
+ */
+ public static Vectors vector(float... values) {
+ return Vectors.newBuilder()
+ .setVector(Vector.newBuilder()
+ .addAllData(Floats.asList(values))
+ .build())
+ .build();
+ }
+}
diff --git a/src/main/java/io/qdrant/client/WithPayloadSelectorFactory.java b/src/main/java/io/qdrant/client/WithPayloadSelectorFactory.java
new file mode 100644
index 00000000..02c61363
--- /dev/null
+++ b/src/main/java/io/qdrant/client/WithPayloadSelectorFactory.java
@@ -0,0 +1,46 @@
+package io.qdrant.client;
+
+import java.util.List;
+
+import static io.qdrant.client.grpc.Points.PayloadExcludeSelector;
+import static io.qdrant.client.grpc.Points.PayloadIncludeSelector;
+import static io.qdrant.client.grpc.Points.WithPayloadSelector;
+
+/**
+ * Convenience methods for constructing {@link WithPayloadSelector}
+ */
+public final class WithPayloadSelectorFactory {
+ private WithPayloadSelectorFactory() {
+ }
+
+ /**
+ * Whether to include all payload in response.
+ * @param enable if true, to include all payload, if false, none.
+ * @return a new instance of {@link WithPayloadSelector}
+ */
+ public static WithPayloadSelector enable(boolean enable) {
+ return WithPayloadSelector.newBuilder().setEnable(enable).build();
+ }
+
+ /**
+ * Which payload fields to include in response.
+ * @param fields the list of fields to include.
+ * @return a new instance of {@link WithPayloadSelector}
+ */
+ public static WithPayloadSelector include(List fields) {
+ return WithPayloadSelector.newBuilder()
+ .setInclude(PayloadIncludeSelector.newBuilder().addAllFields(fields).build())
+ .build();
+ }
+
+ /**
+ * Which payload fields to exclude in response.
+ * @param fields the list of fields to exclude.
+ * @return a new instance of {@link WithPayloadSelector}
+ */
+ public static WithPayloadSelector exclude(List fields) {
+ return WithPayloadSelector.newBuilder()
+ .setExclude(PayloadExcludeSelector.newBuilder().addAllFields(fields).build())
+ .build();
+ }
+}
diff --git a/src/main/java/io/qdrant/client/WithVectorsSelectorFactory.java b/src/main/java/io/qdrant/client/WithVectorsSelectorFactory.java
new file mode 100644
index 00000000..60cb69ad
--- /dev/null
+++ b/src/main/java/io/qdrant/client/WithVectorsSelectorFactory.java
@@ -0,0 +1,35 @@
+package io.qdrant.client;
+
+import io.qdrant.client.grpc.Points;
+
+import java.util.List;
+
+import static io.qdrant.client.grpc.Points.WithVectorsSelector;
+
+/**
+ * Convenience methods for constructing {@link WithVectorsSelector}
+ */
+public final class WithVectorsSelectorFactory {
+ private WithVectorsSelectorFactory() {
+ }
+
+ /**
+ * Whether to include vectors in response.
+ * @param enable if true, to include vectors, if false, none.
+ * @return a new instance of {@link WithVectorsSelector}
+ */
+ public static WithVectorsSelector enable(boolean enable) {
+ return WithVectorsSelector.newBuilder().setEnable(enable).build();
+ }
+
+ /**
+ * List of named vectors to include in response.
+ * @param names The names of vectors.
+ * @return a new instance of {@link WithVectorsSelector}
+ */
+ public static WithVectorsSelector include(List names) {
+ return WithVectorsSelector.newBuilder()
+ .setInclude(Points.VectorsSelector.newBuilder().addAllNames(names))
+ .build();
+ }
+}
diff --git a/src/main/java/io/qdrant/client/package-info.java b/src/main/java/io/qdrant/client/package-info.java
new file mode 100644
index 00000000..05885bd9
--- /dev/null
+++ b/src/main/java/io/qdrant/client/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * package
+ */
+@ParametersAreNonnullByDefault
+package io.qdrant.client;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
diff --git a/src/main/java/io/qdrant/client/utils/FilterUtil.java b/src/main/java/io/qdrant/client/utils/FilterUtil.java
deleted file mode 100644
index 8e452f3e..00000000
--- a/src/main/java/io/qdrant/client/utils/FilterUtil.java
+++ /dev/null
@@ -1,375 +0,0 @@
-package io.qdrant.client.utils;
-
-import io.qdrant.client.grpc.Points.Condition;
-import io.qdrant.client.grpc.Points.FieldCondition;
-import io.qdrant.client.grpc.Points.Filter;
-import io.qdrant.client.grpc.Points.GeoBoundingBox;
-import io.qdrant.client.grpc.Points.GeoLineString;
-import io.qdrant.client.grpc.Points.GeoPoint;
-import io.qdrant.client.grpc.Points.GeoPolygon;
-import io.qdrant.client.grpc.Points.GeoRadius;
-import io.qdrant.client.grpc.Points.HasIdCondition;
-import io.qdrant.client.grpc.Points.IsEmptyCondition;
-import io.qdrant.client.grpc.Points.IsNullCondition;
-import io.qdrant.client.grpc.Points.Match;
-import io.qdrant.client.grpc.Points.NestedCondition;
-import io.qdrant.client.grpc.Points.PointId;
-import io.qdrant.client.grpc.Points.Range;
-import io.qdrant.client.grpc.Points.RepeatedIntegers;
-import io.qdrant.client.grpc.Points.RepeatedStrings;
-import io.qdrant.client.grpc.Points.ValuesCount;
-import java.util.Arrays;
-import java.util.List;
-
-/** Utility class for creating filter conditions and filters. */
-public class FilterUtil {
-
- /**
- * Creates a {@link Condition} with a {@link FieldCondition} for text matching.
- *
- * @param key The key of the field.
- * @param match The text match criteria.
- * @return The created condition.
- */
- public static Condition fieldCondition(String key, Match match) {
- FieldCondition fieldCondition = FieldCondition.newBuilder().setKey(key).setMatch(match).build();
-
- return Condition.newBuilder().setField(fieldCondition).build();
- }
-
- /**
- * Creates a {@link Condition} with a {@link FieldCondition} for range matching.
- *
- * @param key The key of the field.
- * @param range The range criteria.
- * @return The created condition.
- */
- public static Condition fieldCondition(String key, Range range) {
- FieldCondition fieldCondition = FieldCondition.newBuilder().setKey(key).setRange(range).build();
-
- return Condition.newBuilder().setField(fieldCondition).build();
- }
-
- /**
- * Creates a {@link Condition} with a {@link FieldCondition} for geo bounding box matching.
- *
- * @param key The key of the field.
- * @param geoBoundingBox The geo bounding box criteria.
- * @return The created condition.
- */
- public static Condition fieldCondition(String key, GeoBoundingBox geoBoundingBox) {
- FieldCondition fieldCondition =
- FieldCondition.newBuilder().setKey(key).setGeoBoundingBox(geoBoundingBox).build();
-
- return Condition.newBuilder().setField(fieldCondition).build();
- }
-
- /**
- * Creates a {@link Condition} with a {@link FieldCondition} for geo radius matching.
- *
- * @param key The key of the field.
- * @param geoRadius The geo radius criteria.
- * @return The created condition.
- */
- public static Condition fieldCondition(String key, GeoRadius geoRadius) {
- FieldCondition fieldCondition =
- FieldCondition.newBuilder().setKey(key).setGeoRadius(geoRadius).build();
-
- return Condition.newBuilder().setField(fieldCondition).build();
- }
-
- /**
- * Creates a {@link Condition} with a {@link FieldCondition} for values count matching.
- *
- * @param key The key of the field.
- * @param valuesCount The values count criteria.
- * @return The created condition.
- */
- public static Condition fieldCondition(String key, ValuesCount valuesCount) {
- FieldCondition fieldCondition =
- FieldCondition.newBuilder().setKey(key).setValuesCount(valuesCount).build();
-
- return Condition.newBuilder().setField(fieldCondition).build();
- }
-
- /**
- * Creates a {@link Condition} with a {@link FieldCondition} for geo polygon matching.
- *
- * @param key The key of the field.
- * @param geoPolygon The geo polygon criteria.
- * @return The created condition.
- */
- public static Condition fieldCondition(String key, GeoPolygon geoPolygon) {
- FieldCondition fieldCondition =
- FieldCondition.newBuilder().setKey(key).setGeoPolygon(geoPolygon).build();
-
- return Condition.newBuilder().setField(fieldCondition).build();
- }
-
- /**
- * Creates a {@link Match} condition for string matching. If the text contains a space, it is
- * considered a full-text match. Else it is considered a keyword match.
- *
- * @param text The text to match.
- * @return The created match condition.
- */
- public static Match match(String text) {
- return text.contains(" ")
- ? Match.newBuilder().setText(text).build()
- : Match.newBuilder().setKeyword(text).build();
- }
-
- /**
- * Creates a {@link Match} condition for integer matching.
- *
- * @param value The integer value to match.
- * @return The created match condition.
- */
- public static Match match(long value) {
- return Match.newBuilder().setInteger(value).build();
- }
-
- /**
- * Creates a {@link Match} condition for boolean matching.
- *
- * @param value The boolean value to match.
- * @return The created match condition.
- */
- public static Match match(boolean value) {
- return Match.newBuilder().setBoolean(value).build();
- }
-
- /**
- * Creates a {@link Match} condition with a list of keywords for matching.
- *
- * @param keywords The list of keywords to match.
- * @return The created match condition.
- */
- public static Match matchWithKeywords(List keywords) {
- return Match.newBuilder()
- .setKeywords(RepeatedStrings.newBuilder().addAllStrings(keywords).build())
- .build();
- }
-
- /**
- * Creates a {@link Match} condition with a list of integers for matching.
- *
- * @param integers The list of integers to match.
- * @return The created match condition.
- */
- public static Match matchWithIntegers(List integers) {
- return Match.newBuilder()
- .setIntegers(RepeatedIntegers.newBuilder().addAllIntegers(integers).build())
- .build();
- }
-
- /**
- * Creates a {@link GeoBoundingBox} based on the top-left and bottom-right {@link GeoPoint}s.
- *
- * @param topLeft The top-left point of the bounding box.
- * @param bottomRight The bottom-right point of the bounding box.
- * @return The created geo bounding box.
- */
- public static GeoBoundingBox geoBoundingBox(GeoPoint topLeft, GeoPoint bottomRight) {
- return GeoBoundingBox.newBuilder().setTopLeft(topLeft).setBottomRight(bottomRight).build();
- }
-
- /**
- * Creates a {@link GeoRadius} based on the center {@link GeoPoint} and radius.
- *
- * @param center The center point of the radius.
- * @param radius The radius value.
- * @return The created geo radius.
- */
- public static GeoRadius geoRadius(GeoPoint center, float radius) {
- return GeoRadius.newBuilder().setCenter(center).setRadius(radius).build();
- }
-
- /**
- * Creates a {@link GeoPolygon} based on the exterior {@link GeoLineString} and a list of interior
- * {@link GeoLineString}s.
- *
- * @param exterior The exterior line string.
- * @param interiors The list of interior line strings.
- * @return The created geo polygon.
- */
- public static GeoPolygon geoPolygon(GeoLineString exterior, List interiors) {
- return GeoPolygon.newBuilder().setExterior(exterior).addAllInteriors(interiors).build();
- }
-
- /**
- * Creates a {@link Range} for numeric range matching.
- *
- * @param lt The less than value.
- * @param gt The greater than value.
- * @param gte The greater than or equal to value.
- * @param lte The less than or equal to value.
- * @return The created range.
- */
- public static Range range(double lt, double gt, double gte, double lte) {
- return Range.newBuilder().setLt(lt).setGt(gt).setGte(gte).setLte(lte).build();
- }
-
- /**
- * Creates a {@link ValuesCount} for values count matching.
- *
- * @param lt The less than value.
- * @param gt The greater than value.
- * @param gte The greater than or equal to value.
- * @param lte The less than or equal to value.
- * @return The created values count.
- */
- public static ValuesCount valuesCount(long lt, long gt, long gte, long lte) {
- return ValuesCount.newBuilder().setLt(lt).setGt(gt).setGte(gte).setLte(lte).build();
- }
-
- /**
- * Creates a {@link Condition} with a {@link Filter} as a subcondition.
- *
- * @param filter The filter criteria.
- * @return The created condition.
- */
- public static Condition filterCondition(Filter filter) {
- return Condition.newBuilder().setFilter(filter).build();
- }
-
- /**
- * Creates a {@link Condition} with a {@link NestedCondition} based on a key and a nested filter.
- *
- * @param key The key of the nested condition.
- * @param filter The nested filter criteria.
- * @return The created condition.
- */
- public static Condition nestedCondition(String key, Filter filter) {
- validateNestedFilter(filter);
-
- NestedCondition nestedCondition =
- NestedCondition.newBuilder().setKey(key).setFilter(filter).build();
-
- return Condition.newBuilder().setNested(nestedCondition).build();
- }
-
- private static void validateNestedFilter(Filter filter) {
- validateNoHasId(filter.getMustList());
- validateNoHasId(filter.getMustNotList());
- validateNoHasId(filter.getShouldList());
- }
-
- private static void validateNoHasId(List conditions) {
- for (Condition condition : conditions) {
- if (condition.hasHasId()) {
- throw new IllegalArgumentException("Nested filter cannot have a HasIdCondition");
- }
- }
- }
-
- /**
- * Creates a {@link Condition} with an {@link IsEmptyCondition} based on a key for empty condition
- * matching.
- *
- * @param key The key of the field.
- * @return The created condition.
- */
- public static Condition isEmptyCondition(String key) {
- IsEmptyCondition isEmptyCondition = IsEmptyCondition.newBuilder().setKey(key).build();
-
- return Condition.newBuilder().setIsEmpty(isEmptyCondition).build();
- }
-
- /**
- * Creates a {@link Condition} with an {@link IsNullCondition} based on a key for null condition
- * matching.
- *
- * @param key The key of the field.
- * @return The created condition.
- */
- public static Condition isNullCondition(String key) {
- IsNullCondition isNullCondition = IsNullCondition.newBuilder().setKey(key).build();
-
- return Condition.newBuilder().setIsNull(isNullCondition).build();
- }
-
- /**
- * Creates a {@link Condition} with a {@link HasIdCondition} based on a list of point IDs for ID
- * condition matching.
- *
- * @param pointIds The list of point IDs.
- * @return The created condition.
- */
- public static Condition hasIdCondition(List pointIds) {
- HasIdCondition hasIdCondition = HasIdCondition.newBuilder().addAllHasId(pointIds).build();
-
- return Condition.newBuilder().setHasId(hasIdCondition).build();
- }
-
- /**
- * Creates a {@link GeoPoint} based on latitude and longitude values.
- *
- * @param latitude The latitude value.
- * @param longitude The longitude value.
- * @return The created geo point.
- */
- public static GeoPoint geoPoint(double latitude, double longitude) {
- return GeoPoint.newBuilder().setLat(latitude).setLon(longitude).build();
- }
-
- /**
- * Creates a {@link Filter} with "must" conditions.
- *
- * @param mustConditions The list of "must" conditions.
- * @return The created filter.
- */
- public static Filter must(Condition... mustConditions) {
- return Filter.newBuilder().addAllMust(Arrays.asList(mustConditions)).build();
- }
-
- /**
- * Creates a {@link Filter} with "must not" conditions.
- *
- * @param mustNotConditions The list of "must not" conditions.
- * @return The created filter.
- */
- public static Filter mustNot(Condition... mustNotConditions) {
- return Filter.newBuilder().addAllMustNot(Arrays.asList(mustNotConditions)).build();
- }
-
- /**
- * Creates a {@link Filter} with "should" conditions.
- *
- * @param shouldConditions The list of "should" conditions.
- * @return The created filter.
- */
- public static Filter should(Condition... shouldConditions) {
- return Filter.newBuilder().addAllShould(Arrays.asList(shouldConditions)).build();
- }
-
- /**
- * Creates a {@link Filter} with "must" conditions.
- *
- * @param mustConditions The list of "must" conditions.
- * @return The created filter.
- */
- public static Filter must(List mustConditions) {
- return Filter.newBuilder().addAllMust(mustConditions).build();
- }
-
- /**
- * Creates a {@link Filter} with "must not" conditions.
- *
- * @param mustNotConditions The list of "must not" conditions.
- * @return The created filter.
- */
- public static Filter mustNot(List mustNotConditions) {
- return Filter.newBuilder().addAllMustNot(mustNotConditions).build();
- }
-
- /**
- * Creates a {@link Filter} with "should" conditions.
- *
- * @param shouldConditions The list of "should" conditions.
- * @return The created filter.
- */
- public static Filter should(List shouldConditions) {
- return Filter.newBuilder().addAllShould(shouldConditions).build();
- }
-}
diff --git a/src/main/java/io/qdrant/client/utils/PayloadUtil.java b/src/main/java/io/qdrant/client/utils/PayloadUtil.java
deleted file mode 100644
index 01fa6938..00000000
--- a/src/main/java/io/qdrant/client/utils/PayloadUtil.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package io.qdrant.client.utils;
-
-import io.qdrant.client.grpc.JsonWithInt.ListValue;
-import io.qdrant.client.grpc.JsonWithInt.NullValue;
-import io.qdrant.client.grpc.JsonWithInt.Struct;
-import io.qdrant.client.grpc.JsonWithInt.Value;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/** Utility class for working with Payloads. */
-public class PayloadUtil {
-
- /**
- * Converts a map to a payload struct.
- *
- * @param inputMap The input map to convert.
- * @return The converted payload struct.
- */
- public static Struct toPayloadStruct(Map inputMap) {
- Struct.Builder structBuilder = Struct.newBuilder();
- Map map = toPayload(inputMap);
- structBuilder.putAllFields(map);
- return structBuilder.build();
- }
-
- /**
- * Converts a map to a payload map.
- *
- * @param inputMap The input map to convert.
- * @return The converted payload map.
- */
- public static Map toPayload(Map inputMap) {
- Map map = new HashMap<>();
- for (Map.Entry entry : inputMap.entrySet()) {
- String fieldName = entry.getKey();
- Object value = entry.getValue();
-
- Value.Builder valueBuilder = Value.newBuilder();
- setValue(valueBuilder, value);
- map.put(fieldName, valueBuilder.build());
- }
- return map;
- }
-
- /**
- * Converts a payload struct to a Java Map.
- *
- * @param struct The payload struct to convert.
- * @return The converted hash map.
- */
- public static Map toMap(Struct struct) {
- Map