Skip to content

Commit e55d961

Browse files
abdonpijpelinkAnush008timviseeIvanPleshkovjoein
authored
Docs for update_mode (#2097)
* Docs for update_mode * docs: C#, Go, Java snippets Signed-off-by: Anush008 <mail@anush.sh> * doc: Remove _ from C# snippet Signed-off-by: Anush008 <mail@anush.sh> * Add Rust snippet * Fix some snippets * Review feedback * ts snippets * new: add python snippets * fix: add generated python.md --------- Signed-off-by: Anush008 <mail@anush.sh> Co-authored-by: Anush008 <mail@anush.sh> Co-authored-by: timvisee <tim@visee.me> Co-authored-by: Ivan Pleshkov <pleshkov.ivan@gmail.com> Co-authored-by: George Panchuk <george.panchuk@qdrant.tech>
1 parent 2593ffc commit e55d961

File tree

17 files changed

+445
-11
lines changed

17 files changed

+445
-11
lines changed

qdrant-landing/content/documentation/concepts/points.md

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,29 @@ In this case, it means that points with the same id will be overwritten when re-
164164
Idempotence property is useful if you use, for example, a message queue that doesn't provide an exactly-once guarantee.
165165
Even with such a system, Qdrant ensures data consistency.
166166

167+
### Update Mode
168+
169+
_Available as of v1.17.0_
170+
171+
By default, an upsert operation inserts a point if it does not exist, or updates it if it does. To change this behavior, use the `update_mode` parameter:
172+
173+
- `upsert` (default): Insert a point if it does not exist, or update it if it does.
174+
- `insert_only`: Insert a point only if it does not already exist. If a point with the same ID exists, the operation is ignored.
175+
- `update_only`: Update a point only if it already exists. Points that do not exist are not inserted.
176+
177+
For example, to use `insert_only` mode:
178+
179+
{{< code-snippet path="/documentation/headless/snippets/insert-points/update-mode/" >}}
180+
181+
`insert_only` mode is especially useful when [migrating from one embedding model to another](/documentation/database-tutorials/embedding-model-migration/), where conflicts between regular updates and background re-embedding tasks need to be resolved.
182+
183+
{{< figure src="/docs/embedding-model-migration.png" caption="Embedding model migration in blue-green deployment" width="80%" >}}
184+
185+
`update_only` mode is useful with [conditional updates](#conditional-updates). Because upserts default to inserts for non-existing points, a conditional update without an explicit `update_mode` will insert a new point even if the condition is not met, which is not the intended behavior in most cases.
186+
167187
### Named vectors
168188

169-
[_Available as of v0.10.0_](#create-vector-name)
189+
_Available as of v0.10.0_
170190

171191
If the collection was created with multiple vectors, each vector data can be provided using the vector's name:
172192

@@ -291,6 +311,8 @@ All update operations (including point insertion, vector updates, payload update
291311

292312
{{< code-snippet path="/documentation/headless/snippets/insert-points/with-condition/" >}}
293313

314+
<aside role="alert">By default, a conditional update on a non-existent point behaves as a regular upsert, inserting the point regardless of the filter. This is undesirable in most cases. To ensure that only existing points that meet the condition are updated, <a href="#update-mode">set <code>update_mode</code></a> to <code>update_only</code>.</aside>
315+
294316
While conditional payload modification and deletion covers the use-case of mass data modification, conditional point insertion and vector updates are particularly useful for implementing optimistic concurrency control in distributed systems.
295317

296318
A common scenario for such mechanism is when multiple clients try to update the same point independently.
@@ -309,10 +331,6 @@ If Client B tries to write back its changes later, the condition would fail (as
309331

310332
Instead of `version`, applications can use timestamps (assuming synchronized clocks) or any other monotonically increasing value that fits their data model.
311333

312-
This mechanism is especially useful in the scenarios of embedding model migration, where we need to resolve conflicts between regular application updates and background re-embedding tasks.
313-
314-
{{< figure src="/docs/embedding-model-migration.png" caption="Embedding model migration in blue-green deployment" width="80%" >}}
315-
316334
## Retrieve points
317335

318336
There is a method for retrieving points by their ids.

qdrant-landing/content/documentation/headless/snippets/insert-points/list-of-points-simple/generated/rust.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ client
66
UpsertPointsBuilder::new(
77
"{collection_name}",
88
vec![
9-
PointStruct::new(1, vec![0.9, 0.1, 0.1], [("city", "red".into())]),
10-
PointStruct::new(2, vec![0.1, 0.9, 0.1], [("city", "green".into())]),
11-
PointStruct::new(3, vec![0.1, 0.1, 0.9], [("city", "blue".into())]),
9+
PointStruct::new(1, vec![0.9, 0.1, 0.1], [("color", "red".into())]),
10+
PointStruct::new(2, vec![0.1, 0.9, 0.1], [("color", "green".into())]),
11+
PointStruct::new(3, vec![0.1, 0.1, 0.9], [("color", "blue".into())]),
1212
],
1313
)
1414
.wait(true),

qdrant-landing/content/documentation/headless/snippets/insert-points/list-of-points-simple/rust.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ pub async fn main() -> anyhow::Result<()> {
88
UpsertPointsBuilder::new(
99
"{collection_name}",
1010
vec![
11-
PointStruct::new(1, vec![0.9, 0.1, 0.1], [("city", "red".into())]),
12-
PointStruct::new(2, vec![0.1, 0.9, 0.1], [("city", "green".into())]),
13-
PointStruct::new(3, vec![0.1, 0.1, 0.9], [("city", "blue".into())]),
11+
PointStruct::new(1, vec![0.9, 0.1, 0.1], [("color", "red".into())]),
12+
PointStruct::new(2, vec![0.1, 0.9, 0.1], [("color", "green".into())]),
13+
PointStruct::new(3, vec![0.1, 0.1, 0.9], [("color", "blue".into())]),
1414
],
1515
)
1616
.wait(true),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Shows how to set the update mode when upserting points into a collection. Update mode is set to `insert_only` in this example, which means that only new points will be inserted; existing points with the same IDs will be ignored. Other valid values for `update_mode` are `upsert` (default) and `update_only`.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Qdrant.Client;
2+
using Qdrant.Client.Grpc;
3+
4+
public class Snippet
5+
{
6+
public static async Task Run()
7+
{
8+
var client = new QdrantClient("localhost", 6334);
9+
10+
await client.UpsertAsync(
11+
new()
12+
{
13+
CollectionName = "{collection_name}",
14+
Points =
15+
{
16+
new List<PointStruct>
17+
{
18+
new()
19+
{
20+
Id = 1,
21+
Vectors = new[] { 0.9f, 0.1f, 0.1f },
22+
Payload = { ["color"] = "red" },
23+
},
24+
new()
25+
{
26+
Id = 2,
27+
Vectors = new[] { 0.1f, 0.9f, 0.1f },
28+
Payload = { ["color"] = "green" },
29+
},
30+
new()
31+
{
32+
Id = 3,
33+
Vectors = new[] { 0.1f, 0.1f, 0.9f },
34+
Payload = { ["color"] = "blue" },
35+
},
36+
},
37+
},
38+
UpdateMode = UpdateMode.InsertOnly,
39+
Wait = true,
40+
}
41+
);
42+
}
43+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
```csharp
2+
using Qdrant.Client;
3+
using Qdrant.Client.Grpc;
4+
5+
var client = new QdrantClient("localhost", 6334);
6+
7+
await client.UpsertAsync(
8+
new()
9+
{
10+
CollectionName = "{collection_name}",
11+
Points =
12+
{
13+
new List<PointStruct>
14+
{
15+
new()
16+
{
17+
Id = 1,
18+
Vectors = new[] { 0.9f, 0.1f, 0.1f },
19+
Payload = { ["color"] = "red" },
20+
},
21+
new()
22+
{
23+
Id = 2,
24+
Vectors = new[] { 0.1f, 0.9f, 0.1f },
25+
Payload = { ["color"] = "green" },
26+
},
27+
new()
28+
{
29+
Id = 3,
30+
Vectors = new[] { 0.1f, 0.1f, 0.9f },
31+
Payload = { ["color"] = "blue" },
32+
},
33+
},
34+
},
35+
UpdateMode = UpdateMode.InsertOnly,
36+
Wait = true,
37+
}
38+
);
39+
```
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
```go
2+
import (
3+
"context"
4+
5+
"github.com/qdrant/go-client/qdrant"
6+
)
7+
8+
client, err := qdrant.NewClient(&qdrant.Config{
9+
Host: "localhost",
10+
Port: 6334,
11+
})
12+
13+
client.Upsert(context.Background(), &qdrant.UpsertPoints{
14+
CollectionName: "{collection_name}",
15+
Points: []*qdrant.PointStruct{
16+
{
17+
Id: qdrant.NewIDNum(1),
18+
Vectors: qdrant.NewVectors(0.9, 0.1, 0.1),
19+
Payload: qdrant.NewValueMap(map[string]any{"color": "red"}),
20+
},
21+
{
22+
Id: qdrant.NewIDNum(2),
23+
Vectors: qdrant.NewVectors(0.1, 0.9, 0.1),
24+
Payload: qdrant.NewValueMap(map[string]any{"color": "green"}),
25+
},
26+
{
27+
Id: qdrant.NewIDNum(3),
28+
Vectors: qdrant.NewVectors(0.1, 0.1, 0.9),
29+
Payload: qdrant.NewValueMap(map[string]any{"color": "blue"}),
30+
},
31+
},
32+
UpdateMode: qdrant.UpdateMode_InsertOnly.Enum(),
33+
})
34+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
```java
2+
import static io.qdrant.client.PointIdFactory.id;
3+
import static io.qdrant.client.ValueFactory.value;
4+
import static io.qdrant.client.VectorsFactory.vectors;
5+
6+
import io.qdrant.client.QdrantClient;
7+
import io.qdrant.client.QdrantGrpcClient;
8+
import io.qdrant.client.grpc.Points.PointStruct;
9+
import io.qdrant.client.grpc.Points.UpdateMode;
10+
import io.qdrant.client.grpc.Points.UpsertPoints;
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
QdrantClient client =
15+
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
16+
17+
client
18+
.upsertAsync(
19+
UpsertPoints.newBuilder()
20+
.setCollectionName("{collection_name}")
21+
.addAllPoints(
22+
List.of(
23+
PointStruct.newBuilder()
24+
.setId(id(1))
25+
.setVectors(vectors(0.9f, 0.1f, 0.1f))
26+
.putAllPayload(Map.of("color", value("red")))
27+
.build(),
28+
PointStruct.newBuilder()
29+
.setId(id(2))
30+
.setVectors(vectors(0.1f, 0.9f, 0.1f))
31+
.putAllPayload(Map.of("color", value("green")))
32+
.build(),
33+
PointStruct.newBuilder()
34+
.setId(id(3))
35+
.setVectors(vectors(0.1f, 0.1f, 0.9f))
36+
.putAllPayload(Map.of("color", value("blue")))
37+
.build()))
38+
.setUpdateMode(UpdateMode.InsertOnly)
39+
.build())
40+
.get();
41+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
```python
2+
client.upsert(
3+
collection_name="{collection_name}",
4+
points=[
5+
models.PointStruct(
6+
id=1,
7+
vector=[0.9, 0.1, 0.1],
8+
payload={
9+
"color": "red",
10+
},
11+
),
12+
models.PointStruct(
13+
id=2,
14+
vector=[0.1, 0.9, 0.1],
15+
payload={
16+
"color": "green",
17+
},
18+
),
19+
models.PointStruct(
20+
id=3,
21+
vector=[0.1, 0.1, 0.9],
22+
payload={
23+
"color": "blue",
24+
},
25+
),
26+
],
27+
update_mode=models.UpdateMode.INSERT_ONLY
28+
)
29+
```
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
```rust
2+
use qdrant_client::qdrant::{PointStruct, UpdateMode, UpsertPointsBuilder};
3+
4+
client
5+
.upsert_points(
6+
UpsertPointsBuilder::new(
7+
"{collection_name}",
8+
vec![
9+
PointStruct::new(1, vec![0.9, 0.1, 0.1], [("color", "red".into())]),
10+
PointStruct::new(2, vec![0.1, 0.9, 0.1], [("color", "green".into())]),
11+
PointStruct::new(3, vec![0.1, 0.1, 0.9], [("color", "blue".into())]),
12+
],
13+
)
14+
.update_mode(UpdateMode::InsertOnly),
15+
)
16+
.await?;
17+
```

0 commit comments

Comments
 (0)