Операция «Upsert» с сущностями
Операция « upsert » предоставляет удобный способ вставки или обновления сущностей в коллекции.
Обзор
Вы можете использовать операцию « upsert » для вставки нового объекта или обновления существующего, в зависимости от того, существует ли в коллекции первичный ключ, указанный в запросе «upsert». Если первичный ключ не найден, выполняется операция вставки. В противном случае выполняется операция обновления. Частичные обновления в коллекциях типа « autoID » являются исключением: они обновляют только существующие объекты, как описано ниже.
Операция upsert в Milvus работает либо в режиме перезаписи (override), либо в режиме слияния (merge).
Операция «upsert» в режиме перезаписи
Запрос «upsert», работающий в режиме перезаписи, объединяет в себе операции вставки и удаления. При получении запроса « upsert » для существующего объекта Milvus вставляет данные, содержащиеся в полезной нагрузке запроса, и одновременно удаляет существующий объект с исходным первичным ключом, указанным в данных.
Upsert в режиме перезаписи
Если в целевой коллекции включена функция « autoID » для основного поля, запрос upsert все равно должен содержать первичный ключ целевого объекта. Milvus использует предоставленный первичный ключ для поиска объекта, который необходимо заменить, и генерирует новый первичный ключ для данных, содержащихся в поле запроса, перед их вставкой.
Поля, для которых включена функция « nullable », можно опустить в запросе ` upsert `, если они не требуют обновления.
Upsert в режиме слиянияCompatible with Milvus v2.6.2+
Используйте режим слияния (merge), чтобы обновить определённые поля существующего объекта, оставив остальные поля без изменений.
Upsert в режиме слияния
Укажите параметр « partial_update=True » и предоставьте первичный ключ, а также поля, которые необходимо обновить.
Milvus извлекает существующий объект с помощью запроса с жесткой согласованностью, объединяет ваши изменения с сохраненными данными, вставляет объединенный объект и удаляет старый объект.
Если первичный ключ отсутствует, результат зависит от того, включена ли опция « autoID »:
- Если функция «
autoID» отключена, Milvus пытается вставить новую сущность с указанным вами первичным ключом. Запрос выполняется успешно, если он соответствует обычным требованиям к вставке. Если отсутствует обязательное поле, запрос завершается с ошибкой отсутствия поля. Поля, допускающие значение null, и поля со значениями по умолчанию можно опустить, как и при обычной вставке. - Если функция «
autoID» включена, каждый первичный ключ в запросе должен уже существовать. Milvus отклоняет запрос, если отсутствует любой первичный ключ, даже если вы указали все поля, необходимые для вставки. Для существующих сущностей режим слияния сохраняет первичный ключ без изменений.
Если частичное обновление завершается с ошибкой отсутствующего поля, проверьте, существует ли целевая сущность. Без существующей сущности Milvus не может получить значения полей, которые вы пропустили.
Для новых сущностей используйте оператор « insert » или операцию upsert в режиме перезаписи. Для последующих обновлений отдельных полей используйте режим слияния.
Для полей типа « ARRAY » режим слияния в Milvus v2.6.17 и более поздних версиях поддерживает два оператора: ` ARRAY_APPEND ` и ` ARRAY_REMOVE`. Эти операторы позволяют добавлять элементы в существующее поле ` ARRAY ` или удалять из него совпадающие элементы без предварительного запроса к сущности для получения её текущего значения. Подробности см. в разделе «Upsert полей ARRAY в режиме слияния».
Поведение операции «Upsert»: особые замечания
Перед использованием функции слияния следует учесть несколько особых моментов. В следующих примерах предполагается, что у вас есть коллекция с двумя скалярными полями с именами title и issue, а также первичным ключом id и векторным полем vector.
Обработка полей с включенной функцией «
nullable».Предположим, что поле
issueможет принимать значение null. При выполнении операции upsert для этих полей обратите внимание на следующее:Если вы опустите поле
issueв запросеupsertи отключитеpartial_update, полеissueбудет обновлено до значенияnullвместо сохранения исходного значения.Чтобы сохранить исходное значение поля
issue, необходимо либо включить параметрpartial_updateи опустить полеissue, либо включить полеissueс его исходным значением в запросupsert.
Ключи для операции «Upsert» в динамическом поле.
Предположим, что вы включили динамический ключ в коллекции из примера, и пары «ключ-значение» в динамическом поле сущности выглядят примерно так:
{"author": "John", "year": 2020, "tags": ["fiction"]}.При выполнении операции upsert для сущности с ключами, такими как
author,yearилиtags, либо при добавлении других ключей, обратите внимание на следующее:Если вы выполняете операцию upsert с отключенным параметром «
partial_update», по умолчанию происходит перезапись. Это означает, что значение динамического поля будет перезаписано всеми полями, не определёнными в схеме, включёнными в запрос, и их значениями.Например, если данные, включённые в запрос, имеют вид
{"author": "Jane", "genre": "fantasy"}, пары «ключ-значение» в динамическом поле целевой сущности будут обновлены в соответствии с ними.Если вы выполняете операцию upsert с включенным параметром `
partial_update`, по умолчанию происходит слияние. Это означает, что значение динамического поля будет объединено со всеми полями, не определёнными схемой, включёнными в запрос, и их значениями.Например, если данные, включенные в запрос, имеют вид
{"author": "John", "year": 2020, "tags": ["fiction"]}, то после операции «upsert» пары «ключ-значение» в динамическом поле целевой сущности примут вид{"author": "John", "year": 2020, "tags": ["fiction"], "genre": "fantasy"}.
Операция upsert для поля JSON.
Предположим, что в примере коллекции имеется поле JSON, определённое схемой, с именем
extras, а пары «ключ-значение» в этом поле JSON сущности выглядят примерно так:{"author": "John", "year": 2020, "tags": ["fiction"]}.При выполнении операции «upsert» для поля
extrasсущности с измененными данными JSON обратите внимание, что поле JSON рассматривается как единое целое, и вы не можете выборочно обновлять отдельные ключи. Другими словами, поле JSON НЕ поддерживает операцию «upsert» в режиме слияния.Осуществление операции«upsert» для поля «
ARRAY».По умолчанию поле
ARRAYв режиме слияния следует семантике REPLACE: значение, переданное в запросе, перезаписывает существующий массив. Для более тонкого управления обновлениями Milvus v2.6.17 и более поздние версии также поддерживают два оператора:ARRAY_APPENDдобавляет элементы из полезных данных запроса к существующему массиву.ARRAY_REMOVEудаляет из существующего массива каждый элемент, значение которого совпадает со значением в полезной нагрузке запроса.
Синтаксис операторов, поддерживаемые типы элементов и другие ограничения см. в разделе «Upsert полей ARRAY в режиме слияния».
Upsert поля StructArray.
Операция «upsert» поля StructArray в сущности перезаписывает значение поля. Для этого необходимо предоставить список словарей, каждый из которых содержит все подполя, определённые в схеме структуры, даже при выполнении операции «upsert» в режиме слияния.
Подробности см. в разделе «Upsert-операция с полем StructArray в режиме слияния».
Ограничения и запреты
Исходя из вышеизложенного, необходимо соблюдать следующие ограничения:
Запрос «
upsert» всегда должен содержать первичные ключи целевых сущностей, даже если включена опция «autoID». Для коллекций типа «autoID» обработка первичных ключей зависит от режима обновления:В режиме перезаписи первичный ключ идентифицирует существующий объект, который будет заменен, а Milvus генерирует новый первичный ключ для заменяющего объекта.
В режиме слияния первичный ключ идентифицирует существующий объект, который необходимо обновить, и остается неизменным. Если первичный ключ не существует, запрос завершается сбоем вместо вставки нового объекта.
Целевая коллекция должна быть загружена и доступна для запросов.
Все поля, указанные в запросе, должны присутствовать в схеме целевой коллекции.
Значения всех полей, указанных в запросе, должны соответствовать типам данных, определённым в схеме.
Для любого поля, полученного из другого с помощью функций, Milvus удалит производственное поле во время операции upsert, чтобы обеспечить возможность пересчёта.
Операция upsert сущностей в коллекции
В этом разделе мы будем выполнять операцию upsert сущностей в коллекцию с именем « my_collection ». Эта коллекция содержит только два поля: « id », « vector », « title » и « issue ». Поле « id » является первичным, а поля « title » и « issue » — скалярными.
Эти три сущности, если они присутствуют в коллекции, будут перезаписаны теми, которые включены в запрос upsert.
from pymilvus import MilvusClient
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
data=[
{
"id": 0,
"vector": [-0.619954382375778, 0.4479436794798608, -0.17493894838751745, -0.4248030059917294, -0.8648452746018911],
"title": "Artificial Intelligence in Real Life",
"issue": "vol.12"
}, {
"id": 1,
"vector": [0.4762662251462588, -0.6942502138717026, -0.4490002642657902, -0.628696575798281, 0.9660395877041965],
"title": "Hollow Man",
"issue": "vol.19"
}, {
"id": 2,
"vector": [-0.8864122635045097, 0.9260170474445351, 0.801326976181461, 0.6383943392381306, 0.7563037341572827],
"title": "Treasure Hunt in Missouri",
"issue": "vol.12"
}
]
res = client.upsert(
collection_name='my_collection',
data=data
)
print(res)
# Output
# {'upsert_count': 3}
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.UpsertReq;
import io.milvus.v2.service.vector.response.UpsertResp;
import java.util.*;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.token("root:Milvus")
.build());
Gson gson = new Gson();
List<JsonObject> data = Arrays.asList(
gson.fromJson("{\"id\": 0, \"vector\": [-0.619954382375778, 0.4479436794798608, -0.17493894838751745, -0.4248030059917294, -0.8648452746018911], \"title\": \"Artificial Intelligence in Real Life\", \"issue\": \"\vol.12\"}", JsonObject.class),
gson.fromJson("{\"id\": 1, \"vector\": [0.4762662251462588, -0.6942502138717026, -0.4490002642657902, -0.628696575798281, 0.9660395877041965], \"title\": \"Hollow Man\", \"issue\": \"vol.19\"}", JsonObject.class),
gson.fromJson("{\"id\": 2, \"vector\": [-0.8864122635045097, 0.9260170474445351, 0.801326976181461, 0.6383943392381306, 0.7563037341572827], \"title\": \"Treasure Hunt in Missouri\", \"issue\": \"vol.12\"}", JsonObject.class),
);
UpsertReq upsertReq = UpsertReq.builder()
.collectionName("my_collection")
.data(data)
.build();
UpsertResp upsertResp = client.upsert(upsertReq);
System.out.println(upsertResp);
// Output:
//
// UpsertResp(upsertCnt=3)
const { MilvusClient, DataType } = require("@zilliz/milvus2-sdk-node")
const address = "http://localhost:19530";
const token = "root:Milvus";
const client = new MilvusClient({address, token});
data = [
{id: 0, vector: [-0.619954382375778, 0.4479436794798608, -0.17493894838751745, -0.4248030059917294, -0.8648452746018911], title: "Artificial Intelligence in Real Life", issue: "vol.12"},
{id: 1, vector: [0.4762662251462588, -0.6942502138717026, -0.4490002642657902, -0.628696575798281, 0.9660395877041965], title: "Hollow Man", issue: "vol.19"},
{id: 2, vector: [-0.8864122635045097, 0.9260170474445351, 0.801326976181461, 0.6383943392381306, 0.7563037341572827], title: "Treasure Hunt in Missouri", issue: "vol.12"},
]
res = await client.upsert({
collection_name: "my_collection",
data: data,
})
console.log(res.upsert_cnt)
// Output
//
// 3
//
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/column"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
milvusAddr := "localhost:19530"
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: milvusAddr,
})
if err != nil {
fmt.Println(err.Error())
// handle error
}
defer client.Close(ctx)
titleColumn := column.NewColumnString("title", []string{
"Artificial Intelligence in Real Life", "Hollow Man", "Treasure Hunt in Missouri",
})
issueColumn := column.NewColumnString("issue", []string{
"vol.12", "vol.19", "vol.12"
})
_, err = client.Upsert(ctx, milvusclient.NewColumnBasedInsertOption("my_collection").
WithInt64Column("id", []int64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}).
WithFloatVectorColumn("vector", 5, [][]float32{
{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592},
{0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104},
{0.43742130801983836, -0.5597502546264526, 0.6457887650909682, 0.7894058910881185, 0.20785793220625592},
}).
WithColumns(titleColumn, issueColumn),
)
if err != nil {
fmt.Println(err.Error())
// handle err
}
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/upsert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"data": [
{"id": 0, "vector": [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592], "title": "Artificial Intelligence in Real Life", "issue": "vol.12"},
{"id": 1, "vector": [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104], "title": "Hollow Man", "issue": "vol.19"},
{"id": 2, "vector": [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, 0.7894058910881185, 0.20785793220625592], "title": "Treasure Hunt in Missouri", "issue": "vol.12"},
],
"collectionName": "my_collection"
}'
# {
# "code": 0,
# "data": {
# "upsertCount": 3,
# "upsertIds": [
# 0,
# 1,
# 2,
# ]
# }
# }
Upsert сущностей в разделе
Вы также можете выполнить upsert сущностей в указанный раздел. В приведенных ниже фрагментах кода предполагается, что в вашей коллекции имеется раздел с именем PartitionA.
Если эти три сущности уже существуют в разделе, они будут перезаписаны сущностями, включёнными в запрос.
data=[
{
"id": 10,
"vector": [0.06998888224297328, 0.8582816610326578, -0.9657938677934292, 0.6527905683627726, -0.8668460657158576],
"title": "Layour Design Reference",
"issue": "vol.34"
},
{
"id": 11,
"vector": [0.6060703043917468, -0.3765080534566074, -0.7710758854987239, 0.36993888322346136, 0.5507513364206531],
"title": "Doraemon and His Friends",
"issue": "vol.2"
},
{
"id": 12,
"vector": [-0.9041813104515337, -0.9610546012461163, 0.20033003106083358, 0.11842506351635174, 0.8327356724591011],
"title": "Pikkachu and Pokemon",
"issue": "vol.12"
},
]
res = client.upsert(
collection_name="my_collection",
data=data,
partition_name="partitionA"
)
print(res)
# Output
# {'upsert_count': 3}
import io.milvus.v2.service.vector.request.UpsertReq;
import io.milvus.v2.service.vector.response.UpsertResp;
Gson gson = new Gson();
List<JsonObject> data = Arrays.asList(
gson.fromJson("{\"id\": 10, \"vector\": [0.06998888224297328, 0.8582816610326578, -0.9657938677934292, 0.6527905683627726, -0.8668460657158576], \"title\": \"Layour Design Reference\", \"issue\": \"vol.34\"}", JsonObject.class),
gson.fromJson("{\"id\": 11, \"vector\": [0.6060703043917468, -0.3765080534566074, -0.7710758854987239, 0.36993888322346136, 0.5507513364206531], \"title\": \"Doraemon and His Friends\", \"issue\": \"vol.2\"}", JsonObject.class),
gson.fromJson("{\"id\": 12, \"vector\": [-0.9041813104515337, -0.9610546012461163, 0.20033003106083358, 0.11842506351635174, 0.8327356724591011], \"title\": \"Pikkachu and Pokemon\", \"issue\": \"vol.12\"}", JsonObject.class),
);
UpsertReq upsertReq = UpsertReq.builder()
.collectionName("my_collection")
.partitionName("partitionA")
.data(data)
.build();
UpsertResp upsertResp = client.upsert(upsertReq);
System.out.println(upsertResp);
// Output:
//
// UpsertResp(upsertCnt=3)
const { MilvusClient, DataType } = require("@zilliz/milvus2-sdk-node")
// 6. Upsert data in partitions
data = [
{id: 10, vector: [0.06998888224297328, 0.8582816610326578, -0.9657938677934292, 0.6527905683627726, -0.8668460657158576], title: "Layour Design Reference", issue: "vol.34"},
{id: 11, vector: [0.6060703043917468, -0.3765080534566074, -0.7710758854987239, 0.36993888322346136, 0.5507513364206531], title: "Doraemon and His Friends", issue: "vol.2"},
{id: 12, vector: [-0.9041813104515337, -0.9610546012461163, 0.20033003106083358, 0.11842506351635174, 0.8327356724591011], title: "Pikkachu and Pokemon", issue: "vol.12"},
]
res = await client.upsert({
collection_name: "my_collection",
data: data,
partition_name: "partitionA"
})
console.log(res.upsert_cnt)
// Output
//
// 3
//
titleColumn = column.NewColumnString("title", []string{
"Layour Design Reference", "Doraemon and His Friends", "Pikkachu and Pokemon",
})
issueColumn = column.NewColumnString("issue", []string{
"vol.34", "vol.2", "vol.12",
})
_, err = client.Upsert(ctx, milvusclient.NewColumnBasedInsertOption("my_collection").
WithPartition("partitionA").
WithInt64Column("id", []int64{10, 11, 12, 13, 14, 15, 16, 17, 18, 19}).
WithFloatVectorColumn("vector", 5, [][]float32{
{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592},
{0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104},
{0.43742130801983836, -0.5597502546264526, 0.6457887650909682, 0.7894058910881185, 0.20785793220625592},
}).
WithColumns(titleColumn, issueColumn),
)
if err != nil {
fmt.Println(err.Error())
// handle err
}
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/upsert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"data": [
{"id": 10, "vector": [0.06998888224297328, 0.8582816610326578, -0.9657938677934292, 0.6527905683627726, -0.8668460657158576], "title": "Layour Design Reference", "issue": "vol.34"},
{"id": 11, "vector": [0.6060703043917468, -0.3765080534566074, -0.7710758854987239, 0.36993888322346136, 0.5507513364206531], "title": "Doraemon and His Friends", "issue": "vol.2"},
{"id": 12, "vector": [-0.9041813104515337, -0.9610546012461163, 0.20033003106083358, 0.11842506351635174, 0.8327356724591011], "title": "Pikkachu and Pokemon", "issue": "vol.12"},
],
"collectionName": "my_collection",
"partitionName": "partitionA"
}'
# {
# "code": 0,
# "data": {
# "upsertCount": 3,
# "upsertIds": [
# 10,
# 11,
# 12,
# ]
# }
# }
Обновляющее вставление сущностей в режиме слиянияCompatible with Milvus v2.6.2+
В следующем примере обновляется только поле « issue » сущностей с первичными ключами 1 и 2 в коллекции my_collection. Перед запуском убедитесь, что обе сущности уже существуют. Остальные поля этих сущностей сохраняют свои текущие значения.
При выполнении операции upsert в режиме слияния убедитесь, что сущности, участвующие в запросе, имеют одинаковый набор полей. Предположим, что необходимо выполнить операцию upsert для двух или более сущностей, как показано в следующем фрагменте кода. Важно, чтобы они содержали идентичные поля, чтобы предотвратить ошибки и сохранить целостность данных.
data=[
{
"id": 1,
"issue": "vol.14"
},
{
"id": 2,
"issue": "vol.7"
}
]
res = client.upsert(
collection_name="my_collection",
data=data,
partial_update=True
)
print(res)
# Output
# {'upsert_count': 2}
JsonObject row1 = new JsonObject();
row1.addProperty("id", 1);
row1.addProperty("issue", "vol.14");
JsonObject row2 = new JsonObject();
row2.addProperty("id", 2);
row2.addProperty("issue", "vol.7");
UpsertReq upsertReq = UpsertReq.builder()
.collectionName("my_collection")
.data(Arrays.asList(row1, row2))
.partialUpdate(true)
.build();
UpsertResp upsertResp = client.upsert(upsertReq);
System.out.println(upsertResp);
// Output:
//
// UpsertResp(upsertCnt=2)
pkColumn := column.NewColumnInt64("id", []int64{1, 2})
issueColumn = column.NewColumnString("issue", []string{
"vol.17", "vol.7",
})
_, err = client.Upsert(ctx, milvusclient.NewColumnBasedInsertOption("my_collection").
WithColumns(pkColumn, issueColumn).
WithPartialUpdate(true),
)
if err != nil {
fmt.Println(err.Error())
// handle err
}
const data=[
{
"id": 1,
"issue": "vol.14"
},
{
"id": 2,
"issue": "vol.7"
}
];
const res = await client.upsert({
collection_name: "my_collection",
data,
partial_update: true
});
console.log(res)
// Output
//
// 2
//
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
export COLLECTION_NAME="my_collection"
export UPSERT_DATA='[
{
"id": 1,
"issue": "vol.14"
},
{
"id": 2,
"issue": "vol.7"
}
]'
curl -X POST "http://localhost:19530/v2/vectordb/entities/upsert" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer ${TOKEN}" \
-d "{
\"collectionName\": \"${COLLECTION_NAME}\",
\"data\": ${UPSERT_DATA},
\"partialUpdate\": true
}"
# {
# "code": 0,
# "data": {
# "upsertCount": 2,
# "upsertIds": [
# 3,
# 12,
# ]
# }
# }
Операция «upsert» для полей ARRAY в режиме слиянияCompatible with Milvus 2.6.17+
До версии Milvus v2.6.17 для обновления части массива « ARRAY » требовался цикл «чтение-изменение-запись» на стороне клиента: запрос существующего массива, его изменение в коде приложения и выполнение операции «upsert» с полным значением замены. Операторы частичного обновления (ARRAY_APPEND и ARRAY_REMOVE) позволяют отправлять только те элементы, которые нужно добавить или удалить, что упрощает логику на стороне клиента и позволяет избежать лишнего чтения перед операцией upsert.
Предположим, что сущность с первичным ключом 1 уже содержит tags = ["new", "trial"]. До появления операторов частичного обновления добавление элемента "premium" в массив требовало операции upsert для всего массива:
client.upsert(
collection_name="users",
data=[{"pk": 1, "tags": ["new", "trial", "premium"]}],
partial_update=True,
)
List<JsonObject> replacementData = Collections.singletonList(
gson.fromJson("{\"pk\": 1, \"tags\": [\"new\", \"trial\", \"premium\"]}", JsonObject.class)
);
client.upsert(UpsertReq.builder()
.collectionName("users")
.partialUpdate(true)
.data(replacementData)
.build());
// nodejs
// go
# restful
С помощью ARRAY_APPEND отправляется только элемент, который нужно добавить:
client.upsert(
collection_name="users",
data=[{"pk": 1, "tags": ["premium"]}],
field_ops={"tags": FieldOp.array_append()},
)
List<JsonObject> appendData = Collections.singletonList(
gson.fromJson("{\"pk\": 1, \"tags\": [\"premium\"]}", JsonObject.class)
);
UpsertReq.FieldPartialUpdateOp appendTags = UpsertReq.FieldPartialUpdateOp.builder()
.fieldName("tags")
.opType(UpsertReq.FieldPartialUpdateOp.OpType.ARRAY_APPEND)
.build();
client.upsert(UpsertReq.builder()
.collectionName("users")
.data(appendData)
.fieldOps(Collections.singletonList(appendTags))
.build());
// nodejs
// go
# restful
С помощью ARRAY_REMOVE отправляйте только соответствующий элемент для удаления:
client.upsert(
collection_name="users",
data=[{"pk": 1, "tags": ["trial"]}],
field_ops={"tags": FieldOp.array_remove()},
)
List<JsonObject> removeData = Collections.singletonList(
gson.fromJson("{\"pk\": 1, \"tags\": [\"trial\"]}", JsonObject.class)
);
UpsertReq.FieldPartialUpdateOp removeTags = UpsertReq.FieldPartialUpdateOp.builder()
.fieldName("tags")
.opType(UpsertReq.FieldPartialUpdateOp.OpType.ARRAY_REMOVE)
.build();
client.upsert(UpsertReq.builder()
.collectionName("users")
.data(removeData)
.fieldOps(Collections.singletonList(removeTags))
.build());
// nodejs
// go
# restful
Присоединение любого из этих операторов к полю с помощью field_ops неявно включает семантику частичного обновления. Поэтому вам не нужно передавать partial_update=True вместе с field_ops.
Ограничения
Значения полезных данных должны соответствовать типу
element_typeцелевого поляARRAY. Например, если целевое поле —ARRAY<VARCHAR>, полезные данные должны содержать строковые значения.В Milvus версии 2.6.17 и более поздних
ARRAY_APPENDиARRAY_REMOVEподдерживают поляARRAY,element_typeкоторых равенBOOL,INT8,INT16,INT32,INT64,FLOAT,DOUBLEилиVARCHAR.После операции
ARRAY_APPENDдлина результирующего массива не должна превышать значение параметраmax_capacityдля данного поля.Одновременные операции «upsert» для одной и той же сущности не являются атомарными между запросами. Если два запроса одновременно обновляют одно и то же поле
ARRAY, более поздняя запись может перезаписать более раннюю. Используйте координацию на уровне приложения, если необходимо сохранить все одновременные изменения.
Пример
В следующем примере используется небольшая коллекция users с первичным ключом pk, полем tags типа ARRAY<VARCHAR> и векторным полем embedding. Сначала вставляются две сущности с начальными значениями tags, затем с помощью ARRAY_APPEND и ARRAY_REMOVE демонстрируется, как каждый оператор изменяет сохраненный массив.
from pymilvus import DataType, FieldOp, MilvusClient
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
# 1. Create a collection with an ARRAY<VARCHAR> field
schema = client.create_schema(enable_dynamic_field=False)
schema.add_field("pk", DataType.INT64, is_primary=True)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=5)
schema.add_field(
"tags",
DataType.ARRAY,
element_type=DataType.VARCHAR,
max_capacity=8,
max_length=32,
)
index_params = client.prepare_index_params()
index_params.add_index(
field_name="embedding",
index_type="AUTOINDEX",
metric_type="L2",
)
client.create_collection(
collection_name="users",
schema=schema,
index_params=index_params
)
# 2. Seed two entities
client.insert(
collection_name="users",
data=[
{"pk": 1, "embedding": [0.1, 0.2, 0.3, 0.4, 0.5], "tags": ["new"]},
{"pk": 2, "embedding": [0.6, 0.7, 0.8, 0.9, 1.0], "tags": ["new", "trial"]},
],
)
# 3. Append tags without reading the existing ARRAY values
client.upsert(
collection_name="users",
data=[
{"pk": 1, "tags": ["premium", "vip"]},
{"pk": 2, "tags": ["premium"]},
],
field_ops={"tags": FieldOp.array_append()},
)
res = client.query(
collection_name="users",
filter="pk in [1, 2]",
output_fields=["pk", "tags"],
)
print(res)
# Example output:
# data: [
# "{'pk': 1, 'tags': ['new', 'premium', 'vip']}",
# "{'pk': 2, 'tags': ['new', 'trial', 'premium']}"
# ]
# 4. Remove matching tags without replacing the full ARRAY field
client.upsert(
collection_name="users",
data=[
{"pk": 1, "tags": ["new"]},
{"pk": 2, "tags": ["trial"]},
],
field_ops={"tags": FieldOp.array_remove()},
)
res = client.query(
collection_name="users",
filter="pk in [1, 2]",
output_fields=["pk", "tags"],
)
print(res)
# Example output:
# data: [
# "{'pk': 1, 'tags': ['premium', 'vip']}",
# "{'pk': 2, 'tags': ['new', 'premium']}"
# ]
// java
// nodejs
// go
# restful
Upsert поля StructArray в режиме слияния
Операция «upsert» поля StructArray в сущности перезаписывает значение поля. Это означает, что при выполнении операции «upsert» поля StructArray необходимо включить все подполя, определённые в схеме структуры.
В следующем примере показано, как выполнить операцию «upsert» для поля ` chunks ` в режиме слияния — это поле `StructArray` с 6 подполями. По завершении операции поле ` chunks ` сущности с id 1 устанавливается в массив со структурами из двух элементов, указанными в запросе.
client.upsert(
collection_name="books",
data=[{
"id": 1,
"chunks": [
{
"text": "Use HNSW efSearch to trade recall for latency.",
"section": "index",
"page": 1,
"quality_score": 0.92,
"has_code": True,
"emb_list_vector": [0.11, 0.21, 0.31, 0.41]
},
{
"text": "Range search returns vectors within a distance boundary.",
"section": "search",
"page": 2,
"quality_score": 0.86,
"has_code": False,
"emb_list_vector": [0.18, 0.23, 0.29, 0.36]
}
]
}],
partial_update=True
)
// java
// nodejs
// go
# restful