docs: New Go SDK snippets (#1149)

This commit is contained in:
Anush
2024-09-09 16:20:04 +05:30
committed by GitHub
parent f1e3e09905
commit bea2afa5e8
21 changed files with 3307 additions and 47 deletions
@@ -101,3 +101,14 @@ var client = new QdrantClient(
apiKey: "<paste-your-api-key-here>"
);
```
```go
import "github.com/qdrant/go-client/qdrant"
client, err := qdrant.NewClient(&qdrant.Config{
Host: "xyz-example.eu-central.aws.cloud.qdrant.io",
Port: 6334,
APIKey: "<paste-your-api-key-here>",
UseTLS: true,
})
```
@@ -116,6 +116,27 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 100,
Distance: qdrant.Distance_Cosine,
}),
})
```
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.
@@ -254,6 +275,28 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 100,
Distance: qdrant.Distance_Cosine,
}),
InitFromCollection: qdrant.PtrOf("{from_collection_name}"),
})
```
### Collection with multiple vectors
*Available as of v0.10.0*
@@ -388,6 +431,34 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfigMap(
map[string]*qdrant.VectorParams{
"image": {
Size: 4,
Distance: qdrant.Distance_Dot,
},
"text": {
Size: 8,
Distance: qdrant.Distance_Cosine,
},
}),
})
```
For rare use cases, it is possible to create a collection without any vector storage.
*Available as of v1.1.1*
@@ -517,6 +588,28 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 1024,
Distance: qdrant.Distance_Cosine,
Datatype: qdrant.Datatype_Uint8.Enum(),
}),
})
```
Vectors with `uint8` datatype are stored in a more compact format, which can save memory and improve search speed at the cost of some precision.
If you choose to use the `uint8` datatype, elements of the vector will be stored as unsigned 8-bit integers, which can take values **from 0 to 255**.
@@ -632,6 +725,27 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_namee}",
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"text": {},
}),
})
```
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.
@@ -670,6 +784,12 @@ client.collectionExistsAsync("{collection_name}").get();
await client.CollectionExistsAsync("{collection_name}");
```
```go
import "context"
client.CollectionExists(context.Background(), "my_collection")
```
### Delete collection
```http
@@ -693,23 +813,19 @@ 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();
```
```csharp
using Qdrant.Client;
var client = new QdrantClient("localhost", 6334);
await client.DeleteCollectionAsync("{collection_name}");
```
```go
import "context"
client.DeleteCollection(context.Background(), "{collection_name}")
```
### Update collection parameters
Dynamic parameter updates may be helpful, for example, for more efficient initial loading of vectors.
@@ -788,6 +904,26 @@ await client.UpdateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.UpdateCollection(context.Background(), &qdrant.UpdateCollection{
CollectionName: "{collection_name}",
OptimizersConfig: &qdrant.OptimizersConfigDiff{
IndexingThreshold: qdrant.PtrOf(uint64(10000)),
},
})
```
The following parameters can be updated:
* `optimizers_config` - see [optimizer](../optimizer/) for details.
@@ -1103,6 +1239,38 @@ await client.UpdateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.UpdateCollection(context.Background(), &qdrant.UpdateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfigDiffMap(
map[string]*qdrant.VectorParamsDiff{
"my_vector": {
HnswConfig: &qdrant.HnswConfigDiff{
M: qdrant.PtrOf(uint64(3)),
EfConstruct: qdrant.PtrOf(uint64(123)),
},
},
}),
QuantizationConfig: qdrant.NewQuantizationDiffScalar(
&qdrant.ScalarQuantization{
Type: qdrant.QuantizationType_Int8,
Quantile: qdrant.PtrOf(float32(0.8)),
AlwaysRam: qdrant.PtrOf(true),
}),
})
```
## Collection info
Qdrant allows determining the configuration parameters of an existing collection to better understand how the points are
@@ -1136,6 +1304,12 @@ client.getCollectionInfoAsync("{collection_name}").get();
await client.GetCollectionInfoAsync("{collection_name}");
```
```go
import "context"
client.GetCollectionInfo(context.Background(), "{collection_name}")
```
<details>
<summary>Expected result</summary>
@@ -1276,6 +1450,24 @@ await client.UpdateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.UpdateCollection(context.Background(), &qdrant.UpdateCollection{
CollectionName: "{collection_name}",
OptimizersConfig: &qdrant.OptimizersConfigDiff{},
})
```
### Approximate point and vector counts
You may be interested in the count attributes:
@@ -1401,6 +1593,12 @@ client.createAliasAsync("production_collection", "example_collection").get();
await client.CreateAliasAsync(aliasName: "production_collection", collectionName: "example_collection");
```
```go
import "context"
client.CreateAlias(context.Background(), "production_collection", "example_collection")
```
### Remove alias
```bash
@@ -1464,6 +1662,12 @@ client.deleteAliasAsync("production_collection").get();
await client.DeleteAliasAsync("production_collection");
```
```go
import "context"
client.DeleteAlias(context.Background(), "production_collection")
```
### Switch collection
Multiple alias actions are performed atomically.
@@ -1562,6 +1766,14 @@ client.createAliasAsync("production_collection", "example_collection").get();
await client.DeleteAliasAsync("production_collection");
await client.CreateAliasAsync(aliasName: "production_collection", collectionName: "example_collection");
```
```go
import "context"
client.DeleteAlias(context.Background(), "production_collection")
client.CreateAlias(context.Background(), "production_collection", "example_collection")
```
### List collection aliases
```http
@@ -1614,6 +1826,21 @@ var client = new QdrantClient("localhost", 6334);
await client.ListCollectionAliasesAsync("{collection_name}");
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.ListCollectionAliases(context.Background(), "{collection_name}")
```
### List all aliases
```http
@@ -1667,6 +1894,21 @@ var client = new QdrantClient("localhost", 6334);
await client.ListAliasesAsync();
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.ListAliases(context.Background())
```
### List all collections
```http
@@ -1719,3 +1961,18 @@ var client = new QdrantClient("localhost", 6334);
await client.ListCollectionsAsync();
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.ListCollections(context.Background())
```
@@ -169,6 +169,37 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryRecommend(&qdrant.RecommendInput{
Positive: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(100)),
qdrant.NewVectorInputID(qdrant.NewIDNum(231)),
},
Negative: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(718)),
},
}),
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
},
},
})
```
Example result of this API would be
```json
@@ -335,6 +366,33 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryRecommend(&qdrant.RecommendInput{
Positive: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(100)),
qdrant.NewVectorInputID(qdrant.NewIDNum(231)),
},
Negative: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(718)),
},
}),
Using: qdrant.PtrOf("image"),
})
```
Parameter `using` specifies which stored vectors to use for the recommendation.
### Lookup vectors from another collection
@@ -470,6 +528,37 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryRecommend(&qdrant.RecommendInput{
Positive: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(100)),
qdrant.NewVectorInputID(qdrant.NewIDNum(231)),
},
Negative: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(718)),
},
}),
Using: qdrant.PtrOf("image"),
LookupFrom: &qdrant.LookupLocation{
CollectionName: "{external_collection_name}",
VectorName: qdrant.PtrOf("{external_vector_name}"),
},
})
```
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.
@@ -730,6 +819,59 @@ await client.QueryBatchAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
filter := qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
},
}
client.QueryBatch(context.Background(), &qdrant.QueryBatchPoints{
CollectionName: "{collection_name}",
QueryPoints: []*qdrant.QueryPoints{
{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryRecommend(&qdrant.RecommendInput{
Positive: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(100)),
qdrant.NewVectorInputID(qdrant.NewIDNum(231)),
},
Negative: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(718)),
},
},
),
Filter: &filter,
},
{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryRecommend(&qdrant.RecommendInput{
Positive: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(200)),
qdrant.NewVectorInputID(qdrant.NewIDNum(67)),
},
Negative: []*qdrant.VectorInput{
qdrant.NewVectorInputID(qdrant.NewIDNum(300)),
},
},
),
Filter: &filter,
},
},
},
)
```
The result of this API contains one array per recommendation requests.
```json
@@ -947,6 +1089,38 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryDiscover(&qdrant.DiscoverInput{
Target: qdrant.NewVectorInput(0.2, 0.1, 0.9, 0.7),
Context: &qdrant.ContextInput{
Pairs: []*qdrant.ContextInputPair{
{
Positive: qdrant.NewVectorInputID(qdrant.NewIDNum(100)),
Negative: qdrant.NewVectorInputID(qdrant.NewIDNum(718)),
},
{
Positive: qdrant.NewVectorInputID(qdrant.NewIDNum(200)),
Negative: qdrant.NewVectorInputID(qdrant.NewIDNum(300)),
},
},
},
}),
})
```
<aside role="status">
Notes about discovery search:
@@ -1113,6 +1287,35 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryContext(&qdrant.ContextInput{
Pairs: []*qdrant.ContextInputPair{
{
Positive: qdrant.NewVectorInputID(qdrant.NewIDNum(100)),
Negative: qdrant.NewVectorInputID(qdrant.NewIDNum(718)),
},
{
Positive: qdrant.NewVectorInputID(qdrant.NewIDNum(200)),
Negative: qdrant.NewVectorInputID(qdrant.NewIDNum(300)),
},
},
}),
})
```
<aside role="status">
Notes about context search:
@@ -149,6 +149,29 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
qdrant.NewMatch("color", "red"),
},
},
})
```
Filtered points would be:
```json
@@ -258,6 +281,29 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Should: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
qdrant.NewMatch("color", "red"),
},
},
})
```
Filtered points would be:
```json
@@ -367,6 +413,29 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
MustNot: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
qdrant.NewMatch("color", "red"),
},
},
})
```
Filtered points would be:
```json
@@ -475,6 +544,31 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
},
MustNot: []*qdrant.Condition{
qdrant.NewMatch("color", "red"),
},
},
})
```
Filtered points would be:
```json
@@ -602,6 +696,33 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
MustNot: []*qdrant.Condition{
qdrant.NewFilterAsCondition(&qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
qdrant.NewMatch("color", "red"),
},
}),
},
},
})
```
Filtered points would be:
```json
@@ -658,6 +779,12 @@ using static Qdrant.Client.Grpc.Conditions;
MatchKeyword("color", "red");
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewMatch("color", "red")
```
For the other types, the match condition will look exactly the same, except for the type used:
```json
@@ -699,6 +826,12 @@ using static Qdrant.Client.Grpc.Conditions;
Match("count", 0);
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewMatchInt("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.
@@ -753,6 +886,12 @@ using static Qdrant.Client.Grpc.Conditions;
Match("color", ["black", "yellow"]);
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewMatchKeywords("color", "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"]`.
@@ -814,6 +953,12 @@ using static Qdrant.Client.Grpc.Conditions;
Match("color", ["black", "yellow"]);
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewMatchExcept("color", "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"`.
@@ -953,6 +1098,28 @@ var client = new QdrantClient("localhost", 6334);
await client.ScrollAsync(collectionName: "{collection_name}", filter: MatchKeyword("country.name", "Germany"));
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Should: []*qdrant.Condition{
qdrant.NewMatch("country.name", "Germany"),
},
},
})
```
You can also search through arrays by projecting inner values using the `[]` syntax.
```http
@@ -1060,6 +1227,30 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Should: []*qdrant.Condition{
qdrant.NewRange("country.cities[].population", &qdrant.Range{
Gte: qdrant.PtrOf(9.0),
}),
},
},
})
```
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.
@@ -1149,6 +1340,28 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Should: []*qdrant.Condition{
qdrant.NewMatch("country.cities[].sightseeing", "Germany"),
},
},
})
```
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
@@ -1289,6 +1502,29 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("diet[].food", "meat"),
qdrant.NewMatchBool("diet[].likes", true),
},
},
})
```
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`
@@ -1440,6 +1676,33 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewNestedFilter("diet", &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("food", "meat"),
qdrant.NewMatchBool("likes", true),
},
}),
},
},
})
```
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.
@@ -1605,6 +1868,34 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewNestedFilter("diet", &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("food", "meat"),
qdrant.NewMatchBool("likes", true),
},
}),
qdrant.NewHasID(qdrant.NewIDNum(1)),
},
},
})
```
### Full Text Match
*Available as of v0.10.0*
@@ -1658,6 +1949,12 @@ using static Qdrant.Client.Grpc.Conditions;
MatchText("description", "good cheap");
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewMatchText("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
@@ -1726,6 +2023,16 @@ using static Qdrant.Client.Grpc.Conditions;
Range("price", new Qdrant.Client.Grpc.Range { Gte = 100.0, Lte = 450 });
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewRange("price", &qdrant.Range{
Gte: qdrant.PtrOf(100.0),
Lte: qdrant.PtrOf(450.0),
})
```
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.
@@ -1822,6 +2129,19 @@ Conditions.DatetimeRange(
);
```
```go
import (
"time"
"github.com/qdrant/go-client/qdrant"
"google.golang.org/protobuf/types/known/timestamppb"
)
qdrant.NewDatetimeRange("date", &qdrant.DatetimeRange{
Gt: timestamppb.New(time.Date(2023, 2, 8, 10, 49, 0, 0, time.UTC)),
Lte: timestamppb.New(time.Date(2024, 1, 31, 10, 14, 31, 0, time.UTC)),
})
```
### UUID Match
@@ -1867,6 +2187,12 @@ using static Qdrant.Client.Grpc.Conditions;
MatchKeyword("uuid", "f47ac10b-58cc-4372-a567-0e02b2c3d479");
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewMatch("uuid", "f47ac10b-58cc-4372-a567-0e02b2c3d479")
```
### Geo
#### Geo Bounding Box
@@ -1949,6 +2275,12 @@ using static Qdrant.Client.Grpc.Conditions;
GeoBoundingBox("location", 52.520711, 13.403683, 52.495862, 13.455868);
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewGeoBoundingBox("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
@@ -2019,6 +2351,12 @@ using static Qdrant.Client.Grpc.Conditions;
GeoRadius("location", 52.520711, 13.403683, 1000.0f);
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewGeoRadius("location", 52.520711, 13.403683, 1000.0)
```
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.
@@ -2291,6 +2629,29 @@ GeoPolygon(
);
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewGeoPolygon("location",
&qdrant.GeoLineString{
Points: []*qdrant.GeoPoint{
{Lat: -70, Lon: -70},
{Lat: 60, Lon: -70},
{Lat: 60, Lon: 60},
{Lat: -70, Lon: 60},
{Lat: -70, Lon: -70},
},
}, &qdrant.GeoLineString{
Points: []*qdrant.GeoPoint{
{Lat: -65, Lon: -65},
{Lat: 0, Lon: -65},
{Lat: 0, Lon: 0},
{Lat: -65, Lon: 0},
{Lat: -65, Lon: -65},
},
})
```
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.
@@ -2361,6 +2722,14 @@ using static Qdrant.Client.Grpc.Conditions;
ValuesCount("comments", new ValuesCount { Gt = 2 });
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewValuesCount("comments", &qdrant.ValuesCount{
Gt: qdrant.PtrOf(uint64(2)),
})
```
The result would be:
```json
@@ -2415,6 +2784,12 @@ using static Qdrant.Client.Grpc.Conditions;
IsEmpty("reports");
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewIsEmpty("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>
@@ -2465,6 +2840,12 @@ using static Qdrant.Client.Grpc.Conditions;
IsNull("reports");
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.NewIsNull("reports")
```
This condition will match all records where the field `reports` exists and has `NULL` value.
@@ -2552,6 +2933,35 @@ var client = new QdrantClient("localhost", 6334);
await client.ScrollAsync(collectionName: "{collection_name}", filter: HasId([1, 3, 5, 7, 9, 11]));
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewHasID(
qdrant.NewIDNum(1),
qdrant.NewIDNum(3),
qdrant.NewIDNum(5),
qdrant.NewIDNum(7),
qdrant.NewIDNum(9),
qdrant.NewIDNum(11),
),
},
},
})
```
Filtered points would be:
```json
@@ -208,6 +208,34 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Prefetch: []*qdrant.PrefetchQuery{
{
Query: qdrant.NewQuerySparse([]uint32{1, 42}, []float32{0.22, 0.8}),
Using: qdrant.PtrOf("sparse"),
},
{
Query: qdrant.NewQueryDense([]float32{0.01, 0.45, 0.67}),
Using: qdrant.PtrOf("dense"),
},
},
Query: qdrant.NewQueryFusion(qdrant.Fusion_RRF),
})
```
## Multi-stage queries
In many cases, the usage of a larger vector representation gives more accurate search results, but it is also more expensive to compute.
@@ -347,6 +375,32 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Prefetch: []*qdrant.PrefetchQuery{
{
Query: qdrant.NewQueryDense([]float32{1, 23, 45, 67}),
Using: qdrant.PtrOf("mrl_byte"),
Limit: qdrant.PtrOf(uint64(1000)),
},
},
Query: qdrant.NewQueryDense([]float32{0.01, 0.299, 0.45, 0.67}),
Using: qdrant.PtrOf("full"),
})
```
Fetch 100 results using the default vector, then re-score them using a multi-vector to get the top 10.
```http
@@ -487,6 +541,35 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Prefetch: []*qdrant.PrefetchQuery{
{
Query: qdrant.NewQueryDense([]float32{0.01, 0.45, 0.67}),
Limit: qdrant.PtrOf(uint64(100)),
},
},
Query: qdrant.NewQueryMulti([][]float32{
{0.1, 0.2},
{0.2, 0.1},
{0.8, 0.9},
}),
Using: qdrant.PtrOf("colbert"),
})
```
It is possible to combine all the above techniques in a single query:
```http
@@ -667,6 +750,43 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Prefetch: []*qdrant.PrefetchQuery{
{
Prefetch: []*qdrant.PrefetchQuery{
{
Query: qdrant.NewQueryDense([]float32{1, 23, 45, 67}),
Using: qdrant.PtrOf("mrl_byte"),
Limit: qdrant.PtrOf(uint64(1000)),
},
},
Query: qdrant.NewQueryDense([]float32{0.01, 0.45, 0.67}),
Limit: qdrant.PtrOf(uint64(100)),
Using: qdrant.PtrOf("full"),
},
},
Query: qdrant.NewQueryMulti([][]float32{
{0.1, 0.2},
{0.2, 0.1},
{0.8, 0.9},
}),
Using: qdrant.PtrOf("colbert"),
})
```
## Flexible interface
Other than the introduction of `prefetch`, the `Query API` has been designed to make querying simpler. Let's look at a few bonus features:
@@ -748,6 +868,24 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryID(qdrant.NewID("43cf51e2-8777-4f52-bc74-c2cbde0c8b04")),
})
```
The above example will fetch the default vector from the point with this id, and use it as the query vector.
If the `using` parameter is also specified, Qdrant will use the vector with that name.
@@ -857,6 +995,29 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryID(qdrant.NewID("43cf51e2-8777-4f52-bc74-c2cbde0c8b04")),
Using: qdrant.PtrOf("512d-vector"),
LookupFrom: &qdrant.LookupLocation{
CollectionName: "another_collection",
VectorName: qdrant.PtrOf("image-512"),
},
})
```
In the case above, Qdrant will fetch the `"image-512"` vector from the specified point id in the
collection `another_collection`.
@@ -1075,6 +1236,44 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Prefetch: []*qdrant.PrefetchQuery{
{
Query: qdrant.NewQuery(0.01, 0.45, 0.67),
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("color", "red"),
},
},
},
{
Query: qdrant.NewQuery(0.01, 0.45, 0.67),
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("color", "green"),
},
},
},
},
Query: qdrant.NewQueryOrderBy(&qdrant.OrderBy{
Key: "price",
}),
})
```
In this example, we first fetch 10 points with the color `"red"` and then 10 points with the color `"green"`.
Then, we order the results by the price field.
@@ -1178,4 +1377,24 @@ await client.QueryGroupsAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.QueryGroups(context.Background(), &qdrant.QueryPointGroups{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.01, 0.45, 0.67),
GroupBy: "document_id",
GroupSize: qdrant.PtrOf(uint64(2)),
})
```
For more information on the `grouping` capabilities refer to the reference documentation for search with [grouping](./search/#search-groups) and [lookup](./search/#lookup-in-groups).
@@ -100,6 +100,25 @@ var client = new QdrantClient("localhost", 6334);
await client.CreatePayloadIndexAsync(collectionName: "{collection_name}", fieldName: "name_of_the_field_to_index");
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
CollectionName: "{collection_name}",
FieldName: "name_of_the_field_to_index",
FieldType: qdrant.FieldType_FieldTypeKeyword.Enum(),
})
```
You can use dot notation to specify a nested field for indexing. Similar to specifying [nested filters](../filtering/#nested-key).
Available field types are:
@@ -261,6 +280,32 @@ await client.CreatePayloadIndexAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
CollectionName: "{collection_name}",
FieldName: "name_of_the_field_to_index",
FieldType: qdrant.FieldType_FieldTypeText.Enum(),
FieldIndexParams: qdrant.NewPayloadIndexParamsText(
&qdrant.TextIndexParams{
Tokenizer: qdrant.TokenizerType_Whitespace,
MinTokenLen: qdrant.PtrOf(uint64(2)),
MaxTokenLen: qdrant.PtrOf(uint64(10)),
Lowercase: qdrant.PtrOf(true),
}),
})
```
Available tokenizers are:
* `word` - splits the string into words, separated by spaces, punctuation marks, and special characters.
@@ -420,6 +465,30 @@ await client.CreatePayloadIndexAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
CollectionName: "{collection_name}",
FieldName: "name_of_the_field_to_index",
FieldType: qdrant.FieldType_FieldTypeInteger.Enum(),
FieldIndexParams: qdrant.NewPayloadIndexParamsInt(
&qdrant.IntegerIndexParams{
Lookup: false,
Range: true,
}),
})
```
### On-disk payload index
*Available as of v1.11.0*
@@ -535,7 +604,29 @@ await client.CreatePayloadIndexAsync(
}
}
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
CollectionName: "{collection_name}",
FieldName: "name_of_the_field_to_index",
FieldType: qdrant.FieldType_FieldTypeKeyword.Enum(),
FieldIndexParams: qdrant.NewPayloadIndexParamsKeyword(
&qdrant.KeywordIndexParams{
OnDisk: qdrant.PtrOf(true),
}),
})
```
Payload index on-disk is supported for following types:
@@ -668,9 +759,30 @@ await client.CreatePayloadIndexAsync(
}
}
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
CollectionName: "{collection_name}",
FieldName: "name_of_the_field_to_index",
FieldType: qdrant.FieldType_FieldTypeKeyword.Enum(),
FieldIndexParams: qdrant.NewPayloadIndexParamsKeyword(
&qdrant.KeywordIndexParams{
IsTenant: qdrant.PtrOf(true),
}),
})
```
Tenant optimization is supported for the following datatypes:
@@ -785,7 +897,29 @@ await client.CreatePayloadIndexAsync(
}
}
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
CollectionName: "{collection_name}",
FieldName: "name_of_the_field_to_index",
FieldType: qdrant.FieldType_FieldTypeInteger.Enum(),
FieldIndexParams: qdrant.NewPayloadIndexParamsInt(
&qdrant.IntegerIndexParams{
IsPrincipal: qdrant.PtrOf(true),
}),
})
```
Principal optimization is supported for following types:
@@ -968,6 +1102,30 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"splade-model-name": {
Index: &qdrant.SparseIndexConfig{
OnDisk: qdrant.PtrOf(false),
}},
}),
})
````
The following parameters may affect performance:
- `on_disk: true` - The index is stored on disk, which lets you save memory. This may slow down search performance.
@@ -1090,6 +1248,29 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"text": {
Modifier: qdrant.Modifier_Idf.Enum(),
},
}),
})
```
Qdrant uses the following formula to calculate the IDF modifier:
$$
@@ -406,6 +406,44 @@ await client.UpsertAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectors(0.05, 0.61, 0.76, 0.74),
Payload: qdrant.NewValueMap(map[string]any{
"city": "Berlin", "price": 1.99}),
},
{
Id: qdrant.NewIDNum(2),
Vectors: qdrant.NewVectors(0.19, 0.81, 0.75, 0.11),
Payload: qdrant.NewValueMap(map[string]any{
"city": []any{"Berlin", "London"}}),
},
{
Id: qdrant.NewIDNum(3),
Vectors: qdrant.NewVectors(0.36, 0.55, 0.47, 0.94),
Payload: qdrant.NewValueMap(map[string]any{
"city": []any{"Berlin", "London"},
"price": []any{1.99, 2.99}}),
},
},
})
```
## Update payload
### Set payload
@@ -504,6 +542,28 @@ await client.SetPayloadAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.SetPayload(context.Background(), &qdrant.SetPayloadPoints{
CollectionName: "{collection_name}",
Payload: qdrant.NewValueMap(
map[string]any{"property1": "string", "property2": "string"}),
PointsSelector: qdrant.NewPointsSelector(
qdrant.NewIDNum(0),
qdrant.NewIDNum(3)),
})
```
You don't need to know the ids of the points you want to modify. The alternative
is to use filters.
@@ -619,6 +679,30 @@ await client.SetPayloadAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.SetPayload(context.Background(), &qdrant.SetPayloadPoints{
CollectionName: "{collection_name}",
Payload: qdrant.NewValueMap(
map[string]any{"property1": "string", "property2": "string"}),
PointsSelector: qdrant.NewPointsSelectorFilter(&qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("color", "red"),
},
}),
})
```
_Available as of v1.8.0_
It is possible to modify only a specific key of the payload by using the `key` parameter.
@@ -755,6 +839,28 @@ await client.OverwritePayloadAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.OverwritePayload(context.Background(), &qdrant.SetPayloadPoints{
CollectionName: "{collection_name}",
Payload: qdrant.NewValueMap(
map[string]any{"property1": "string", "property2": "string"}),
PointsSelector: qdrant.NewPointsSelector(
qdrant.NewIDNum(0),
qdrant.NewIDNum(3)),
})
```
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.
@@ -816,6 +922,26 @@ var client = new QdrantClient("localhost", 6334);
await client.ClearPayloadAsync(collectionName: "{collection_name}", ids: new ulong[] { 0, 3, 100 });
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.ClearPayload(context.Background(), &qdrant.ClearPayloadPoints{
CollectionName: "{collection_name}",
Points: qdrant.NewPointsSelector(
qdrant.NewIDNum(0),
qdrant.NewIDNum(3)),
})
```
<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>
@@ -892,7 +1018,27 @@ await client.DeletePayloadAsync(
keys: ["color", "price"],
ids: new ulong[] { 0, 3, 100 }
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.DeletePayload(context.Background(), &qdrant.DeletePayloadPoints{
CollectionName: "{collection_name}",
Keys: []string{"color", "price"},
PointsSelector: qdrant.NewPointsSelector(
qdrant.NewIDNum(0),
qdrant.NewIDNum(3)),
})
```
Alternatively, you can use filters to delete payload keys from the points.
@@ -992,6 +1138,29 @@ await client.DeletePayloadAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.DeletePayload(context.Background(), &qdrant.DeletePayloadPoints{
CollectionName: "{collection_name}",
Keys: []string{"color", "price"},
PointsSelector: qdrant.NewPointsSelectorFilter(
&qdrant.Filter{
Must: []*qdrant.Condition{qdrant.NewMatch("color", "red")},
},
),
})
```
## 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.
@@ -1069,6 +1238,25 @@ await client.CreatePayloadIndexAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
CollectionName: "{collection_name}",
FieldName: "name_of_the_field_to_index",
FieldType: qdrant.FieldType_FieldTypeKeyword.Enum(),
})
```
The index usage flag is displayed in the payload schema with the [collection info API](https://api.qdrant.tech/api-reference/collections/get-collection).
Payload schema example:
@@ -158,12 +158,36 @@ await client.UpsertAsync(
{
Id = Guid.Parse("5c56c793-69f3-4fbf-87e6-c4bf54c28c26"),
Vectors = new[] { 0.05f, 0.61f, 0.76f, 0.74f },
Payload = { ["city"] = "red" }
Payload = { ["color"] = "Red" }
}
}
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewID("5c56c793-69f3-4fbf-87e6-c4bf54c28c26"),
Vectors: qdrant.NewVectors(0.05, 0.61, 0.76, 0.74),
Payload: qdrant.NewValueMap(map[string]any{"color": "Red"}),
},
},
})
```
and
```http
@@ -270,11 +294,34 @@ await client.UpsertAsync(
{
Id = 1,
Vectors = new[] { 0.05f, 0.61f, 0.76f, 0.74f },
Payload = { ["city"] = "red" }
Payload = { ["color"] = "Red" }
}
}
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectors(0.05, 0.61, 0.76, 0.74),
Payload: qdrant.NewValueMap(map[string]any{"color": "Red"}),
},
},
})
```
are both possible.
@@ -506,24 +553,58 @@ await client.UpsertAsync(
{
Id = 1,
Vectors = new[] { 0.9f, 0.1f, 0.1f },
Payload = { ["city"] = "red" }
Payload = { ["color"] = "red" }
},
new()
{
Id = 2,
Vectors = new[] { 0.1f, 0.9f, 0.1f },
Payload = { ["city"] = "green" }
Payload = { ["color"] = "green" }
},
new()
{
Id = 3,
Vectors = new[] { 0.1f, 0.1f, 0.9f },
Payload = { ["city"] = "blue" }
Payload = { ["color"] = "blue" }
}
}
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectors(0.9, 0.1, 0.1),
Payload: qdrant.NewValueMap(map[string]any{"color": "red"}),
},
{
Id: qdrant.NewIDNum(2),
Vectors: qdrant.NewVectors(0.1, 0.9, 0.1),
Payload: qdrant.NewValueMap(map[string]any{"color": "green"}),
},
{
Id: qdrant.NewIDNum(3),
Vectors: qdrant.NewVectors(0.1, 0.1, 0.9),
Payload: qdrant.NewValueMap(map[string]any{"color": "blue"}),
},
},
})
```
The Python client has additional features for loading points, which include:
- Parallelization
@@ -773,6 +854,39 @@ await client.UpsertAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"image": qdrant.NewVector(0.9, 0.1, 0.1, 0.2),
"text": qdrant.NewVector(0.4, 0.7, 0.1, 0.8, 0.1, 0.1, 0.9, 0.2),
}),
},
{
Id: qdrant.NewIDNum(2),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"image": qdrant.NewVector(0.2, 0.1, 0.3, 0.9),
"text": qdrant.NewVector(0.5, 0.2, 0.7, 0.4, 0.7, 0.2, 0.3, 0.9),
}),
},
},
})
```
_Available as of v1.2.0_
Named vectors are optional. When uploading points, some vectors may be omitted.
@@ -1002,6 +1116,41 @@ await client.UpsertAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"text": qdrant.NewVectorSparse(
[]uint32{6, 7},
[]float32{1.0, 2.0}),
}),
},
{
Id: qdrant.NewIDNum(2),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"text": qdrant.NewVectorSparse(
[]uint32{1, 2, 3, 4, 5},
[]float32{0.1, 0.2, 0.3, 0.4, 0.5}),
}),
},
},
})
```
## Modify points
To change a point, you can modify its vectors or its payload. There are several
@@ -1156,6 +1305,37 @@ await client.UpdateVectorsAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.UpdateVectors(context.Background(), &qdrant.UpdatePointVectors{
CollectionName: "{collection_name}",
Points: []*qdrant.PointVectors{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"image": qdrant.NewVector(0.1, 0.2, 0.3, 0.4),
}),
},
{
Id: qdrant.NewIDNum(2),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"text": qdrant.NewVector(0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2),
}),
},
},
})
```
To update points and replace all of its vectors, see [uploading
points](#upload-points).
@@ -1281,6 +1461,26 @@ var client = new QdrantClient("localhost", 6334);
await client.DeleteAsync(collectionName: "{collection_name}", ids: [0, 3, 100]);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Delete(context.Background(), &qdrant.DeletePoints{
CollectionName: "{collection_name}",
Points: qdrant.NewPointsSelector(
qdrant.NewIDNum(0), qdrant.NewIDNum(3), qdrant.NewIDNum(100),
),
})
```
Alternative way to specify which points to remove is to use filter.
```http
@@ -1366,6 +1566,30 @@ var client = new QdrantClient("localhost", 6334);
await client.DeleteAsync(collectionName: "{collection_name}", filter: MatchKeyword("color", "red"));
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Delete(context.Background(), &qdrant.DeletePoints{
CollectionName: "{collection_name}",
Points: qdrant.NewPointsSelectorFilter(
&qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("color", "red"),
},
},
),
})
```
This example removes all points with `{ "color": "red" }` from the collection.
## Retrieve points
@@ -1428,6 +1652,26 @@ await client.RetrieveAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Get(context.Background(), &qdrant.GetPoints{
CollectionName: "{collection_name}",
Ids: []*qdrant.PointId{
qdrant.NewIDNum(0), qdrant.NewIDNum(3), qdrant.NewIDNum(100),
},
})
```
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.
@@ -1553,6 +1797,30 @@ await client.ScrollAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("color", "red"),
},
},
Limit: qdrant.PtrOf(uint32(1)),
WithPayload: qdrant.NewWithPayload(true),
})
```
Returns all point with `color` = `red`.
```json
@@ -1637,6 +1905,27 @@ client.scrollAsync(ScrollPoints.newBuilder()
await client.ScrollAsync("{collection_name}", limit: 15, orderBy: "timestamp");
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Scroll(context.Background(), &qdrant.ScrollPoints{
CollectionName: "{collection_name}",
Limit: qdrant.PtrOf(uint32(15)),
OrderBy: &qdrant.OrderBy{
Key: "timestamp",
},
})
```
You need to use the `order_by` `key` parameter to specify the payload key. Then you can add other fields to control the ordering, such as `direction` and `start_from`:
```http
@@ -1697,6 +1986,16 @@ new OrderBy
};
```
```go
import "github.com/qdrant/go-client/qdrant"
qdrant.OrderBy{
Key: "timestamp",
Direction: qdrant.Direction_Desc.Enum(),
StartFrom: qdrant.NewStartFromInt(123),
}
```
<aside role="alert">When you use the <code>order_by</code> parameter, pagination is disabled.</aside>
When sorting is based on a non-unique value, it is not possible to rely on an ID offset. Thus, next_page_offset is not returned within the response. However, you can still do pagination by combining `"order_by": { "start_from": ... }` with a `{ "must_not": [{ "has_id": [...] }] }` filter.
@@ -1801,6 +2100,28 @@ await client.CountAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Count(context.Background(), &qdrant.CountPoints{
CollectionName: "midlib",
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("color", "red"),
},
},
})
```
Returns number of counts matching given filtering conditions:
```json
@@ -103,6 +103,24 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
})
```
**Search By Id**
```http
@@ -171,6 +189,24 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryID(qdrant.NewID("43cf51e2-8777-4f52-bc74-c2cbde0c8b04")),
})
```
## Metrics
There are many ways to estimate the similarity of vectors with each other.
@@ -332,6 +368,33 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
},
},
Params: &qdrant.SearchParams{
Exact: qdrant.PtrOf(false),
HnswEf: qdrant.PtrOf(uint64(128)),
},
})
```
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.
@@ -452,6 +515,25 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Using: qdrant.PtrOf("image"),
})
```
Search is processing only among vectors with the same name.
*Available as of v1.7.0*
@@ -564,6 +646,27 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuerySparse(
[]uint32{1, 2},
[]float32{2.0, 1.0}),
Using: qdrant.PtrOf("text"),
})
```
### Filtering results by score
In addition to payload filtering, it might be useful to filter out results with a low similarity score.
@@ -662,6 +765,26 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
WithPayload: qdrant.NewWithPayload(true),
WithVectors: qdrant.NewWithVectors(true),
})
```
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`:
@@ -762,6 +885,25 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
WithPayload: qdrant.NewWithPayloadInclude("city", "village", "town"),
})
```
Or use `include` or `exclude` explicitly. For example, to exclude `city`:
```http
@@ -858,6 +1000,25 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
WithPayload: qdrant.NewWithPayloadExclude("city"),
})
```
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
@@ -1061,6 +1222,41 @@ var queries = new List<QueryPoints>
await client.QueryBatchAsync(collectionName: "{collection_name}", queries: queries);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
filter := qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
},
}
client.QueryBatch(context.Background(), &qdrant.QueryBatchPoints{
CollectionName: "{collection_name}",
QueryPoints: []*qdrant.QueryPoints{
{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Filter: &filter,
},
{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.5, 0.3, 0.2, 0.3),
Filter: &filter,
},
},
})
```
The result of this API contains one array per search requests.
```json
@@ -1189,6 +1385,27 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
WithPayload: qdrant.NewWithPayload(true),
WithVectors: qdrant.NewWithVectors(true),
Offset: qdrant.PtrOf(uint64(100)),
})
```
Is equivalent to retrieving the 11th page with 10 records per page.
<aside role="alert">Large offset values may cause performance issues</aside>
@@ -1309,7 +1526,7 @@ client
.query_groups(
QueryPointGroupsBuilder::new("{collection_name}", "document_id")
.query(vec![0.2, 0.1, 0.9, 0.7])
.limit(2u64)
.group_size(2u64)
.with_payload(true)
.with_vectors(true)
.limit(4u64),
@@ -1340,13 +1557,33 @@ var client = new QdrantClient("localhost", 6334);
await client.QueryGroupsAsync(
collectionName: "{collection_name}",
query: new float[] { 1.1f },
query: new float[] { 0.2f, 0.1f, 0.9f, 0.7f },
groupBy: "document_id",
limit: 4,
groupSize: 2
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.QueryGroups(context.Background(), &qdrant.QueryPointGroups{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
GroupBy: "document_id",
GroupSize: qdrant.PtrOf(uint64(2)),
})
```
The output of a ***groups*** call looks like this:
```json
@@ -1532,7 +1769,7 @@ var client = new QdrantClient("localhost", 6334);
await client.SearchGroupsAsync(
collectionName: "{collection_name}",
vector: new float[] { 1.0f },
vector: new float[] { 0.2f, 0.1f, 0.9f, 0.7f},
groupBy: "document_id",
limit: 2,
groupSize: 2,
@@ -1548,6 +1785,30 @@ await client.SearchGroupsAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.QueryGroups(context.Background(), &qdrant.QueryPointGroups{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
GroupBy: "document_id",
GroupSize: qdrant.PtrOf(uint64(2)),
WithLookup: &qdrant.WithLookup{
Collection: "documents",
WithPayload: qdrant.NewWithPayloadInclude("title", "text"),
},
})
```
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.
@@ -1676,6 +1937,24 @@ var client = new QdrantClient("localhost", 6334);
await client.QueryAsync(collectionName: "{collection_name}", query: Sample.Random);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.QueryGroups(context.Background(), &qdrant.QueryPointGroups{
CollectionName: "{collection_name}",
Query: qdrant.NewQuerySample(qdrant.Sample_Random),
})
```
## Query planning
Depending on the filter used in the search - there are several possible scenarios for query execution.
@@ -67,6 +67,21 @@ var client = new QdrantClient("localhost", 6334);
await client.CreateSnapshotAsync("{collection_name}");
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateSnapshot(context.Background(), "{collection_name}")
```
This is a synchronous operation for which a `tar` archive file will be generated into the `snapshot_path`.
### Delete snapshot
@@ -127,6 +142,21 @@ var client = new QdrantClient("localhost", 6334);
await client.DeleteSnapshotAsync(collectionName: "{collection_name}", snapshotName: "{snapshot_name}");
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.DeleteSnapshot(context.Background(), "{collection_name}", "{snapshot_name}")
```
## List snapshot
List of snapshots for a collection:
@@ -177,6 +207,21 @@ var client = new QdrantClient("localhost", 6334);
await client.ListSnapshotsAsync("{collection_name}");
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.ListSnapshots(context.Background(), "{collection_name}")
```
## Retrieve snapshot
<aside role="status">Only available through the REST API for the time being.</aside>
@@ -395,6 +440,21 @@ var client = new QdrantClient("localhost", 6334);
await client.CreateFullSnapshotAsync();
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateFullSnapshot(context.Background())
```
### Delete full storage snapshot
*Available as of v1.0.0*
@@ -445,6 +505,21 @@ var client = new QdrantClient("localhost", 6334);
await client.DeleteFullSnapshotAsync("{snapshot_name}");
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.DeleteFullSnapshot(context.Background(), "{snapshot_name}")
```
### List full storage snapshots
```http
@@ -493,6 +568,21 @@ var client = new QdrantClient("localhost", 6334);
await client.ListFullSnapshotsAsync();
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.ListFullSnapshots(context.Background())
```
### Download full storage snapshot
<aside role="status">Only available through the REST API for the time being.</aside>
@@ -131,6 +131,28 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
OnDisk: qdrant.PtrOf(true),
}),
})
```
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.
@@ -243,6 +265,30 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
}),
OptimizersConfig: &qdrant.OptimizersConfigDiff{
MaxSegmentSize: qdrant.PtrOf(uint64(20000)),
},
})
```
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.
@@ -364,6 +410,33 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
}),
OptimizersConfig: &qdrant.OptimizersConfigDiff{
MaxSegmentSize: qdrant.PtrOf(uint64(20000)),
},
HnswConfig: &qdrant.HnswConfigDiff{
OnDisk: qdrant.PtrOf(true),
},
})
```
## Payload storage
Qdrant supports two types of payload storages: InMemory and OnDisk.
@@ -180,6 +180,27 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"text": {},
}),
})
```
Insert a point with a sparse vector into the created collection:
```http
@@ -187,7 +208,7 @@ PUT /collections/{collection_name}/points
{
"points": [
{
"id": 129,
"id": 1,
"vector": {
"text": {
"indices": [1, 3, 5, 7],
@@ -208,7 +229,7 @@ client.upsert(
collection_name="{collection_name}",
points=[
models.PointStruct(
id=129,
id=1,
payload={}, # Add any additional payload if necessary
vector={
"text": models.SparseVector(
@@ -229,7 +250,7 @@ const client = new QdrantClient({ host: "localhost", port: 6333 });
client.upsert("{collection_name}", {
points: [
{
id: 129,
id: 1,
vector: {
text: {
indices: [1, 3, 5, 7],
@@ -248,7 +269,7 @@ use qdrant_client::{Payload, Qdrant};
let client = Qdrant::from_url("http://localhost:6334").build()?;
let points = vec![PointStruct::new(
129,
1,
NamedVectors::default().add_vector(
"text",
Vector::new_sparse(vec![1, 3, 5, 7], vec![0.1, 0.2, 0.3, 0.4]),
@@ -281,7 +302,7 @@ client
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(129))
.setId(id(1))
.setVectors(
namedVectors(Map.of(
"text", vector(List.of(1.0f, 2.0f), List.of(6, 7))))
@@ -300,7 +321,7 @@ await client.UpsertAsync(
collectionName: "{collection_name}",
points: new List < PointStruct > {
new() {
Id = 129,
Id = 1,
Vectors = new Dictionary < string, Vector > {
["text"] = ([0.1 f, 0.2 f, 0.3 f, 0.4 f], [1, 3, 5, 7])
}
@@ -309,6 +330,34 @@ await client.UpsertAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectorsMap(
map[string]*qdrant.Vector{
"text": qdrant.NewVectorSparse(
[]uint32{1, 3, 5, 7},
[]float32{0.1, 0.2, 0.3, 0.4}),
}),
},
},
})
```
Now you can run a search with sparse vectors:
```http
@@ -401,6 +450,27 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuerySparse(
[]uint32{1, 3, 5, 7},
[]float32{0.1, 0.2, 0.3, 0.4}),
Using: qdrant.PtrOf("text"),
})
```
### Multivectors
**Available as of v1.10.0**
@@ -548,6 +618,30 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 128,
Distance: qdrant.Distance_Cosine,
MultivectorConfig: &qdrant.MultiVectorConfig{
Comparator: qdrant.MultiVectorComparator_MaxSim,
},
}),
})
```
To insert a point with multivector:
```http
@@ -681,6 +775,33 @@ await client.UpsertAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectorsMulti(
[][]float32{
{-0.013, 0.020, -0.007, -0.111},
{-0.030, -0.055, 0.001, 0.072},
{-0.041, 0.014, -0.032, -0.062}}),
},
},
})
```
To search with multivector (available in `query` API):
```http
@@ -777,6 +898,29 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryMulti(
[][]float32{
{-0.013, 0.020, -0.007, -0.111},
{-0.030, -0.055, 0.001, 0.072},
{-0.041, 0.014, -0.032, -0.062},
}),
})
```
## Named Vectors
@@ -910,6 +1054,33 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfigMap(
map[string]*qdrant.VectorParams{
"image": {
Size: 4,
Distance: qdrant.Distance_Dot,
},
"text": {
Size: 8,
Distance: qdrant.Distance_Cosine,
},
}),
})
```
<!-- ToDo: Examples of insert and search -->
@@ -1095,6 +1266,36 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 128,
Distance: qdrant.Distance_Cosine,
Datatype: qdrant.Datatype_Float16.Enum(),
}),
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"text": {
Index: &qdrant.SparseIndexConfig{
Datatype: qdrant.Datatype_Float16.Enum(),
},
},
}),
})
```
**Uint8**
Another step towards memory optimization is to use the Uint8 datatype for vectors.
@@ -1264,6 +1465,36 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 128,
Distance: qdrant.Distance_Cosine,
Datatype: qdrant.Datatype_Uint8.Enum(),
}),
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"text": {
Index: &qdrant.SparseIndexConfig{
Datatype: qdrant.Datatype_Uint8.Enum(),
},
},
}),
})
```
## Quantization
Apart from changing the datatype of the original vectors, Qdrant can create quantized representations of vectors alongside the original ones.
@@ -270,6 +270,28 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 300,
Distance: qdrant.Distance_Cosine,
}),
ShardNumber: qdrant.PtrOf(uint32(6)),
})
```
To ensure all nodes in your cluster are evenly utilized, the number of shards must be a multiple of the number of nodes you are currently running in your cluster.
> Aside: Advanced use cases such as multitenancy may require an uneven distribution of shards. See [Multitenancy](/articles/multitenancy/).
@@ -440,6 +462,30 @@ await client.CreateShardKeyAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
// ... other collection parameters
ShardNumber: qdrant.PtrOf(uint32(1)),
ShardingMethod: qdrant.ShardingMethod_Custom.Enum(),
})
client.CreateShardKey(context.Background(), "{collection_name}", &qdrant.CreateShardKey{
ShardKey: qdrant.NewShardKey("{shard_key}"),
})
```
In this mode, the `shard_number` means the number of shards per shard key, where points will be distributed evenly. For example, if you have 10 shard keys and a collection config with these settings:
```json
@@ -562,10 +608,38 @@ await client.UpsertAsync(
{
new() { Id = 111, Vectors = new[] { 0.1f, 0.2f, 0.3f } }
},
shardKeySelector: new ShardKeySelector { ShardKeys = { new List<ShardKey> { "user_id" } } }
shardKeySelector: new ShardKeySelector { ShardKeys = { new List<ShardKey> { "user_1" } } }
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(111),
Vectors: qdrant.NewVectors(0.1, 0.2, 0.3),
},
},
ShardKeySelector: &qdrant.ShardKeySelector{
ShardKeys: []*qdrant.ShardKey{
qdrant.NewShardKey("user_1"),
},
},
})
```
<aside role="alert">
Using the same point ID across multiple shard keys is <strong>not supported<sup>*</sup></strong> and should be avoided.
</aside>
@@ -791,6 +865,29 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 300,
Distance: qdrant.Distance_Cosine,
}),
ShardNumber: qdrant.PtrOf(uint32(6)),
ReplicationFactor: qdrant.PtrOf(uint32(2)),
})
```
This code sample creates a collection with a total of 6 logical shards backed by a total of 12 physical shards.
Since a replication factor of "2" would require twice as much storage space, it is advised to make sure the hardware can host the additional shard replicas beforehand.
@@ -1037,6 +1134,30 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 300,
Distance: qdrant.Distance_Cosine,
}),
ShardNumber: qdrant.PtrOf(uint32(6)),
ReplicationFactor: qdrant.PtrOf(uint32(2)),
WriteConsistencyFactor: qdrant.PtrOf(uint32(2)),
})
```
Write operations will fail if the number of active replicas is less than the `write_consistency_factor`.
### Read consistency
@@ -1151,7 +1272,7 @@ client.queryAsync(
.setCollectionName("{collection_name}")
.setFilter(Filter.newBuilder().addMust(matchKeyword("city", "London")).build())
.setQuery(nearest(.2f, 0.1f, 0.9f, 0.7f))
.setParams(SearchParams.newBuilder().setHnswEf(128).setExact(true).build())
.setParams(SearchParams.newBuilder().setHnswEf(128).setExact(false).build())
.setLimit(3)
.setReadConsistency(
ReadConsistency.newBuilder().setType(ReadConsistencyType.Majority).build())
@@ -1170,12 +1291,40 @@ await client.QueryAsync(
collectionName: "{collection_name}",
query: new float[] { 0.2f, 0.1f, 0.9f, 0.7f },
filter: MatchKeyword("city", "London"),
searchParams: new SearchParams { HnswEf = 128, Exact = true },
searchParams: new SearchParams { HnswEf = 128, Exact = false },
limit: 3,
readConsistency: new ReadConsistency { Type = ReadConsistencyType.Majority }
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
},
},
Params: &qdrant.SearchParams{
HnswEf: qdrant.PtrOf(uint64(128)),
},
Limit: qdrant.PtrOf(uint64(3)),
ReadConsistency: qdrant.NewReadConsistencyType(qdrant.ReadConsistencyType_Majority),
})
```
### Write ordering
Write `ordering` can be specified for any write request to serialize it through a single "leader" node,
@@ -1320,25 +1469,62 @@ await client.UpsertAsync(
{
Id = 1,
Vectors = new[] { 0.9f, 0.1f, 0.1f },
Payload = { ["city"] = "red" }
Payload = { ["color"] = "red" }
},
new()
{
Id = 2,
Vectors = new[] { 0.1f, 0.9f, 0.1f },
Payload = { ["city"] = "green" }
Payload = { ["color"] = "green" }
},
new()
{
Id = 3,
Vectors = new[] { 0.1f, 0.1f, 0.9f },
Payload = { ["city"] = "blue" }
Payload = { ["color"] = "blue" }
}
},
ordering: WriteOrderingType.Strong
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectors(0.9, 0.1, 0.1),
Payload: qdrant.NewValueMap(map[string]any{"color": "red"}),
},
{
Id: qdrant.NewIDNum(2),
Vectors: qdrant.NewVectors(0.1, 0.9, 0.1),
Payload: qdrant.NewValueMap(map[string]any{"color": "green"}),
},
{
Id: qdrant.NewIDNum(3),
Vectors: qdrant.NewVectors(0.1, 0.1, 0.9),
Payload: qdrant.NewValueMap(map[string]any{"color": "blue"}),
},
},
Ordering: &qdrant.WriteOrdering{
Type: qdrant.WriteOrderingType_Strong,
},
})
```
## Listener mode
<aside role="alert">This is an experimental feature, its behavior may change in the future.</aside>
@@ -176,6 +176,40 @@ await client.UpsertAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectors(0.9, 0.1, 0.1),
Payload: qdrant.NewValueMap(map[string]any{"group_id": "user_1"}),
},
{
Id: qdrant.NewIDNum(2),
Vectors: qdrant.NewVectors(0.1, 0.9, 0.1),
Payload: qdrant.NewValueMap(map[string]any{"group_id": "user_1"}),
},
{
Id: qdrant.NewIDNum(3),
Vectors: qdrant.NewVectors(0.1, 0.1, 0.9),
Payload: qdrant.NewValueMap(map[string]any{"group_id": "user_2"}),
},
},
})
```
2. Use a filter along with `group_id` to filter vectors for each user.
```http
@@ -291,6 +325,29 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.1, 0.1, 0.9),
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("group_id", "user_1"),
},
},
})
```
## Calibrate performance
The speed of indexation may become a bottleneck in this case, as each user's vector will be indexed into the same collection. To avoid this bottleneck, consider _bypassing the construction of a global vector index_ for the entire collection and building it only for individual groups instead.
@@ -407,6 +464,31 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
}),
HnswConfig: &qdrant.HnswConfigDiff{
PayloadM: qdrant.PtrOf(uint64(16)),
M: qdrant.PtrOf(uint64(0)),
},
})
```
3. Create keyword payload index for `group_id` field.
<aside role="alert">
@@ -512,7 +594,29 @@ await client.CreatePayloadIndexAsync(
}
}
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
CollectionName: "{collection_name}",
FieldName: "group_id",
FieldType: qdrant.FieldType_FieldTypeKeyword.Enum(),
FieldIndexParams: qdrant.NewPayloadIndexParams(
&qdrant.KeywordIndexParams{
IsTenant: qdrant.PtrOf(true),
}),
})
```
`is_tenant=true` parameter is optional, but specifying it provides storage with additional inforamtion about the usage patterns the collection is going to use.
@@ -154,6 +154,32 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
OnDisk: qdrant.PtrOf(true),
}),
QuantizationConfig: qdrant.NewQuantizationScalar(&qdrant.ScalarQuantization{
Type: qdrant.QuantizationType_Int8,
AlwaysRam: qdrant.PtrOf(true),
}),
})
```
`on_disk` will ensure that vectors will be stored on disk, while `always_ram` will ensure that quantized vectors will be stored in RAM.
Optionally, you can disable rescoring with search `params`, which will reduce the number of disk reads even further, but potentially slightly decrease the precision.
@@ -264,6 +290,29 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Params: &qdrant.SearchParams{
Quantization: &qdrant.QuantizationSearchParams{
Rescore: qdrant.PtrOf(true),
},
},
})
```
## Prefer high precision with low memory footprint
In case you need high precision, but don't have enough RAM to store vectors in memory, you can enable on-disk vectors and HNSW index.
@@ -372,6 +421,31 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
OnDisk: qdrant.PtrOf(true),
}),
HnswConfig: &qdrant.HnswConfigDiff{
OnDisk: qdrant.PtrOf(true),
},
})
```
In this scenario you can increase the precision of the search by increasing the `ef` and `m` parameters of the HNSW index, even with limited RAM.
```json
@@ -399,8 +473,7 @@ PUT /collections/{collection_name}
{
"vectors": {
"size": 768,
"distance": "Cosine",
"on_disk": true
"distance": "Cosine"
},
"quantization_config": {
"scalar": {
@@ -418,7 +491,7 @@ client = QdrantClient(url="http://localhost:6333")
client.create_collection(
collection_name="{collection_name}",
vectors_config=models.VectorParams(size=768, distance=models.Distance.COSINE, on_disk=True),
vectors_config=models.VectorParams(size=768, distance=models.Distance.COSINE),
quantization_config=models.ScalarQuantization(
scalar=models.ScalarQuantizationConfig(
type=models.ScalarType.INT8,
@@ -437,7 +510,6 @@ client.createCollection("{collection_name}", {
vectors: {
size: 768,
distance: "Cosine",
on_disk: true,
},
quantization_config: {
scalar: {
@@ -460,7 +532,7 @@ let client = Qdrant::from_url("http://localhost:6334").build()?;
client
.create_collection(
CreateCollectionBuilder::new("{collection_name}")
.vectors_config(VectorParamsBuilder::new(768, Distance::Cosine).on_disk(true))
.vectors_config(VectorParamsBuilder::new(768, Distance::Cosine))
.quantization_config(
ScalarQuantizationBuilder::default()
.r#type(QuantizationType::Int8.into())
@@ -495,7 +567,6 @@ client
VectorParams.newBuilder()
.setSize(768)
.setDistance(Distance.Cosine)
.setOnDisk(true)
.build())
.build())
.setQuantizationConfig(
@@ -518,7 +589,7 @@ var client = new QdrantClient("localhost", 6334);
await client.CreateCollectionAsync(
collectionName: "{collection_name}",
vectorsConfig: new VectorParams { Size = 768, Distance = Distance.Cosine, OnDisk = true},
vectorsConfig: new VectorParams { Size = 768, Distance = Distance.Cosine},
quantizationConfig: new QuantizationConfig
{
Scalar = new ScalarQuantization { Type = QuantizationType.Int8, AlwaysRam = true }
@@ -526,6 +597,31 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
}),
QuantizationConfig: qdrant.NewQuantizationScalar(&qdrant.ScalarQuantization{
Type: qdrant.QuantizationType_Int8,
AlwaysRam: qdrant.PtrOf(true),
}),
})
```
There are also some search-time parameters you can use to tune the search accuracy and speed:
```http
@@ -619,6 +715,28 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Params: &qdrant.SearchParams{
HnswEf: qdrant.PtrOf(uint64(128)),
Exact: qdrant.PtrOf(false),
},
})
```
- `hnsw_ef` - controls the number of neighbors to visit during search. The higher the value, the more accurate and slower the search will be. Recommended range is 32-512.
- `exact` - if set to `true`, will perform exact search, which will be slower, but more accurate. You can use it to compare results of the search with different `hnsw_ef` values versus the ground truth.
@@ -736,6 +854,30 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
}),
OptimizersConfig: &qdrant.OptimizersConfigDiff{
DefaultSegmentNumber: qdrant.PtrOf(uint64(16)),
},
})
```
To prefer throughput, you can set up Qdrant to use as many cores as possible for processing multiple requests in parallel.
To do that, you can configure qdrant to use minimal number of segments, which is usually 2.
Large segments benefit from the size of the index and overall smaller number of vector comparisons required to find the nearest neighbors. But at the same time require more time to build index.
@@ -841,4 +983,28 @@ await client.CreateCollectionAsync(
vectorsConfig: new VectorParams { Size = 768, Distance = Distance.Cosine },
optimizersConfig: new OptimizersConfigDiff { DefaultSegmentNumber = 2 }
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
}),
OptimizersConfig: &qdrant.OptimizersConfigDiff{
DefaultSegmentNumber: qdrant.PtrOf(uint64(2)),
},
})
```
@@ -282,6 +282,34 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
}),
QuantizationConfig: qdrant.NewQuantizationScalar(
&qdrant.ScalarQuantization{
Type: qdrant.QuantizationType_Int8,
Quantile: qdrant.PtrOf(float32(0.99)),
AlwaysRam: qdrant.PtrOf(true),
},
),
})
```
There are 3 parameters that you can specify in the `quantization_config` section:
`type` - the type of the quantized vector components. Currently, Qdrant supports only `int8`.
@@ -418,6 +446,32 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 1536,
Distance: qdrant.Distance_Cosine,
}),
QuantizationConfig: qdrant.NewQuantizationBinary(
&qdrant.BinaryQuantization{
AlwaysRam: qdrant.PtrOf(true),
},
),
})
```
`always_ram` - whether to keep quantized vectors always cached in RAM or not. By default, quantized vectors are loaded in the same way as the original vectors.
However, in some setups you might want to keep quantized vectors in RAM to speed up the search process.
@@ -553,6 +607,33 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
}),
QuantizationConfig: qdrant.NewQuantizationProduct(
&qdrant.ProductQuantization{
Compression: qdrant.CompressionRatio_x16,
AlwaysRam: qdrant.PtrOf(true),
},
),
})
```
There are two parameters that you can specify in the `quantization_config` section:
`compression` - compression ratio.
@@ -697,6 +778,31 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Params: &qdrant.SearchParams{
Quantization: &qdrant.QuantizationSearchParams{
Ignore: qdrant.PtrOf(false),
Rescore: qdrant.PtrOf(true),
Oversampling: qdrant.PtrOf(2.0),
},
},
})
```
`ignore` - Toggle whether to ignore quantized vectors during the search process. By default, Qdrant will use quantized vectors if they are available.
`rescore` - Having the original vectors available, Qdrant can re-evaluate top-k search results using the original vectors.
@@ -827,6 +933,29 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Params: &qdrant.SearchParams{
Quantization: &qdrant.QuantizationSearchParams{
Ignore: qdrant.PtrOf(false),
},
},
})
```
- **Adjust the quantile parameter**: The quantile parameter in scalar quantization determines the quantization bounds.
By setting it to a value lower than 1.0, you can exclude extreme values (outliers) from the quantization bounds.
For example, if you set the quantile to 0.99, 1% of the extreme values will be excluded.
@@ -978,6 +1107,34 @@ await client.CreateCollectionAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
OnDisk: qdrant.PtrOf(true),
}),
QuantizationConfig: qdrant.NewQuantizationScalar(
&qdrant.ScalarQuantization{
Type: qdrant.QuantizationType_Int8,
AlwaysRam: qdrant.PtrOf(true),
},
),
})
```
In this scenario, the number of disk reads may play a significant role in the search speed.
In a system with high disk latency, the re-scoring step may become a bottleneck.
@@ -1089,6 +1246,29 @@ await client.QueryAsync(
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Params: &qdrant.SearchParams{
Quantization: &qdrant.QuantizationSearchParams{
Rescore: qdrant.PtrOf(false),
},
},
})
```
- **All on Disk** - all vectors, original and quantized, are stored on disk. This mode allows to achieve the smallest memory footprint, but at the cost of the search speed.
It is recommended to use this mode if you have a large collection and fast storage (e.g. SSD or NVMe).
@@ -1226,3 +1406,31 @@ await client.CreateCollectionAsync(
}
);
```
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
OnDisk: qdrant.PtrOf(true),
}),
QuantizationConfig: qdrant.NewQuantizationScalar(
&qdrant.ScalarQuantization{
Type: qdrant.QuantizationType_Int8,
AlwaysRam: qdrant.PtrOf(false),
},
),
})
```
@@ -108,6 +108,17 @@ var client = new QdrantClient(
);
```
```go
import "github.com/qdrant/go-client/qdrant"
client, err := qdrant.NewClient(&qdrant.Config{
Host: "xyz-example.eu-central.aws.cloud.qdrant.io",
Port: 6334,
APIKey: "<paste-your-api-key-here>",
UseTLS: true,
})
```
<aside role="alert">Internal communication channels are <strong>never</strong> protected by an API key nor bearer tokens. 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
@@ -215,6 +226,16 @@ var client = new QdrantClient(
);
```
```go
import "github.com/qdrant/go-client/qdrant"
client, err := qdrant.NewClient(&qdrant.Config{
Host: "xyz-example.eu-central.aws.cloud.qdrant.io",
Port: 6334,
APIKey: "<JWT>",
UseTLS: true,
})
```
#### Generating JSON Web Tokens
Due to the nature of JWT, anyone who knows the `api_key` can generate tokens by using any of the existing libraries and tools, it is not necessary for them to have access to the Qdrant instance to generate them.
@@ -53,7 +53,7 @@ const client = new QdrantClient({ host: "localhost", port: 6333 });
```rust
use qdrant_client::Qdrant;
// The Rust client uses Qdrant's GRPC interface
// The Rust client uses Qdrant's gRPC interface
let client = Qdrant::from_url("http://localhost:6334").build()?;
```
@@ -61,7 +61,7 @@ let client = Qdrant::from_url("http://localhost:6334").build()?;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
// The Java client uses Qdrant's GRPC interface
// The Java client uses Qdrant's gRPC interface
QdrantClient client = new QdrantClient(
QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
```
@@ -69,10 +69,20 @@ QdrantClient client = new QdrantClient(
```csharp
using Qdrant.Client;
// The C# client uses Qdrant's GRPC interface
// The C# client uses Qdrant's gRPC interface
var client = new QdrantClient("localhost", 6334);
```
```go
import "github.com/qdrant/go-client/qdrant"
// The Go client uses Qdrant's gRPC interface
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
```
<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="/documentation/security/">Security</a> carefully for details on how to secure your instance.</aside>
## Create a collection
@@ -122,8 +132,21 @@ await client.CreateCollectionAsync(collectionName: "test_collection", vectorsCon
});
```
<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>
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 4,
Distance: qdrant.Distance_Cosine,
}),
})
```
## Add vectors
@@ -262,6 +285,41 @@ var operationInfo = await client.UpsertAsync(collectionName: "test_collection",
Console.WriteLine(operationInfo);
```
```go
import (
"context"
"fmt"
"github.com/qdrant/go-client/qdrant"
)
operationInfo, err := client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "test_collection",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectors(0.05, 0.61, 0.76, 0.74),
Payload: qdrant.NewValueMap(map[string]any{"city": "Berlin"}),
},
{
Id: qdrant.NewIDNum(2),
Vectors: qdrant.NewVectors(0.19, 0.81, 0.75, 0.11),
Payload: qdrant.NewValueMap(map[string]any{"city": "London"}),
},
{
Id: qdrant.NewIDNum(3),
Vectors: qdrant.NewVectors(0.36, 0.55, 0.47, 0.94),
Payload: qdrant.NewValueMap(map[string]any{"city": "Moscow"}),
},
// Truncated
},
})
if err != nil {
panic(err)
}
fmt.Println(operationInfo)
```
**Response:**
```python
@@ -295,6 +353,10 @@ status: Completed
{ "operationId": "0", "status": "Completed" }
```
```go
operation_id:0 status:Acknowledged
```
## 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]`?
@@ -324,7 +386,6 @@ let search_result = client
.query(
QueryPointsBuilder::new("test_collection")
.query(vec![0.2, 0.1, 0.9, 0.7])
.with_payload(true),
)
.await?;
@@ -337,7 +398,6 @@ import java.util.List;
import io.qdrant.client.grpc.Points.ScoredPoint;
import io.qdrant.client.grpc.Points.QueryPoints;
import static io.qdrant.client.WithPayloadSelectorFactory.enable;
import static io.qdrant.client.QueryFactory.nearest;
List<ScoredPoint> searchResult =
@@ -345,7 +405,6 @@ List<ScoredPoint> searchResult =
.setCollectionName("test_collection")
.setLimit(3)
.setQuery(nearest(0.2f, 0.1f, 0.9f, 0.7f))
.setWithPayload(enable(true))
.build()).get();
System.out.println(searchResult);
@@ -356,12 +415,30 @@ var searchResult = await client.QueryAsync(
collectionName: "test_collection",
query: new float[] { 0.2f, 0.1f, 0.9f, 0.7f },
limit: 3,
payloadSelector: true
);
Console.WriteLine(searchResult);
```
```go
import (
"context"
"fmt"
"github.com/qdrant/go-client/qdrant"
)
searchResult, err := client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "test_collection",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
})
if err != nil {
panic(err)
}
fmt.Println(searchResult)
```
**Response:**
```json
@@ -473,6 +550,31 @@ var searchResult = await client.QueryAsync(
Console.WriteLine(searchResult);
```
```go
import (
"context"
"fmt"
"github.com/qdrant/go-client/qdrant"
)
searchResult, err := client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "test_collection",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Filter: &qdrant.Filter{
Must: []*qdrant.Condition{
qdrant.NewMatch("city", "London"),
},
},
WithPayload: qdrant.NewWithPayload(true),
})
if err != nil {
panic(err)
}
fmt.Println(searchResult)
```
**Response:**
```json