mirror of
https://github.com/qdrant/landing_page.git
synced 2026-09-26 22:48:30 +02:00
Merge pull request #2362 from qdrant/with_lookup-example
Add with_lookup example
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
Populate both collections for the `with_lookup` pattern. The `documents` collection holds one point per document with payload only. Each chunk in `chunks` carries a `document_id` payload value that matches the id of a point in `documents`.
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
public class Snippet
|
||||
{
|
||||
public static async Task Run()
|
||||
{
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.UpsertAsync(
|
||||
collectionName: "documents",
|
||||
points: new List<PointStruct>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = 200,
|
||||
Vectors = new Dictionary<string, Vector>(),
|
||||
Payload =
|
||||
{
|
||||
["title"] = "Document A",
|
||||
["text"] = "This is document A",
|
||||
},
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = 201,
|
||||
Vectors = new Dictionary<string, Vector>(),
|
||||
Payload =
|
||||
{
|
||||
["title"] = "Document B",
|
||||
["text"] = "This is document B",
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await client.UpsertAsync(
|
||||
collectionName: "chunks",
|
||||
points: new List<PointStruct>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = 0,
|
||||
Vectors = new float[] { 0.1f, 0.2f, 0.3f, 0.4f },
|
||||
Payload = { ["document_id"] = 200 },
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = 1,
|
||||
Vectors = new float[] { 0.5f, 0.6f, 0.7f, 0.8f },
|
||||
Payload = { ["document_id"] = new Value[] { 200L, 201L } },
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.UpsertAsync(
|
||||
collectionName: "documents",
|
||||
points: new List<PointStruct>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = 200,
|
||||
Vectors = new Dictionary<string, Vector>(),
|
||||
Payload =
|
||||
{
|
||||
["title"] = "Document A",
|
||||
["text"] = "This is document A",
|
||||
},
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = 201,
|
||||
Vectors = new Dictionary<string, Vector>(),
|
||||
Payload =
|
||||
{
|
||||
["title"] = "Document B",
|
||||
["text"] = "This is document B",
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await client.UpsertAsync(
|
||||
collectionName: "chunks",
|
||||
points: new List<PointStruct>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = 0,
|
||||
Vectors = new float[] { 0.1f, 0.2f, 0.3f, 0.4f },
|
||||
Payload = { ["document_id"] = 200 },
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = 1,
|
||||
Vectors = new float[] { 0.5f, 0.6f, 0.7f, 0.8f },
|
||||
Payload = { ["document_id"] = new Value[] { 200L, 201L } },
|
||||
},
|
||||
}
|
||||
);
|
||||
```
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
```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: "documents",
|
||||
Points: []*qdrant.PointStruct{
|
||||
{
|
||||
Id: qdrant.NewIDNum(200),
|
||||
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{}),
|
||||
Payload: qdrant.NewValueMap(map[string]any{
|
||||
"title": "Document A",
|
||||
"text": "This is document A",
|
||||
}),
|
||||
},
|
||||
{
|
||||
Id: qdrant.NewIDNum(201),
|
||||
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{}),
|
||||
Payload: qdrant.NewValueMap(map[string]any{
|
||||
"title": "Document B",
|
||||
"text": "This is document B",
|
||||
}),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
client.Upsert(context.Background(), &qdrant.UpsertPoints{
|
||||
CollectionName: "chunks",
|
||||
Points: []*qdrant.PointStruct{
|
||||
{
|
||||
Id: qdrant.NewIDNum(0),
|
||||
Vectors: qdrant.NewVectors(0.1, 0.2, 0.3, 0.4),
|
||||
Payload: qdrant.NewValueMap(map[string]any{"document_id": 200}),
|
||||
},
|
||||
{
|
||||
Id: qdrant.NewIDNum(1),
|
||||
Vectors: qdrant.NewVectors(0.5, 0.6, 0.7, 0.8),
|
||||
Payload: qdrant.NewValueMap(map[string]any{"document_id": []int{200, 201}}),
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
```java
|
||||
import static io.qdrant.client.PointIdFactory.id;
|
||||
import static io.qdrant.client.ValueFactory.list;
|
||||
import static io.qdrant.client.ValueFactory.value;
|
||||
import static io.qdrant.client.VectorsFactory.namedVectors;
|
||||
import static io.qdrant.client.VectorsFactory.vectors;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Points.PointStruct;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
QdrantClient client = new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client.upsertAsync(
|
||||
"documents",
|
||||
List.of(
|
||||
PointStruct.newBuilder()
|
||||
.setId(id(200))
|
||||
.setVectors(namedVectors(Map.of()))
|
||||
.putAllPayload(Map.of(
|
||||
"title", value("Document A"),
|
||||
"text", value("This is document A")))
|
||||
.build(),
|
||||
PointStruct.newBuilder()
|
||||
.setId(id(201))
|
||||
.setVectors(namedVectors(Map.of()))
|
||||
.putAllPayload(Map.of(
|
||||
"title", value("Document B"),
|
||||
"text", value("This is document B")))
|
||||
.build())).get();
|
||||
|
||||
client.upsertAsync(
|
||||
"chunks",
|
||||
List.of(
|
||||
PointStruct.newBuilder()
|
||||
.setId(id(0))
|
||||
.setVectors(vectors(0.1f, 0.2f, 0.3f, 0.4f))
|
||||
.putAllPayload(Map.of("document_id", value(200)))
|
||||
.build(),
|
||||
PointStruct.newBuilder()
|
||||
.setId(id(1))
|
||||
.setVectors(vectors(0.5f, 0.6f, 0.7f, 0.8f))
|
||||
.putAllPayload(Map.of("document_id", list(List.of(value(200), value(201)))))
|
||||
.build())).get();
|
||||
```
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
```python
|
||||
client.upsert(
|
||||
collection_name="documents",
|
||||
points=[
|
||||
models.PointStruct(
|
||||
id=200,
|
||||
vector={},
|
||||
payload={"title": "Document A", "text": "This is document A"},
|
||||
),
|
||||
models.PointStruct(
|
||||
id=201,
|
||||
vector={},
|
||||
payload={"title": "Document B", "text": "This is document B"},
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
client.upsert(
|
||||
collection_name="chunks",
|
||||
points=[
|
||||
models.PointStruct(
|
||||
id=0,
|
||||
vector=[0.1, 0.2, 0.3, 0.4],
|
||||
payload={"document_id": 200},
|
||||
),
|
||||
models.PointStruct(
|
||||
id=1,
|
||||
vector=[0.5, 0.6, 0.7, 0.8],
|
||||
payload={"document_id": [200, 201]},
|
||||
),
|
||||
],
|
||||
)
|
||||
```
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
|
||||
use qdrant_client::Qdrant;
|
||||
use qdrant_client::qdrant::{PointStruct, UpsertPointsBuilder, Vector};
|
||||
|
||||
client
|
||||
.upsert_points(UpsertPointsBuilder::new(
|
||||
"documents",
|
||||
vec![
|
||||
PointStruct::new(
|
||||
200,
|
||||
HashMap::<String, Vector>::new(),
|
||||
[
|
||||
("title", "Document A".into()),
|
||||
("text", "This is document A".into()),
|
||||
],
|
||||
),
|
||||
PointStruct::new(
|
||||
201,
|
||||
HashMap::<String, Vector>::new(),
|
||||
[
|
||||
("title", "Document B".into()),
|
||||
("text", "This is document B".into()),
|
||||
],
|
||||
),
|
||||
],
|
||||
))
|
||||
.await?;
|
||||
|
||||
client
|
||||
.upsert_points(UpsertPointsBuilder::new(
|
||||
"chunks",
|
||||
vec![
|
||||
PointStruct::new(0, vec![0.1, 0.2, 0.3, 0.4], [("document_id", 200.into())]),
|
||||
PointStruct::new(1, vec![0.5, 0.6, 0.7, 0.8], [("document_id", vec![200, 201].into())]),
|
||||
],
|
||||
))
|
||||
.await?;
|
||||
```
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
```typescript
|
||||
await client.upsert("documents", {
|
||||
points: [
|
||||
{
|
||||
id: 200,
|
||||
vector: {},
|
||||
payload: { title: "Document A", text: "This is document A" },
|
||||
},
|
||||
{
|
||||
id: 201,
|
||||
vector: {},
|
||||
payload: { title: "Document B", text: "This is document B" },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await client.upsert("chunks", {
|
||||
points: [
|
||||
{
|
||||
id: 0,
|
||||
vector: [0.1, 0.2, 0.3, 0.4],
|
||||
payload: { document_id: 200 },
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
vector: [0.5, 0.6, 0.7, 0.8],
|
||||
payload: { document_id: [200, 201] },
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package snippet
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
if err != nil { panic(err) } // @hide
|
||||
|
||||
client.Upsert(context.Background(), &qdrant.UpsertPoints{
|
||||
CollectionName: "documents",
|
||||
Points: []*qdrant.PointStruct{
|
||||
{
|
||||
Id: qdrant.NewIDNum(200),
|
||||
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{}),
|
||||
Payload: qdrant.NewValueMap(map[string]any{
|
||||
"title": "Document A",
|
||||
"text": "This is document A",
|
||||
}),
|
||||
},
|
||||
{
|
||||
Id: qdrant.NewIDNum(201),
|
||||
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{}),
|
||||
Payload: qdrant.NewValueMap(map[string]any{
|
||||
"title": "Document B",
|
||||
"text": "This is document B",
|
||||
}),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
client.Upsert(context.Background(), &qdrant.UpsertPoints{
|
||||
CollectionName: "chunks",
|
||||
Points: []*qdrant.PointStruct{
|
||||
{
|
||||
Id: qdrant.NewIDNum(0),
|
||||
Vectors: qdrant.NewVectors(0.1, 0.2, 0.3, 0.4),
|
||||
Payload: qdrant.NewValueMap(map[string]any{"document_id": 200}),
|
||||
},
|
||||
{
|
||||
Id: qdrant.NewIDNum(1),
|
||||
Vectors: qdrant.NewVectors(0.5, 0.6, 0.7, 0.8),
|
||||
Payload: qdrant.NewValueMap(map[string]any{"document_id": []int{200, 201}}),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
```http
|
||||
PUT /collections/documents/points
|
||||
{
|
||||
"points": [
|
||||
{
|
||||
"id": 200,
|
||||
"vector": {},
|
||||
"payload": {"title": "Document A", "text": "This is document A"}
|
||||
},
|
||||
{
|
||||
"id": 201,
|
||||
"vector": {},
|
||||
"payload": {"title": "Document B", "text": "This is document B"}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
PUT /collections/chunks/points
|
||||
{
|
||||
"points": [
|
||||
{
|
||||
"id": 0,
|
||||
"vector": [0.1, 0.2, 0.3, 0.4],
|
||||
"payload": {"document_id": 200}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"vector": [0.5, 0.6, 0.7, 0.8],
|
||||
"payload": {"document_id": [200, 201]}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.example.snippets_amalgamation;
|
||||
|
||||
import static io.qdrant.client.PointIdFactory.id;
|
||||
import static io.qdrant.client.ValueFactory.list;
|
||||
import static io.qdrant.client.ValueFactory.value;
|
||||
import static io.qdrant.client.VectorsFactory.namedVectors;
|
||||
import static io.qdrant.client.VectorsFactory.vectors;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Points.PointStruct;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Snippet {
|
||||
public static void run() throws Exception {
|
||||
QdrantClient client = new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client.upsertAsync(
|
||||
"documents",
|
||||
List.of(
|
||||
PointStruct.newBuilder()
|
||||
.setId(id(200))
|
||||
.setVectors(namedVectors(Map.of()))
|
||||
.putAllPayload(Map.of(
|
||||
"title", value("Document A"),
|
||||
"text", value("This is document A")))
|
||||
.build(),
|
||||
PointStruct.newBuilder()
|
||||
.setId(id(201))
|
||||
.setVectors(namedVectors(Map.of()))
|
||||
.putAllPayload(Map.of(
|
||||
"title", value("Document B"),
|
||||
"text", value("This is document B")))
|
||||
.build())).get();
|
||||
|
||||
client.upsertAsync(
|
||||
"chunks",
|
||||
List.of(
|
||||
PointStruct.newBuilder()
|
||||
.setId(id(0))
|
||||
.setVectors(vectors(0.1f, 0.2f, 0.3f, 0.4f))
|
||||
.putAllPayload(Map.of("document_id", value(200)))
|
||||
.build(),
|
||||
PointStruct.newBuilder()
|
||||
.setId(id(1))
|
||||
.setVectors(vectors(0.5f, 0.6f, 0.7f, 0.8f))
|
||||
.putAllPayload(Map.of("document_id", list(List.of(value(200), value(201)))))
|
||||
.build())).get();
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
from qdrant_client import QdrantClient, models # @hide
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333") # @hide
|
||||
|
||||
client.upsert(
|
||||
collection_name="documents",
|
||||
points=[
|
||||
models.PointStruct(
|
||||
id=200,
|
||||
vector={},
|
||||
payload={"title": "Document A", "text": "This is document A"},
|
||||
),
|
||||
models.PointStruct(
|
||||
id=201,
|
||||
vector={},
|
||||
payload={"title": "Document B", "text": "This is document B"},
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
client.upsert(
|
||||
collection_name="chunks",
|
||||
points=[
|
||||
models.PointStruct(
|
||||
id=0,
|
||||
vector=[0.1, 0.2, 0.3, 0.4],
|
||||
payload={"document_id": 200},
|
||||
),
|
||||
models.PointStruct(
|
||||
id=1,
|
||||
vector=[0.5, 0.6, 0.7, 0.8],
|
||||
payload={"document_id": [200, 201]},
|
||||
),
|
||||
],
|
||||
)
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use qdrant_client::Qdrant;
|
||||
use qdrant_client::qdrant::{PointStruct, UpsertPointsBuilder, Vector};
|
||||
|
||||
pub async fn main() -> anyhow::Result<()> {
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?; // @hide
|
||||
|
||||
client
|
||||
.upsert_points(UpsertPointsBuilder::new(
|
||||
"documents",
|
||||
vec![
|
||||
PointStruct::new(
|
||||
200,
|
||||
HashMap::<String, Vector>::new(),
|
||||
[
|
||||
("title", "Document A".into()),
|
||||
("text", "This is document A".into()),
|
||||
],
|
||||
),
|
||||
PointStruct::new(
|
||||
201,
|
||||
HashMap::<String, Vector>::new(),
|
||||
[
|
||||
("title", "Document B".into()),
|
||||
("text", "This is document B".into()),
|
||||
],
|
||||
),
|
||||
],
|
||||
))
|
||||
.await?;
|
||||
|
||||
client
|
||||
.upsert_points(UpsertPointsBuilder::new(
|
||||
"chunks",
|
||||
vec![
|
||||
PointStruct::new(0, vec![0.1, 0.2, 0.3, 0.4], [("document_id", 200.into())]),
|
||||
PointStruct::new(1, vec![0.5, 0.6, 0.7, 0.8], [("document_id", vec![200, 201].into())]),
|
||||
],
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { QdrantClient } from "@qdrant/js-client-rest"; // @hide
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 }); // @hide
|
||||
|
||||
await client.upsert("documents", {
|
||||
points: [
|
||||
{
|
||||
id: 200,
|
||||
vector: {},
|
||||
payload: { title: "Document A", text: "This is document A" },
|
||||
},
|
||||
{
|
||||
id: 201,
|
||||
vector: {},
|
||||
payload: { title: "Document B", text: "This is document B" },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await client.upsert("chunks", {
|
||||
points: [
|
||||
{
|
||||
id: 0,
|
||||
vector: [0.1, 0.2, 0.3, 0.4],
|
||||
payload: { document_id: 200 },
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
vector: [0.5, 0.6, 0.7, 0.8],
|
||||
payload: { document_id: [200, 201] },
|
||||
},
|
||||
],
|
||||
});
|
||||
+1
@@ -0,0 +1 @@
|
||||
Create the two collections used by the `with_lookup` pattern. The `chunks` collection holds one point per chunk with its own vector. The `documents` collection holds one point per document and carries only payload, no vectors. A payload index on `document_id` is required for `group_by` to work on that field.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
public class Snippet
|
||||
{
|
||||
public static async Task Run()
|
||||
{
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateCollectionAsync(
|
||||
collectionName: "chunks",
|
||||
vectorsConfig: new VectorParams { Size = 4, Distance = Distance.Cosine }
|
||||
);
|
||||
|
||||
await client.CreatePayloadIndexAsync(
|
||||
collectionName: "chunks",
|
||||
fieldName: "document_id",
|
||||
schemaType: PayloadSchemaType.Integer
|
||||
);
|
||||
|
||||
// No vectors, payload only.
|
||||
await client.CreateCollectionAsync(
|
||||
collectionName: "documents",
|
||||
vectorsConfig: new VectorParamsMap()
|
||||
);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateCollectionAsync(
|
||||
collectionName: "chunks",
|
||||
vectorsConfig: new VectorParams { Size = 4, Distance = Distance.Cosine }
|
||||
);
|
||||
|
||||
await client.CreatePayloadIndexAsync(
|
||||
collectionName: "chunks",
|
||||
fieldName: "document_id",
|
||||
schemaType: PayloadSchemaType.Integer
|
||||
);
|
||||
|
||||
// No vectors, payload only.
|
||||
await client.CreateCollectionAsync(
|
||||
collectionName: "documents",
|
||||
vectorsConfig: new VectorParamsMap()
|
||||
);
|
||||
```
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
```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: "chunks",
|
||||
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
|
||||
Size: 4,
|
||||
Distance: qdrant.Distance_Cosine,
|
||||
}),
|
||||
})
|
||||
|
||||
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
|
||||
CollectionName: "chunks",
|
||||
FieldName: "document_id",
|
||||
FieldType: qdrant.FieldType_FieldTypeInteger.Enum(),
|
||||
})
|
||||
|
||||
// No vectors, payload only.
|
||||
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
|
||||
CollectionName: "documents",
|
||||
VectorsConfig: qdrant.NewVectorsConfigMap(
|
||||
map[string]*qdrant.VectorParams{},
|
||||
),
|
||||
})
|
||||
```
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.CreateCollection;
|
||||
import io.qdrant.client.grpc.Collections.Distance;
|
||||
import io.qdrant.client.grpc.Collections.PayloadSchemaType;
|
||||
import io.qdrant.client.grpc.Collections.VectorParams;
|
||||
import io.qdrant.client.grpc.Collections.VectorParamsMap;
|
||||
import io.qdrant.client.grpc.Collections.VectorsConfig;
|
||||
|
||||
QdrantClient client = new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client.createCollectionAsync("chunks",
|
||||
VectorParams.newBuilder().setDistance(Distance.Cosine).setSize(4).build()).get();
|
||||
|
||||
client.createPayloadIndexAsync(
|
||||
"chunks",
|
||||
"document_id",
|
||||
PayloadSchemaType.Integer,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null).get();
|
||||
|
||||
// No vectors, payload only.
|
||||
client.createCollectionAsync(
|
||||
CreateCollection.newBuilder()
|
||||
.setCollectionName("documents")
|
||||
.setVectorsConfig(VectorsConfig.newBuilder()
|
||||
.setParamsMap(VectorParamsMap.newBuilder().build())
|
||||
.build())
|
||||
.build()).get();
|
||||
```
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
```python
|
||||
client.create_collection(
|
||||
collection_name="chunks",
|
||||
vectors_config=models.VectorParams(size=4, distance=models.Distance.COSINE),
|
||||
)
|
||||
|
||||
client.create_payload_index(
|
||||
collection_name="chunks",
|
||||
field_name="document_id",
|
||||
field_schema=models.PayloadSchemaType.INTEGER,
|
||||
)
|
||||
|
||||
client.create_collection(
|
||||
collection_name="documents",
|
||||
vectors_config={}, # no vectors, payload only
|
||||
)
|
||||
```
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
```rust
|
||||
use qdrant_client::Qdrant;
|
||||
use qdrant_client::qdrant::{
|
||||
CreateCollectionBuilder, CreateFieldIndexCollectionBuilder, Distance, FieldType,
|
||||
VectorParamsBuilder, VectorsConfigBuilder,
|
||||
};
|
||||
|
||||
client
|
||||
.create_collection(
|
||||
CreateCollectionBuilder::new("chunks")
|
||||
.vectors_config(VectorParamsBuilder::new(4, Distance::Cosine)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
client
|
||||
.create_field_index(
|
||||
CreateFieldIndexCollectionBuilder::new("chunks", "document_id", FieldType::Integer)
|
||||
.wait(true),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// No vectors, payload only.
|
||||
client
|
||||
.create_collection(
|
||||
CreateCollectionBuilder::new("documents")
|
||||
.vectors_config(VectorsConfigBuilder::default()),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
```typescript
|
||||
await client.createCollection("chunks", {
|
||||
vectors: { size: 4, distance: "Cosine" },
|
||||
});
|
||||
|
||||
await client.createPayloadIndex("chunks", {
|
||||
field_name: "document_id",
|
||||
field_schema: "integer",
|
||||
});
|
||||
|
||||
await client.createCollection("documents", {
|
||||
vectors: {}, // no vectors, payload only
|
||||
});
|
||||
```
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package snippet
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
if err != nil { panic(err) } // @hide
|
||||
|
||||
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
|
||||
CollectionName: "chunks",
|
||||
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
|
||||
Size: 4,
|
||||
Distance: qdrant.Distance_Cosine,
|
||||
}),
|
||||
})
|
||||
|
||||
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
|
||||
CollectionName: "chunks",
|
||||
FieldName: "document_id",
|
||||
FieldType: qdrant.FieldType_FieldTypeInteger.Enum(),
|
||||
})
|
||||
|
||||
// No vectors, payload only.
|
||||
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
|
||||
CollectionName: "documents",
|
||||
VectorsConfig: qdrant.NewVectorsConfigMap(
|
||||
map[string]*qdrant.VectorParams{},
|
||||
),
|
||||
})
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
```http
|
||||
PUT /collections/chunks
|
||||
{
|
||||
"vectors": {
|
||||
"size": 4,
|
||||
"distance": "Cosine"
|
||||
}
|
||||
}
|
||||
|
||||
PUT /collections/chunks/index
|
||||
{
|
||||
"field_name": "document_id",
|
||||
"field_schema": "integer"
|
||||
}
|
||||
|
||||
PUT /collections/documents
|
||||
{
|
||||
"vectors": {}
|
||||
}
|
||||
```
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.example.snippets_amalgamation;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.CreateCollection;
|
||||
import io.qdrant.client.grpc.Collections.Distance;
|
||||
import io.qdrant.client.grpc.Collections.PayloadSchemaType;
|
||||
import io.qdrant.client.grpc.Collections.VectorParams;
|
||||
import io.qdrant.client.grpc.Collections.VectorParamsMap;
|
||||
import io.qdrant.client.grpc.Collections.VectorsConfig;
|
||||
|
||||
public class Snippet {
|
||||
public static void run() throws Exception {
|
||||
QdrantClient client = new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client.createCollectionAsync("chunks",
|
||||
VectorParams.newBuilder().setDistance(Distance.Cosine).setSize(4).build()).get();
|
||||
|
||||
client.createPayloadIndexAsync(
|
||||
"chunks",
|
||||
"document_id",
|
||||
PayloadSchemaType.Integer,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null).get();
|
||||
|
||||
// No vectors, payload only.
|
||||
client.createCollectionAsync(
|
||||
CreateCollection.newBuilder()
|
||||
.setCollectionName("documents")
|
||||
.setVectorsConfig(VectorsConfig.newBuilder()
|
||||
.setParamsMap(VectorParamsMap.newBuilder().build())
|
||||
.build())
|
||||
.build()).get();
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
from qdrant_client import QdrantClient, models # @hide
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333") # @hide
|
||||
|
||||
client.create_collection(
|
||||
collection_name="chunks",
|
||||
vectors_config=models.VectorParams(size=4, distance=models.Distance.COSINE),
|
||||
)
|
||||
|
||||
client.create_payload_index(
|
||||
collection_name="chunks",
|
||||
field_name="document_id",
|
||||
field_schema=models.PayloadSchemaType.INTEGER,
|
||||
)
|
||||
|
||||
client.create_collection(
|
||||
collection_name="documents",
|
||||
vectors_config={}, # no vectors, payload only
|
||||
)
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
use qdrant_client::Qdrant;
|
||||
use qdrant_client::qdrant::{
|
||||
CreateCollectionBuilder, CreateFieldIndexCollectionBuilder, Distance, FieldType,
|
||||
VectorParamsBuilder, VectorsConfigBuilder,
|
||||
};
|
||||
|
||||
pub async fn main() -> anyhow::Result<()> {
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?; // @hide
|
||||
|
||||
client
|
||||
.create_collection(
|
||||
CreateCollectionBuilder::new("chunks")
|
||||
.vectors_config(VectorParamsBuilder::new(4, Distance::Cosine)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
client
|
||||
.create_field_index(
|
||||
CreateFieldIndexCollectionBuilder::new("chunks", "document_id", FieldType::Integer)
|
||||
.wait(true),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// No vectors, payload only.
|
||||
client
|
||||
.create_collection(
|
||||
CreateCollectionBuilder::new("documents")
|
||||
.vectors_config(VectorsConfigBuilder::default()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { QdrantClient } from "@qdrant/js-client-rest"; // @hide
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 }); // @hide
|
||||
|
||||
await client.createCollection("chunks", {
|
||||
vectors: { size: 4, distance: "Cosine" },
|
||||
});
|
||||
|
||||
await client.createPayloadIndex("chunks", {
|
||||
field_name: "document_id",
|
||||
field_schema: "integer",
|
||||
});
|
||||
|
||||
await client.createCollection("documents", {
|
||||
vectors: {}, // no vectors, payload only
|
||||
});
|
||||
+3
-3
@@ -7,10 +7,10 @@ public class Snippet
|
||||
{
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.SearchGroupsAsync(
|
||||
collectionName: "{collection_name}",
|
||||
vector: new float[] { 0.2f, 0.1f, 0.9f, 0.7f},
|
||||
await client.QueryGroupsAsync(
|
||||
collectionName: "chunks",
|
||||
groupBy: "document_id",
|
||||
query: new float[] { 0.2f, 0.1f, 0.9f, 0.7f },
|
||||
limit: 2,
|
||||
groupSize: 2,
|
||||
withLookup: new WithLookup
|
||||
|
||||
+3
-3
@@ -4,10 +4,10 @@ using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.SearchGroupsAsync(
|
||||
collectionName: "{collection_name}",
|
||||
vector: new float[] { 0.2f, 0.1f, 0.9f, 0.7f},
|
||||
await client.QueryGroupsAsync(
|
||||
collectionName: "chunks",
|
||||
groupBy: "document_id",
|
||||
query: new float[] { 0.2f, 0.1f, 0.9f, 0.7f },
|
||||
limit: 2,
|
||||
groupSize: 2,
|
||||
withLookup: new WithLookup
|
||||
|
||||
+2
-1
@@ -11,9 +11,10 @@ client, err := qdrant.NewClient(&qdrant.Config{
|
||||
})
|
||||
|
||||
client.QueryGroups(context.Background(), &qdrant.QueryPointGroups{
|
||||
CollectionName: "{collection_name}",
|
||||
CollectionName: "chunks",
|
||||
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
|
||||
GroupBy: "document_id",
|
||||
Limit: qdrant.PtrOf(uint64(2)),
|
||||
GroupSize: qdrant.PtrOf(uint64(2)),
|
||||
WithLookup: &qdrant.WithLookup{
|
||||
Collection: "documents",
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import java.util.List;
|
||||
|
||||
client.queryGroupsAsync(
|
||||
QueryPointGroups.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setCollectionName("chunks")
|
||||
.setQuery(nearest(0.2f, 0.1f, 0.9f, 0.7f))
|
||||
.setGroupBy("document_id")
|
||||
.setLimit(2)
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
client.query_points_groups(
|
||||
collection_name="chunks",
|
||||
# Same as in the regular search() API
|
||||
query=[1.1],
|
||||
query=[0.2, 0.1, 0.9, 0.7],
|
||||
# Grouping parameters
|
||||
group_by="document_id", # Path of the field to group by
|
||||
limit=2, # Max amount of groups
|
||||
@@ -15,7 +15,7 @@ client.query_points_groups(
|
||||
# of the looked up point, True by default
|
||||
with_payload=["title", "text"],
|
||||
# Options for specifying what to bring from the vector(s)
|
||||
# of the looked up point, True by default
|
||||
# of the looked up point, False by default
|
||||
with_vectors=False,
|
||||
),
|
||||
)
|
||||
|
||||
+2
-2
@@ -3,10 +3,10 @@ use qdrant_client::qdrant::{with_payload_selector::SelectorOptions, QueryPointGr
|
||||
|
||||
client
|
||||
.query_groups(
|
||||
QueryPointGroupsBuilder::new("{collection_name}", "document_id")
|
||||
QueryPointGroupsBuilder::new("chunks", "document_id")
|
||||
.query(vec![0.2, 0.1, 0.9, 0.7])
|
||||
.limit(2u64)
|
||||
.limit(2u64)
|
||||
.group_size(2u64)
|
||||
.with_lookup(
|
||||
WithLookupBuilder::new("documents")
|
||||
.with_payload(SelectorOptions::Include(
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
```typescript
|
||||
client.queryGroups("{collection_name}", {
|
||||
query: [1.1],
|
||||
client.queryGroups("chunks", {
|
||||
query: [0.2, 0.1, 0.9, 0.7],
|
||||
group_by: "document_id",
|
||||
limit: 2,
|
||||
group_size: 2,
|
||||
|
||||
+2
-1
@@ -15,9 +15,10 @@ func Main() {
|
||||
if err != nil { panic(err) } // @hide
|
||||
|
||||
client.QueryGroups(context.Background(), &qdrant.QueryPointGroups{
|
||||
CollectionName: "{collection_name}",
|
||||
CollectionName: "chunks",
|
||||
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
|
||||
GroupBy: "document_id",
|
||||
Limit: qdrant.PtrOf(uint64(2)),
|
||||
GroupSize: qdrant.PtrOf(uint64(2)),
|
||||
WithLookup: &qdrant.WithLookup{
|
||||
Collection: "documents",
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
POST /collections/chunks/points/query/groups
|
||||
{
|
||||
// Same as in the regular query API
|
||||
"query": [1.1],
|
||||
"query": [0.2, 0.1, 0.9, 0.7],
|
||||
|
||||
// Grouping parameters
|
||||
"group_by": "document_id",
|
||||
@@ -19,7 +19,7 @@ POST /collections/chunks/points/query/groups
|
||||
"with_payload": ["title", "text"],
|
||||
|
||||
// Options for specifying what to bring from the vector(s)
|
||||
// of the looked up point, true by default
|
||||
// of the looked up point, false by default
|
||||
"with_vectors": false
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class Snippet {
|
||||
|
||||
client.queryGroupsAsync(
|
||||
QueryPointGroups.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setCollectionName("chunks")
|
||||
.setQuery(nearest(0.2f, 0.1f, 0.9f, 0.7f))
|
||||
.setGroupBy("document_id")
|
||||
.setLimit(2)
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ client = QdrantClient(url="http://localhost:6333") # @hide
|
||||
client.query_points_groups(
|
||||
collection_name="chunks",
|
||||
# Same as in the regular search() API
|
||||
query=[1.1],
|
||||
query=[0.2, 0.1, 0.9, 0.7],
|
||||
# Grouping parameters
|
||||
group_by="document_id", # Path of the field to group by
|
||||
limit=2, # Max amount of groups
|
||||
@@ -18,7 +18,7 @@ client.query_points_groups(
|
||||
# of the looked up point, True by default
|
||||
with_payload=["title", "text"],
|
||||
# Options for specifying what to bring from the vector(s)
|
||||
# of the looked up point, True by default
|
||||
# of the looked up point, False by default
|
||||
with_vectors=False,
|
||||
),
|
||||
)
|
||||
|
||||
+2
-2
@@ -5,10 +5,10 @@ pub async fn main() -> anyhow::Result<()> {
|
||||
|
||||
client
|
||||
.query_groups(
|
||||
QueryPointGroupsBuilder::new("{collection_name}", "document_id")
|
||||
QueryPointGroupsBuilder::new("chunks", "document_id")
|
||||
.query(vec![0.2, 0.1, 0.9, 0.7])
|
||||
.limit(2u64)
|
||||
.limit(2u64)
|
||||
.group_size(2u64)
|
||||
.with_lookup(
|
||||
WithLookupBuilder::new("documents")
|
||||
.with_payload(SelectorOptions::Include(
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ import { QdrantClient } from "@qdrant/js-client-rest"; // @hide
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 }); // @hide
|
||||
|
||||
client.queryGroups("{collection_name}", {
|
||||
query: [1.1],
|
||||
client.queryGroups("chunks", {
|
||||
query: [0.2, 0.1, 0.9, 0.7],
|
||||
group_by: "document_id",
|
||||
limit: 2,
|
||||
group_size: 2,
|
||||
|
||||
@@ -404,38 +404,48 @@ If the `group_by` field of a point is an array (e.g. `"document_id": ["a", "b"]`
|
||||
|
||||
### Lookup in groups
|
||||
|
||||
Having multiple points for parts of the same item often introduces redundancy in the stored data. Which may be fine if the information shared by the points is small, but it can become a problem if the payload is large, because it multiplies the storage space needed to store the points by a factor of the amount of points we have per group.
|
||||
When the points in a group share large fields like titles, abstracts, or full document vectors, copying that data onto every point inflates storage and forces you to rewrite every chunk whenever a shared field changes.
|
||||
|
||||
One way of optimizing storage when using groups is to store the information shared by the points with the same group id in a single point in another collection. Then, when using the [**groups** API](#grouping-api), add the `with_lookup` parameter to bring the information from those points into each group.
|
||||
`with_lookup` solves this. Store the shared data once in a separate collection, then attach it to each group at query time using the [groups API](#grouping-api).
|
||||
|
||||

|
||||
|
||||
<aside role="status">Store only document-level metadata (e.g., titles, abstracts) in the lookup collection, not chunks or duplicated data.</aside>
|
||||
|
||||
This has the extra benefit of having a single point to update when the information shared by the points in a group changes.
|
||||
Set up the two collections. `chunks` holds one point per chunk with its own vector. `documents` holds one point per document with payload only. The payload index on `document_id` lets `group_by` work on that field.
|
||||
|
||||
For example, if you have a collection of documents, you may want to chunk them and store the points for the chunks in a separate collection, making sure that you store the point id from the document it belongs in the payload of the chunk point.
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-groups/with-lookup-setup/" >}}
|
||||
|
||||
In this case, to bring the information from the documents into the chunks grouped by the document id, you can use the `with_lookup` parameter:
|
||||
Ingest both collections, with `documents` populated before any query that uses `with_lookup`.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-groups/with-lookup-ingest/" >}}
|
||||
|
||||
The lookup is a plain join by point id, not a vector search:
|
||||
|
||||
- The `documents` collection must already contain a point whose id matches the `group_by` value from each chunk.
|
||||
- The id types must match. A string `group_by` value against integer point ids returns an empty `lookup` field with no error.
|
||||
- Group ids without a matching point in `documents` get an empty `lookup` field.
|
||||
|
||||
Query the `chunks` collection with `group_by="document_id"` and `with_lookup` pointing at the `documents` collection:
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-groups/with-lookup/" >}}
|
||||
|
||||
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.
|
||||
You can also pass `with_lookup="documents"` as a shorthand. It uses the server defaults (`with_payload=True`, `with_vectors=False`), so the documents' vectors are not returned. Use the explicit `WithLookup(...)` form when you need those vectors back.
|
||||
|
||||
The looked up result will show up under `lookup` in each group.
|
||||
The looked-up result appears under `lookup` in each group. Below, chunk id 1 shows up in both groups because its `document_id` payload is an array (`[200, 201]`), placing the chunk into every matching group.
|
||||
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"id": 200,
|
||||
"hits": [
|
||||
{ "id": 0, "score": 0.91 },
|
||||
{ "id": 1, "score": 0.85 }
|
||||
],
|
||||
"lookup": {
|
||||
"id": 1,
|
||||
"id": 200,
|
||||
"payload": {
|
||||
"title": "Document A",
|
||||
"text": "This is document A"
|
||||
@@ -443,12 +453,12 @@ The looked up result will show up under `lookup` in each group.
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"id": 201,
|
||||
"hits": [
|
||||
{ "id": 1, "score": 0.85 }
|
||||
],
|
||||
"lookup": {
|
||||
"id": 2,
|
||||
"id": 201,
|
||||
"payload": {
|
||||
"title": "Document B",
|
||||
"text": "This is document B"
|
||||
@@ -462,9 +472,7 @@ The looked up result will show up under `lookup` in each group.
|
||||
}
|
||||
```
|
||||
|
||||
Since the lookup is done by matching directly with the point id, the lookup collection must be pre-populated with points where the `id` matches the `group_by` value (e.g., document_id) from your primary collection.
|
||||
|
||||
Any group id that is not an existing (and valid) point id in the lookup collection will be ignored, and the `lookup` field will be empty.
|
||||
For a collection of 20 000 documents with around 24 chunks each, duplicating ~3 KB of document-level data on every chunk adds up to ~1.4 GB. Storing those fields once per document in the `documents` collection brings that down to ~60 MB. The split pays off when the shared fields are large or change often, since updating one point in `documents` shows up under every chunk's group at query time.
|
||||
|
||||
## Random Sampling
|
||||
|
||||
|
||||
Reference in New Issue
Block a user