docs: Java client usage (#489)

* docs: java usage quick-start

* docs: collections java usage

* docs: payload java usage

* docs: points java-usage

* docs: search java-usage

* docs: explore java usage

* docs: filtering java usage

* docs: storage java-usage

* docs: indexing java usage

* docs: snapshots java-usage

* docs: list Java sdk
This commit is contained in:
Anush
2024-01-02 16:32:33 +05:30
committed by GitHub
parent 4e9b0d9555
commit e29c2f54ac
14 changed files with 1756 additions and 6 deletions
@@ -21,7 +21,7 @@ However, we recommend rotating the keys from time to time. To create additional
## Authenticate via SDK
Now that you have created your first cluster and key, you might want to access Qdrant Cloud from within your application.
Our official Qdrant clients for Python, TypeScript, Go, Rust, and .NET all support the API key parameter.
Our official Qdrant clients for Python, TypeScript, Go, Rust, .NET and Java all support the API key parameter.
```bash
curl \
@@ -70,3 +70,17 @@ let client = QdrantClient::from_url("xyz-example.eu-central.aws.cloud.qdrant.io:
.build()
.unwrap();
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(
QdrantGrpcClient.newBuilder(
"xyz-example.eu-central.aws.cloud.qdrant.io",
6334,
true)
.withApiKey("<paste-your-api-key-here>")
.build());
```
@@ -88,6 +88,19 @@ client
.await?;
```
```java
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client = new QdrantClient(
QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.createCollectionAsync("test_collection",
VectorParams.newBuilder().setDistance(Distance.Dot).setSize(4).build()).get();
```
In addition to the required options, you can also specify custom values for the following collection options:
* `hnsw_config` - see [indexing](../indexing/#vector-index) for details.
@@ -180,6 +193,33 @@ client
.await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorsConfig;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build())
client
.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setVectorsConfig(
VectorsConfig.newBuilder()
.setParams(
VectorParams.newBuilder()
.setSize(100)
.setDistance(Distance.Cosine)
.build()))
.setInitFromCollection("{from_collection_name}")
.build())
.get();
```
### Collection with multiple vectors
*Available as of v0.10.0*
@@ -275,6 +315,27 @@ client
.await?;
```
```java
import java.util.Map;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.VectorParams;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createCollectionAsync(
"{collection_name}",
Map.of(
"image", VectorParams.newBuilder().setSize(4).setDistance(Distance.Dot).build(),
"text",
VectorParams.newBuilder().setSize(8).setDistance(Distance.Cosine).build()))
.get();
```
For rare use cases, it is possible to create a collection without any vector storage.
*Available as of v1.1.1*
@@ -371,6 +432,27 @@ client
.await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.SparseVectorConfig;
import io.qdrant.client.grpc.Collections.SparseVectorParams;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setSparseVectorsConfig(
SparseVectorConfig.newBuilder()
.putMap("text", SparseVectorParams.getDefaultInstance()))
.build())
.get();
```
Outside of a unique name, there are no required configuration parameters for sparse vectors.
The distance function for sparse vectors is always `Dot` and does not need to be specified.
@@ -395,6 +477,16 @@ client.deleteCollection("{collection_name}");
client.delete_collection("{collection_name}").await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.deleteCollectionAsync("{collection_name}").get();
```
### Update collection parameters
Dynamic parameter updates may be helpful, for example, for more efficient initial loading of vectors.
@@ -446,6 +538,18 @@ client
.await?;
```
```java
import io.qdrant.client.grpc.Collections.OptimizersConfigDiff;
import io.qdrant.client.grpc.Collections.UpdateCollection;
client.updateCollectionAsync(
UpdateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setOptimizersConfig(
OptimizersConfigDiff.newBuilder().setIndexingThreshold(10000).build())
.build());
```
The following parameters can be updated:
* `optimizers_config` - see [optimizer](../optimizer/) for details.
@@ -640,6 +744,46 @@ client
.await?;
```
```java
import io.qdrant.client.grpc.Collections.HnswConfigDiff;
import io.qdrant.client.grpc.Collections.QuantizationConfigDiff;
import io.qdrant.client.grpc.Collections.QuantizationType;
import io.qdrant.client.grpc.Collections.ScalarQuantization;
import io.qdrant.client.grpc.Collections.UpdateCollection;
import io.qdrant.client.grpc.Collections.VectorParamsDiff;
import io.qdrant.client.grpc.Collections.VectorParamsDiffMap;
import io.qdrant.client.grpc.Collections.VectorsConfigDiff;
client
.updateCollectionAsync(
UpdateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setHnswConfig(HnswConfigDiff.newBuilder().setEfConstruct(123).build())
.setVectorsConfig(
VectorsConfigDiff.newBuilder()
.setParamsMap(
VectorParamsDiffMap.newBuilder()
.putMap(
"my_vector",
VectorParamsDiff.newBuilder()
.setHnswConfig(
HnswConfigDiff.newBuilder()
.setM(3)
.setEfConstruct(123)
.build())
.build())))
.setQuantizationConfig(
QuantizationConfigDiff.newBuilder()
.setScalar(
ScalarQuantization.newBuilder()
.setType(QuantizationType.Int8)
.setQuantile(0.8f)
.setAlwaysRam(true)
.build()))
.build())
.get();
```
## Collection info
Qdrant allows determining the configuration parameters of an existing collection to better understand how the points are
@@ -706,6 +850,10 @@ client.getCollection("{collection_name}");
client.collection_info("{collection_name}").await?;
```
```java
client.getCollectionInfoAsync("{collection_name}").get();
```
If you insert the vectors into the collection, the `status` field may become
`yellow` whilst it is optimizing. It will become `green` once all the points are
successfully processed.
@@ -811,6 +959,10 @@ client.updateCollectionAliases({
client.create_alias("example_collection", "production_collection").await?;
```
```java
client.createAliasAsync("production_collection", "example_collection").get();
```
### Remove alias
```http
@@ -852,6 +1004,10 @@ client.updateCollectionAliases({
client.delete_alias("production_collection").await?;
```
```java
client.deleteAliasAsync("production_collection").get();
```
### Switch collection
Multiple alias actions are performed atomically.
@@ -914,6 +1070,11 @@ client.delete_alias("production_collection").await?;
client.create_alias("example_collection", "production_collection").await?;
```
```java
client.deleteAliasAsync("production_collection").get();
client.createAliasAsync("production_collection", "example_collection").get();
```
### List collection aliases
```http
@@ -944,6 +1105,16 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client.list_collection_aliases("{collection_name}").await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.listCollectionAliasesAsync("{collection_name}").get();
```
### List all aliases
```http
@@ -974,6 +1145,16 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client.list_aliases().await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.listAliasesAsync().get();
```
### List all collections
```http
@@ -1003,3 +1184,13 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client.list_collections().await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.listCollectionsAsync().get();
```
@@ -109,6 +109,37 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.VectorFactory.vector;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.RecommendPoints;
import io.qdrant.client.grpc.Points.RecommendStrategy;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.recommendAsync(
RecommendPoints.newBuilder()
.setCollectionName("{collection_name}")
.addAllPositive(List.of(id(100), id(200)))
.addAllPositiveVectors(List.of(vector(100.0f, 231.0f)))
.addAllNegative(List.of(id(718)))
.addAllPositiveVectors(List.of(vector(0.2f, 0.3f, 0.4f, 0.5f)))
.setStrategy(RecommendStrategy.AverageVector)
.setFilter(Filter.newBuilder().addMust(matchKeyword("city", "London")))
.setLimit(3)
.build())
.get();
```
Example result of this API would be
```json
@@ -222,6 +253,25 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
import io.qdrant.client.grpc.Points.RecommendPoints;
client
.recommendAsync(
RecommendPoints.newBuilder()
.setCollectionName("{collection_name}")
.addAllPositive(List.of(id(100), id(231)))
.addAllNegative(List.of(id(718)))
.setUsing("image")
.setLimit(10)
.build())
.get();
```
Parameter `using` specifies which stored vectors to use for the recommendation.
### Lookup vectors from another collection
@@ -277,6 +327,31 @@ client.recommend("{collection_name}", {
});
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
import io.qdrant.client.grpc.Points.LookupLocation;
import io.qdrant.client.grpc.Points.RecommendPoints;
client
.recommendAsync(
RecommendPoints.newBuilder()
.setCollectionName("{collection_name}")
.addAllPositive(List.of(id(100), id(231)))
.addAllNegative(List.of(id(718)))
.setUsing("image")
.setLimit(10)
.setLookupFrom(
LookupLocation.newBuilder()
.setCollectionName("{external_collection_name}")
.setVectorName("{external_vector_name}")
.build())
.build())
.get();
```
Vectors are retrieved from the external collection by ids provided in the `positive` and `negative` lists.
These vectors then used to perform the recommendation in the current collection, comparing against the "using" or default vector.
@@ -426,6 +501,40 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import static io.qdrant.client.PointIdFactory.id;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.RecommendPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
Filter filter = Filter.newBuilder().addMust(matchKeyword("city", "London")).build();
List<RecommendPoints> recommendQueries =
List.of(
RecommendPoints.newBuilder()
.addAllPositive(List.of(id(100), id(231)))
.addAllNegative(List.of(id(718)))
.setFilter(filter)
.setLimit(3)
.build(),
RecommendPoints.newBuilder()
.addAllPositive(List.of(id(200), id(67)))
.addAllNegative(List.of(id(300)))
.setFilter(filter)
.setLimit(3)
.build());
client.recommendBatchAsync("{collection_name}", recommendQueries, null).get();
```
The result of this API contains one array per recommendation requests.
```json
@@ -551,6 +660,47 @@ client.discover("{collection_name}", {
});
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.VectorFactory.vector;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.ContextExamplePair;
import io.qdrant.client.grpc.Points.DiscoverPoints;
import io.qdrant.client.grpc.Points.TargetVector;
import io.qdrant.client.grpc.Points.VectorExample;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.discoverAsync(
DiscoverPoints.newBuilder()
.setCollectionName("{collection_name}")
.setTarget(
TargetVector.newBuilder()
.setSingle(
VectorExample.newBuilder()
.setVector(vector(0.2f, 0.1f, 0.9f, 0.7f))
.build()))
.addAllContext(
List.of(
ContextExamplePair.newBuilder()
.setPositive(VectorExample.newBuilder().setId(id(100)))
.setNegative(VectorExample.newBuilder().setId(id(718)))
.build(),
ContextExamplePair.newBuilder()
.setPositive(VectorExample.newBuilder().setId(id(200)))
.setNegative(VectorExample.newBuilder().setId(id(300)))
.build()))
.setLimit(10)
.build())
.get();
```
<aside role="status">
Notes about discovery search:
@@ -639,6 +789,39 @@ client.discover("{collection_name}", {
});
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.ContextExamplePair;
import io.qdrant.client.grpc.Points.DiscoverPoints;
import io.qdrant.client.grpc.Points.VectorExample;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.discoverAsync(
DiscoverPoints.newBuilder()
.setCollectionName("{collection_name}")
.addAllContext(
List.of(
ContextExamplePair.newBuilder()
.setPositive(VectorExample.newBuilder().setId(id(100)))
.setNegative(VectorExample.newBuilder().setId(id(718)))
.build(),
ContextExamplePair.newBuilder()
.setPositive(VectorExample.newBuilder().setId(id(200)))
.setNegative(VectorExample.newBuilder().setId(id(300)))
.build()))
.setLimit(10)
.build())
.get();
```
<aside role="status">
Notes about context search:
@@ -105,7 +105,7 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client
.scroll(&ScrollPoints {
collection_name: "test_collection".to_string(),
collection_name: "{collection_name}".to_string(),
filter: Some(Filter::must([
Condition::matches("city", "london".to_string()),
Condition::matches("color", "red".to_string()),
@@ -115,6 +115,32 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addAllMust(
List.of(matchKeyword("city", "London"), matchKeyword("color", "Red")))
.build())
.build())
.get();
```
Filtered points would be:
```json
@@ -180,7 +206,7 @@ use qdrant_client::qdrant::{Condition, Filter, ScrollPoints};
client
.scroll(&ScrollPoints {
collection_name: "test_collection".to_string(),
collection_name: "{collection_name}".to_string(),
filter: Some(Filter::should([
Condition::matches("city", "london".to_string()),
Condition::matches("color", "red".to_string()),
@@ -190,6 +216,26 @@ client
.await?;
```
```java
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
import java.util.List;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addAllShould(
List.of(matchKeyword("city", "London"), matchKeyword("color", "Red")))
.build())
.build())
.get();
```
Filtered points would be:
```json
@@ -254,7 +300,7 @@ use qdrant_client::qdrant::{Condition, Filter, ScrollPoints};
client
.scroll(&ScrollPoints {
collection_name: "test_collection".to_string(),
collection_name: "{collection_name}".to_string(),
filter: Some(Filter::must_not([
Condition::matches("city", "london".to_string()),
Condition::matches("color", "red".to_string()),
@@ -264,6 +310,27 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addAllMustNot(
List.of(matchKeyword("city", "London"), matchKeyword("color", "Red")))
.build())
.build())
.get();
```
Filtered points would be:
```json
@@ -343,6 +410,25 @@ client
.await?;
```
```java
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addMust(matchKeyword("city", "London"))
.addMustNot(matchKeyword("color", "Red"))
.build())
.build())
.get();
```
Filtered points would be:
```json
@@ -429,6 +515,34 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.filter;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addMustNot(
filter(
Filter.newBuilder()
.addAllMust(
List.of(
matchKeyword("city", "London"),
matchKeyword("color", "Red")))
.build()))
.build())
.build())
.get();
```
Filtered points would be:
```json
@@ -475,6 +589,10 @@ models.FieldCondition(
Condition::matches("color", "red".to_string())
```
```java
matchKeyword("color", "Red");
```
For the other types, the match condition will look exactly the same, except for the type used:
```json
@@ -504,6 +622,12 @@ models.FieldCondition(
Condition::matches("count", 0)
```
```java
import static io.qdrant.client.ConditionFactory.match;
match("count", 0);
```
The simplest kind of condition is one that checks if the stored value equals the given one.
If several values are stored, at least one of them should match the condition.
You can apply it to [keyword](../payload/#keyword), [integer](../payload/#integer) and [bool](../payload/#bool) payloads.
@@ -546,6 +670,12 @@ FieldCondition(
Condition::matches("color", vec!["black".to_string(), "yellow".to_string()])
```
```java
import static io.qdrant.client.ConditionFactory.matchKeywords;
matchKeywords("color", List.of("black", "yellow"))
```
In this example, the condition will be satisfied if the stored value is either `black` or `yellow`.
If the stored value is an array, it should have at least one value matching any of the given values. E.g. if the stored value is `["black", "green"]`, the condition will be satisfied, because `"black"` is in `["black", "yellow"]`.
@@ -593,6 +723,12 @@ Condition::matches(
)
```
```java
import static io.qdrant.client.ConditionFactory.matchExceptKeywords;
matchExceptKeywords("color", List.of("black", "yellow"));
```
In this example, the condition will be satisfied if the stored value is neither `black` nor `yellow`.
If the stored value is an array, it should have at least one value not matching any of the given values. E.g. if the stored value is `["black", "green"]`, the condition will be satisfied, because `"green"` does not match `"black"` nor `"yellow"`.
@@ -707,6 +843,24 @@ client
.await?;
```
```java
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addShould(matchKeyword("country.name", "Germany"))
.build())
.build())
.get();
```
You can also search through arrays by projecting inner values using the `[]` syntax.
```http
@@ -780,6 +934,28 @@ client
.await?;
```
```java
import static io.qdrant.client.ConditionFactory.range;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.Range;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addShould(
range(
"country.cities[].population",
Range.newBuilder().setGte(9.0).build()))
.build())
.build())
.get();
```
This query would only output the point with id 2 as only Japan has a city with population greater than 9.0.
And the leaf nested field can also be an array.
@@ -842,6 +1018,24 @@ client
.await?;
```
```java
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addShould(matchKeyword("country.cities[].sightseeing", "Germany"))
.build())
.build())
.get();
```
This query would only output the point with id 2 as only Japan has a city with the "Osaka castke" as part of the sightseeing.
### Nested object filter
@@ -935,7 +1129,7 @@ use qdrant_client::qdrant::{Condition, Filter, ScrollPoints};
client
.scroll(&ScrollPoints {
collection_name: "test_collection".to_string(),
collection_name: "{collection_name}".to_string(),
filter: Some(Filter::must([
Condition::matches("diet[].food", "meat".to_string()),
Condition::matches("diet[].likes", true),
@@ -945,6 +1139,33 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.match;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addAllMust(
List.of(matchKeyword("diet[].food", "meat"), match("diet[].likes", true)))
.build())
.build())
.get();
```
This happens because both points are matching the two conditions:
- the "t-rex" matches food=meat on `diet[1].food` and likes=true on `diet[1].likes`
@@ -1059,6 +1280,35 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.match;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import static io.qdrant.client.ConditionFactory.nested;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addMust(
nested(
"diet",
Filter.newBuilder()
.addAllMust(
List.of(
matchKeyword("food", "meat"), match("likes", true)))
.build()))
.build())
.build())
.get();
```
The matching logic is modified to be applied at the level of an array element within the payload.
Nested filters work in the same way as if the nested filter was applied to a single element of the array at a time.
@@ -1177,6 +1427,38 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.hasId;
import static io.qdrant.client.ConditionFactory.match;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import static io.qdrant.client.ConditionFactory.nested;
import static io.qdrant.client.PointIdFactory.id;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addMust(
nested(
"diet",
Filter.newBuilder()
.addAllMust(
List.of(
matchKeyword("food", "meat"), match("likes", true)))
.build()))
.addMust(hasId(id(1)))
.build())
.build())
.get();
```
### Full Text Match
*Available as of v0.10.0*
@@ -1218,6 +1500,12 @@ models.FieldCondition(
Condition::matches("description", "good cheap".to_string())
```
```java
import static io.qdrant.client.ConditionFactory.matchText;
matchText("description", "good cheap");
```
If the query has several words, then the condition will be satisfied only if all of them are present in the text.
### Range
@@ -1270,6 +1558,14 @@ Condition::range(
)
```
```java
import static io.qdrant.client.ConditionFactory.range;
import io.qdrant.client.grpc.Points.Range;
range("price", Range.newBuilder().setGte(100.0).setLte(450).build());
```
The `range` condition sets the range of possible values for stored payload values.
If several values are stored, at least one of them should match the condition.
@@ -1350,6 +1646,12 @@ Condition::geo_bounding_box(
)
```
```java
import static io.qdrant.client.ConditionFactory.geoBoundingBox;
geoBoundingBox("location", 52.520711, 13.403683, 52.495862, 13.455868);
```
It matches with `location`s inside a rectangle with the coordinates of the upper left corner in `bottom_right` and the coordinates of the lower right corner in `top_left`.
#### Geo Radius
@@ -1406,6 +1708,12 @@ Condition::geo_radius(
)
```
```java
import static io.qdrant.client.ConditionFactory.geoRadius;
geoRadius("location", 52.520711, 13.403683, 1000.0f);
```
It matches with `location`s inside a circle with the `center` at the center and a radius of `radius` meters.
If several values are stored, at least one of them should match the condition.
@@ -1614,6 +1922,35 @@ Condition::geo_polygon(
)
```
```java
import static io.qdrant.client.ConditionFactory.geoPolygon;
import io.qdrant.client.grpc.Points.GeoLineString;
import io.qdrant.client.grpc.Points.GeoPoint;
geoPolygon(
"location",
GeoLineString.newBuilder()
.addAllPoints(
List.of(
GeoPoint.newBuilder().setLon(-70.0).setLat(-70.0).build(),
GeoPoint.newBuilder().setLon(60.0).setLat(-70.0).build(),
GeoPoint.newBuilder().setLon(60.0).setLat(60.0).build(),
GeoPoint.newBuilder().setLon(-70.0).setLat(60.0).build(),
GeoPoint.newBuilder().setLon(-70.0).setLat(-70.0).build()))
.build(),
List.of(
GeoLineString.newBuilder()
.addAllPoints(
List.of(
GeoPoint.newBuilder().setLon(-65.0).setLat(-65.0).build(),
GeoPoint.newBuilder().setLon(0.0).setLat(-65.0).build(),
GeoPoint.newBuilder().setLon(0.0).setLat(0.0).build(),
GeoPoint.newBuilder().setLon(-65.0).setLat(0.0).build(),
GeoPoint.newBuilder().setLon(-65.0).setLat(-65.0).build()))
.build()));
```
A match is considered any point location inside or on the boundaries of the given polygon's exterior but not inside any interiors.
If several location values are stored for a point, then any of them matching will include that point as a candidate in the resultset.
@@ -1667,6 +2004,14 @@ Condition::values_count(
)
```
```java
import static io.qdrant.client.ConditionFactory.valuesCount;
import io.qdrant.client.grpc.Points.ValuesCount;
valuesCount("comments", ValuesCount.newBuilder().setGt(2).build());
```
The result would be:
```json
@@ -1706,6 +2051,12 @@ models.IsEmptyCondition(
Condition::is_empty("reports")
```
```java
import static io.qdrant.client.ConditionFactory.isEmpty;
isEmpty("reports");
```
This condition will match all records where the field `reports` either does not exist, or has `null` or `[]` value.
<aside role="status">The <b>IsEmpty</b> is often useful together with the logical negation <b>must_not</b>. In this case all non-empty values will be selected.</aside>
@@ -1741,6 +2092,12 @@ models.IsNullCondition(
Condition::is_null("reports")
```
```java
import static io.qdrant.client.ConditionFactory.isNull;
isNull("reports");
```
This condition will match all records where the field `reports` exists and has `NULL` value.
@@ -1796,6 +2153,27 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.hasId;
import static io.qdrant.client.PointIdFactory.id;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(
Filter.newBuilder()
.addMust(hasId(List.of(id(1), id(3), id(5), id(7), id(9), id(11))))
.build())
.build())
.get();
```
Filtered points would be:
```json
@@ -72,6 +72,26 @@ client
.await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.PayloadSchemaType;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createPayloadIndexAsync(
"{collection_name}",
"name_of_the_field_to_index",
PayloadSchemaType.Keyword,
null,
null,
null,
null)
.get();
```
Available field types are:
* `keyword` - for [keyword](../payload/#keyword) payload, affects [Match](../filtering/#match) filtering conditions.
@@ -176,6 +196,37 @@ client
.await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.PayloadIndexParams;
import io.qdrant.client.grpc.Collections.PayloadSchemaType;
import io.qdrant.client.grpc.Collections.TextIndexParams;
import io.qdrant.client.grpc.Collections.TokenizerType;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createPayloadIndexAsync(
"{collection_name}",
"name_of_the_field_to_index",
PayloadSchemaType.Text,
PayloadIndexParams.newBuilder()
.setTextIndexParams(
TextIndexParams.newBuilder()
.setTokenizer(TokenizerType.Word)
.setMaxTokenLen(2)
.setMaxTokenLen(10)
.setLowercase(true)
.build())
.build(),
null,
null,
null)
.get();
```
Available tokenizers are:
* `word` - splits the string into words, separated by spaces, punctuation marks, and special characters.
@@ -276,6 +276,49 @@ client
.await?;
```
```java
import java.util.List;
import java.util.Map;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.ValueFactory.value;
import static io.qdrant.client.VectorsFactory.vectors;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.PointStruct;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.upsertAsync(
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(1))
.setVectors(vectors(0.05f, 0.61f, 0.76f, 0.74f))
.putAllPayload(Map.of("city", value("Berlin"), "price", value(1.99)))
.build(),
PointStruct.newBuilder()
.setId(id(2))
.setVectors(vectors(0.19f, 0.81f, 0.75f, 0.11f))
.putAllPayload(
Map.of("city", list(List.of(value("Berlin"), value("London")))))
.build(),
PointStruct.newBuilder()
.setId(id(3))
.setVectors(vectors(0.36f, 0.55f, 0.47f, 0.94f))
.putAllPayload(
Map.of(
"city",
list(List.of(value("Berlin"), value("London"))),
"price",
list(List.of(value(1.99), value(2.99)))))
.build()))
.get();
```
## Update payload
### Set payload
@@ -344,6 +387,24 @@ client
.await?;
```
```java
import java.util.List;
import java.util.Map;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.ValueFactory.value;
client
.setPayloadAsync(
"{collection_name}",
Map.of("property1", value("string"), "property2", value("string")),
List.of(id(0), id(3), id(10)),
true,
null,
null)
.get();
```
You don't need to know the ids of the points you want to modify. The alternative
is to use filters.
@@ -430,6 +491,23 @@ client
.await?;
```
```java
import java.util.Map;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import static io.qdrant.client.ValueFactory.value;
client
.setPayloadAsync(
"{collection_name}",
Map.of("property1", value("string"), "property2", value("string")),
Filter.newBuilder().addMust(matchKeyword("color", "red")).build(),
true,
null,
null)
.get();
```
### Overwrite payload
Fully replace any existing payload with the given one.
@@ -496,6 +574,23 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.ValueFactory.value;
client
.overwritePayloadAsync(
"{collection_name}",
Map.of("property1", value("string"), "property2", value("string")),
List.of(id(0), id(3), id(10)),
true,
null,
null)
.get();
```
Like [set payload](#set-payload), you don't need to know the ids of the points
you want to modify. The alternative is to use filters.
@@ -546,6 +641,16 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
client
.clearPayloadAsync("{collection_name}", List.of(id(0), id(3), id(100)), true, null, null)
.get();
```
<aside role="status">
You can also use <code>models.FilterSelector</code> to remove the points matching given filter criteria, instead of providing the ids.
</aside>
@@ -599,6 +704,22 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
client
.deletePayloadAsync(
"{collection_name}",
List.of("color", "price"),
List.of(id(0), id(3), id(100)),
true,
null,
null)
.get();
```
Alternatively, you can use filters to delete payload keys from the points.
```http
@@ -669,6 +790,22 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.matchKeyword;
client
.deletePayloadAsync(
"{collection_name}",
List.of("color", "price"),
Filter.newBuilder().addMust(matchKeyword("color", "red")).build(),
true,
null,
null)
.get();
```
## Payload indexing
To search more efficiently with filters, Qdrant allows you to create indexes for payload fields by specifying the name and type of field it is intended to be.
@@ -721,6 +858,19 @@ client
.await?;
```
```java
import io.qdrant.client.grpc.Collections.PayloadSchemaType;
client.createPayloadIndexAsync(
"{collection_name}",
"name_of_the_field_to_index",
PayloadSchemaType.Keyword,
null,
true,
null,
null);
```
The index usage flag is displayed in the payload schema with the [collection info API](https://qdrant.github.io/qdrant/redoc/index.html#operation/get_collection).
Payload schema example:
@@ -143,6 +143,34 @@ client
.await?;
```
```java
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.ValueFactory.value;
import static io.qdrant.client.VectorsFactory.vectors;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.PointStruct;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.upsertAsync(
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(UUID.fromString("5c56c793-69f3-4fbf-87e6-c4bf54c28c26")))
.setVectors(vectors(0.05f, 0.61f, 0.76f, 0.74f))
.putAllPayload(Map.of("color", value("Red")))
.build()))
.get();
```
and
```http
@@ -209,6 +237,33 @@ client
.await?;
```
```java
import java.util.List;
import java.util.Map;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.ValueFactory.value;
import static io.qdrant.client.VectorsFactory.vectors;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.PointStruct;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.upsertAsync(
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(1))
.setVectors(vectors(0.05f, 0.61f, 0.76f, 0.74f))
.putAllPayload(Map.of("color", value("Red")))
.build()))
.get();
```
are both possible.
## Upload points
@@ -517,6 +572,42 @@ client
.await?;
```
```java
import java.util.List;
import java.util.Map;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.VectorsFactory.namedVectors;
import io.qdrant.client.grpc.Points.PointStruct;
client
.upsertAsync(
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(1))
.setVectors(
namedVectors(
Map.of(
"image",
List.of(0.9f, 0.1f, 0.1f, 0.2f),
"text",
List.of(0.4f, 0.7f, 0.1f, 0.8f, 0.1f, 0.1f, 0.9f, 0.2f))))
.build(),
PointStruct.newBuilder()
.setId(id(2))
.setVectors(
namedVectors(
Map.of(
"image",
List.of(0.2f, 0.1f, 0.3f, 0.9f),
"text",
List.of(0.5f, 0.2f, 0.7f, 0.4f, 0.7f, 0.2f, 0.3f, 0.9f))))
.build()))
.get();
```
*Available as of v1.2.0*
Named vectors are optional. When uploading points, some vectors may be omitted.
@@ -681,6 +772,51 @@ client
.await?;
```
```java
import java.util.List;
import java.util.Map;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.VectorFactory.vector;
import io.qdrant.client.grpc.Points.NamedVectors;
import io.qdrant.client.grpc.Points.PointStruct;
import io.qdrant.client.grpc.Points.Vectors;
client
.upsertAsync(
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(1))
.setVectors(
Vectors.newBuilder()
.setVectors(
NamedVectors.newBuilder()
.putAllVectors(
Map.of(
"text", vector(List.of(1.0f, 2.0f), List.of(6, 7))))
.build())
.build())
.build(),
PointStruct.newBuilder()
.setId(id(2))
.setVectors(
Vectors.newBuilder()
.setVectors(
NamedVectors.newBuilder()
.putAllVectors(
Map.of(
"text",
vector(
List.of(0.1f, 0.2f, 0.3f, 0.4f, 0.5f),
List.of(1, 2, 3, 4, 5))))
.build())
.build())
.build()))
.get();
```
## Modify points
To change a point, you can modify its vectors or its payload. There are several
@@ -785,6 +921,32 @@ client
.await?;
```
```java
import java.util.List;
import java.util.Map;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.VectorsFactory.namedVectors;
client
.updateVectorsAsync(
"{collection_name}",
List.of(
PointVectors.newBuilder()
.setId(id(1))
.setVectors(namedVectors(Map.of("image", List.of(0.1f, 0.2f, 0.3f, 0.4f))))
.build(),
PointVectors.newBuilder()
.setId(id(2))
.setVectors(
namedVectors(
Map.of(
"text", List.of(0.9f, 0.8f, 0.7f, 0.6f, 0.5f, 0.4f, 0.3f, 0.2f))))
.build()))
.get();
```
To update points and replace all of its vectors, see [uploading
points](#upload-points).
@@ -844,6 +1006,17 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
client
.deleteVectorsAsync(
"{collection_name}", List.of("text", "image"), List.of(id(0), id(3), id(10)))
.get();
```
To delete entire points, see [deleting points](#delete-points).
### Update payload
@@ -895,6 +1068,14 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
client.deleteAsync("{collection_name}", List.of(id(0), id(3), id(100)));
```
Alternative way to specify which points to remove is to use filter.
```http
@@ -963,6 +1144,18 @@ client
.await?;
```
```java
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.grpc.Points.Filter;
client
.deleteAsync(
"{collection_name}",
Filter.newBuilder().addMust(matchKeyword("color", "red")).build())
.get();
```
This example removes all points with `{ "color": "red" }` from the collection.
## Retrieve points
@@ -1004,6 +1197,16 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
client
.retrieveAsync("{collection_name}", List.of(id(0), id(30), id(100)), false, false, null)
.get();
```
This method has additional parameters `with_vectors` and `with_payload`.
Using these parameters, you can select parts of the point you want as a result.
Excluding helps you not to waste traffic transmitting useless data.
@@ -1098,6 +1301,24 @@ client
.await?;
```
```java
import static io.qdrant.client.ConditionFactory.matchKeyword;
import static io.qdrant.client.WithPayloadSelectorFactory.enable;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
client
.scrollAsync(
ScrollPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(Filter.newBuilder().addMust(matchKeyword("color", "red")).build())
.setLimit(1)
.setWithPayload(enable(true))
.build())
.get();
```
Returns all point with `color` = `red`.
```json
@@ -1205,6 +1426,19 @@ client
.await?;
```
```java
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.grpc.Points.Filter;
client
.countAsync(
"{collection_name}",
Filter.newBuilder().addMust(matchKeyword("color", "red")).build(),
true)
.get();
```
Returns number of counts matching given filtering conditions:
```json
@@ -158,6 +158,32 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.SearchParams;
import io.qdrant.client.grpc.Points.SearchPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.searchAsync(
SearchPoints.newBuilder()
.setCollectionName("{collection_name}")
.setFilter(Filter.newBuilder().addMust(matchKeyword("city", "London")).build())
.setParams(SearchParams.newBuilder().setExact(false).setHnswEf(128).build())
.addAllVector(List.of(0.2f, 0.1f, 0.9f, 0.7f))
.setLimit(3)
.build())
.get();
```
In this example, we are looking for vectors similar to vector `[0.2, 0.1, 0.9, 0.7]`.
Parameter `limit` (or its alias - `top`) specifies the amount of most similar results we would like to retrieve.
@@ -249,6 +275,27 @@ client
.await?;
```
```java
import java.util.List;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.SearchPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.searchAsync(
SearchPoints.newBuilder()
.setCollectionName("{collection_name}")
.setVectorName("image")
.addAllVector(List.of(0.2f, 0.1f, 0.9f, 0.7f))
.setLimit(3)
.build())
.get();
```
Search is processing only among vectors with the same name.
*Available as of v1.7.0*
@@ -336,6 +383,29 @@ client
.await?;
```
```java
import java.util.List;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.SearchPoints;
import io.qdrant.client.grpc.Points.SparseIndices;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.searchAsync(
SearchPoints.newBuilder()
.setCollectionName("{collection_name}")
.setVectorName("text")
.addAllVector(List.of(2.0f, 1.0f))
.setSparseIndices(SparseIndices.newBuilder().addAllData(List.of(1, 7)).build())
.setLimit(3)
.build())
.get();
```
### Filtering results by score
In addition to payload filtering, it might be useful to filter out results with a low similarity score.
@@ -396,6 +466,31 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.WithPayloadSelectorFactory.enable;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.WithVectorsSelectorFactory;
import io.qdrant.client.grpc.Points.SearchPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.searchAsync(
SearchPoints.newBuilder()
.setCollectionName("{collection_name}")
.addAllVector(List.of(0.2f, 0.1f, 0.9f, 0.7f))
.setWithPayload(enable(true))
.setWithVectors(WithVectorsSelectorFactory.enable(true))
.setLimit(3)
.build())
.get();
```
You can use `with_payload` to scope to or filter a specific payload subset.
You can even specify an array of items to include, such as `city`,
`village`, and `town`:
@@ -448,6 +543,29 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.WithPayloadSelectorFactory.include;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.SearchPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.searchAsync(
SearchPoints.newBuilder()
.setCollectionName("{collection_name}")
.addAllVector(List.of(0.2f, 0.1f, 0.9f, 0.7f))
.setWithPayload(include(List.of("city", "village", "town")))
.setLimit(3)
.build())
.get();
```
Or use `include` or `exclude` explicitly. For example, to exclude `city`:
```http
@@ -514,6 +632,29 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.WithPayloadSelectorFactory.exclude;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.SearchPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.searchAsync(
SearchPoints.newBuilder()
.setCollectionName("{collection_name}")
.addAllVector(List.of(0.2f, 0.1f, 0.9f, 0.7f))
.setWithPayload(exclude(List.of("city")))
.setLimit(3)
.build())
.get();
```
It is possible to target nested fields using a dot notation:
- `payload.nested_field` - for a nested field
- `payload.nested_array[].sub_field` - for projecting nested fields within an array
@@ -667,6 +808,35 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.SearchPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
Filter filter = Filter.newBuilder().addMust(matchKeyword("city", "London")).build();
List<SearchPoints> searches =
List.of(
SearchPoints.newBuilder()
.addAllVector(List.of(0.2f, 0.1f, 0.9f, 0.7f))
.setFilter(filter)
.setLimit(3)
.build(),
SearchPoints.newBuilder()
.addAllVector(List.of(0.5f, 0.3f, 0.2f, 0.3f))
.setFilter(filter)
.setLimit(3)
.build());
client.searchBatchAsync("{collection_name}", searches, null).get();
```
The result of this API contains one array per search requests.
```json
@@ -754,6 +924,32 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.WithPayloadSelectorFactory.enable;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.WithVectorsSelectorFactory;
import io.qdrant.client.grpc.Points.SearchPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.searchAsync(
SearchPoints.newBuilder()
.setCollectionName("{collection_name}")
.addAllVector(List.of(0.2f, 0.1f, 0.9f, 0.7f))
.setWithPayload(enable(true))
.setWithVectors(WithVectorsSelectorFactory.enable(true))
.setLimit(10)
.setOffset(100)
.build())
.get();
```
Is equivalent to retrieving the 11th page with 10 records per page.
<aside role="alert">Large offset values may cause performance issues</aside>
@@ -883,6 +1079,23 @@ client
.await?;
```
```java
import java.util.List;
import io.qdrant.client.grpc.Points.SearchPointGroups;
client
.searchGroupsAsync(
SearchPointGroups.newBuilder()
.setCollectionName("{collection_name}")
.addAllVector(List.of(1.1f))
.setGroupBy("document_id")
.setLimit(4)
.setGroupSize(2)
.build())
.get();
```
The output of a ***groups*** call looks like this:
```json
@@ -1033,6 +1246,33 @@ client
.await?;
```
```java
import java.util.List;
import static io.qdrant.client.WithPayloadSelectorFactory.include;
import static io.qdrant.client.WithVectorsSelectorFactory.enable;
import io.qdrant.client.grpc.Points.SearchPointGroups;
import io.qdrant.client.grpc.Points.WithLookup;
client
.searchGroupsAsync(
SearchPointGroups.newBuilder()
.setCollectionName("{collection_name}")
.addAllVector(List.of(1.0f))
.setGroupBy("document_id")
.setLimit(2)
.setGroupSize(2)
.setWithLookup(
WithLookup.newBuilder()
.setCollection("documents")
.setWithPayload(include(List.of("title", "text")))
.setWithVectors(enable(false))
.build())
.build())
.get();
```
For the `with_lookup` parameter, you can also use the shorthand `with_lookup="documents"` to bring the whole payload and vector(s) without explicitly specifying it.
The looked up result will show up under `lookup` in each group.
@@ -67,6 +67,16 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client.create_snapshot("{collection_name}").await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.createSnapshotAsync("{collection_name}").get();
```
This is a synchronous operation for which a `tar` archive file will be generated into the `snapshot_path`.
### Delete snapshot
@@ -103,6 +113,16 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client.delete_snapshot("{collection_name}", "{snapshot_name}").await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.deleteSnapshotAsync("{collection_name}", "{snapshot_name}").get();
```
## List snapshot
List of snapshots for a collection:
@@ -135,6 +155,16 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client.list_snapshots("{collection_name}").await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.listSnapshotAsync("{collection_name}").get();
```
## Retrieve snapshot
<aside role="status">Only available through the REST API for the time being.</aside>
@@ -324,6 +354,16 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client.create_full_snapshot().await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.createFullSnapshotAsync().get();
```
### Delete full storage snapshot
*Available as of v1.0.0*
@@ -356,6 +396,16 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client.delete_full_snapshot("{snapshot_name}").await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.deleteFullSnapshotAsync("{snapshot_name}").get();
```
### List full storage snapshots
```http
@@ -386,6 +436,16 @@ let client = QdrantClient::from_url("http://localhost:6334").build()?;
client.list_full_snapshots().await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.listFullSnapshotAsync().get();
```
### Download full storage snapshot
<aside role="status">Only available through the REST API for the time being.</aside>
@@ -104,6 +104,26 @@ client
.await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.VectorParams;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createCollectionAsync(
"{collection_name}",
VectorParams.newBuilder()
.setSize(768)
.setDistance(Distance.Cosine)
.setOnDisk(true)
.build())
.get();
```
This will create a collection with all vectors immediately stored in memmap storage.
This is the recommended way, in case your Qdrant instance operates with fast disks and you are working with large collections.
@@ -186,6 +206,36 @@ client
.await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.OptimizersConfigDiff;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorsConfig;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setVectorsConfig(
VectorsConfig.newBuilder()
.setParams(
VectorParams.newBuilder()
.setSize(768)
.setDistance(Distance.Cosine)
.build())
.build())
.setOptimizersConfig(
OptimizersConfigDiff.newBuilder().setMemmapThreshold(20000).build())
.build())
.get();
```
The rule of thumb to set the memmap threshold parameter is simple:
- if you have a balanced use scenario - set memmap threshold the same as `indexing_threshold` (default is 20000). In this case the optimizer will not make any extra runs and will optimize all thresholds at once.
@@ -276,6 +326,38 @@ client
.await?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.HnswConfigDiff;
import io.qdrant.client.grpc.Collections.OptimizersConfigDiff;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorsConfig;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setVectorsConfig(
VectorsConfig.newBuilder()
.setParams(
VectorParams.newBuilder()
.setSize(768)
.setDistance(Distance.Cosine)
.build())
.build())
.setOptimizersConfig(
OptimizersConfigDiff.newBuilder().setMemmapThreshold(20000).build())
.setHnswConfig(HnswConfigDiff.newBuilder().setOnDisk(true).build())
.build())
.get();
```
## Payload storage
Qdrant supports two types of payload storages: InMemory and OnDisk.
@@ -41,7 +41,7 @@ For using API key based authentication in Qdrant cloud see the cloud
section.
The API key then needs to be present in all REST or gRPC requests to your instance.
All official Qdrant clients for Python, Go, Rust, and .NET support the API key parameter.
All official Qdrant clients for Python, Go, Rust, .NET and Java support the API key parameter.
<!---
Examples with clients
@@ -91,6 +91,19 @@ let client = QdrantClient::from_url("https://xyz-example.eu-central.aws.cloud.qd
.build()?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client =
new QdrantClient(
QdrantGrpcClient.newBuilder(
"xyz-example.eu-central.aws.cloud.qdrant.io",
6334,
true)
.withApiKey("<paste-your-api-key-here>")
.build());
```
<aside role="alert">Internal communication channels are <strong>never</strong> protected by an API key. Internal gRPC uses port 6335 by default if running in distributed mode. You must ensure that this port is not publicly reachable and can only be used for node communication. By default, this setting is disabled for Qdrant Cloud and the Qdrant Helm chart.</aside>
### Read-only API key
@@ -17,6 +17,7 @@ or [protobuf](https://github.com/qdrant/qdrant/tree/master/lib/api/src/grpc/prot
|![rust](/docs/misc/rust.webp)|**[Rust](https://github.com/qdrant/rust-client)**|`cargo add qdrant-client`|**Latest Release**|
|![golang](/docs/misc/go.webp)|**[Go](https://github.com/qdrant/go-client)**|`go get github.com/qdrant/go-client`|**Latest Release**|
|![.net](/docs/misc/dotnet.webp)|**[.NET](https://github.com/qdrant/qdrant-dotnet)**|`dotnet add package Qdrant.Client`|**Latest Release**|
|![java](/docs/misc/java.webp)|**[Java](https://github.com/qdrant/java-client)**|[Available on Maven Central](https://central.sonatype.com/artifact/io.qdrant/client)|**Latest Release**|
## API Reference
@@ -54,6 +54,14 @@ use qdrant_client::client::QdrantClient;
let client = QdrantClient::from_url("http://localhost:6334").build()?;
```
```java
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
QdrantClient client = new QdrantClient(
QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
```
<aside role="status">By default, Qdrant starts with no encryption or authentication . This means anyone with network access to your machine can access your Qdrant container instance. Please read <a href="https://qdrant.tech/documentation/security/">Security</a> carefully for details on how to secure your instance.</aside>
## Create a collection
@@ -92,7 +100,17 @@ client
})
.await?;
```
```java
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.VectorParams;
client.createCollectionAsync("test_collection",
VectorParams.newBuilder().setDistance(Distance.Dot).setSize(4).build()).get();
```
<aside role="status">TypeScript, Rust examples use async/await syntax, so should be used in an async block.</aside>
<aside role="status">Java examples are enclosed within a try/catch block.</aside>
## Add vectors
@@ -164,6 +182,44 @@ let operation_info = client
dbg!(operation_info);
```
```java
import java.util.List;
import java.util.Map;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.ValueFactory.value;
import static io.qdrant.client.VectorsFactory.vectors;
import io.qdrant.client.grpc.Points.PointStruct;
import io.qdrant.client.grpc.Points.UpdateResult;
UpdateResult operationInfo =
client
.upsertAsync(
"test_collection",
List.of(
PointStruct.newBuilder()
.setId(id(1))
.setVectors(vectors(0.05f, 0.61f, 0.76f, 0.74f))
.putAllPayload(Map.of("city", value("Berlin")))
.build(),
PointStruct.newBuilder()
.setId(id(2))
.setVectors(vectors(0.19f, 0.81f, 0.75f, 0.11f))
.putAllPayload(Map.of("city", value("London")))
.build(),
PointStruct.newBuilder()
.setId(id(3))
.setVectors(vectors(0.36f, 0.55f, 0.47f, 0.94f))
.putAllPayload(Map.of("city", value("Moscow")))
.build()))
// Truncated
.get();
System.out.println(operationInfo);
```
**Response:**
```python
@@ -184,6 +240,11 @@ PointsOperationResponse {
}
```
```java
operation_id: 0
status: Completed
```
## Run a query
Let's ask a basic question - Which of our stored vectors are most similar to the query vector `[0.2, 0.1, 0.9, 0.7]`?
@@ -220,6 +281,28 @@ let search_result = client
dbg!(search_result);
```
```java
import java.util.List;
import io.qdrant.client.grpc.Points.ScoredPoint;
import io.qdrant.client.grpc.Points.SearchPoints;
import static io.qdrant.client.WithPayloadSelectorFactory.enable;
List<ScoredPoint> searchResult =
client
.searchAsync(
SearchPoints.newBuilder()
.setCollectionName("test_collection")
.setLimit(3)
.addAllVector(List.of(0.2f, 0.1f, 0.9f, 0.7f))
.setWithPayload(enable(true))
.build())
.get();
System.out.println(searchResult);
```
**Response:**
```python
@@ -289,6 +372,43 @@ SearchResponse {
}
```
```java
[id {
num: 4
}
payload {
key: "city"
value {
string_value: "New York"
}
}
score: 1.362
version: 1
, id {
num: 1
}
payload {
key: "city"
value {
string_value: "Berlin"
}
}
score: 1.273
version: 1
, id {
num: 3
}
payload {
key: "city"
value {
string_value: "Moscow"
}
}
score: 1.208
version: 1
]
```
The results are returned in decreasing similarity order. Note that payload and vector data is missing in these results by default.
See [payload and vector in the result](../concepts/search#payload-and-vector-in-the-result) on how to enable it.
@@ -344,6 +464,24 @@ let search_result = client
dbg!(search_result);
```
```java
import static io.qdrant.client.ConditionFactory.matchKeyword;
List<ScoredPoint> searchResult =
client
.searchAsync(
SearchPoints.newBuilder()
.setCollectionName("test_collection")
.setLimit(3)
.setFilter(Filter.newBuilder().addMust(matchKeyword("city", "London")))
.addAllVector(List.of(0.2f, 0.1f, 0.9f, 0.7f))
.setWithPayload(enable(true))
.build())
.get();
System.out.println(searchResult);
```
**Response:**
```python
@@ -393,6 +531,21 @@ SearchResponse {
}
```
```java
[id {
num: 2
}
payload {
key: "city"
value {
string_value: "London"
}
}
score: 0.871
version: 1
]
```
<aside role="status">To make filtered search fast on real datasets, we highly recommend to create <a href="../concepts/indexing/#payload-index">payload indexes</a>!</aside>
You have just conducted vector search. You loaded vectors into a database and queried the database with a vector of your own. Qdrant found the closest results and presented you with a similarity score.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB