• О компании Milvus
  • Начать работу
  • Понятия
  • Руководство пользователя
  • Импорт данных
  • Инструменты искусственного интеллекта
  • Руководство по администрированию
  • Инструменты
  • Интеграции
  • Учебные пособия
  • Часто задаваемые вопросы
  • API Reference

Операция «Upsert» с сущностями

Операция « upsert » предоставляет удобный способ вставки или обновления сущностей в коллекции.

Обзор

Вы можете использовать операцию « upsert » для вставки нового объекта или обновления существующего, в зависимости от того, существует ли в коллекции первичный ключ, указанный в запросе «upsert». Если первичный ключ не найден, выполняется операция вставки. В противном случае выполняется операция обновления. Частичные обновления в коллекциях типа « autoID » являются исключением: они обновляют только существующие объекты, как описано ниже.

Операция upsert в Milvus работает либо в режиме перезаписи, либо в режиме слияния.

Операция «upsert» в режиме перезаписи

Запрос «upsert», работающий в режиме перезаписи, сочетает в себе операции вставки и удаления. При получении запроса « upsert » для существующего объекта Milvus вставляет данные, содержащиеся в полезенном грузе запроса, и одновременно удаляет существующий объект с исходным первичным ключом, указанным в данных.

Upsert In Override Mode Upsert в режиме перезаписи

Если в целевой коллекции для основного поля включена функция « autoID », запрос « upsert » все равно должен содержать первичный ключ целевого объекта. Milvus использует предоставленный первичный ключ для поиска объекта, который необходимо заменить, и генерирует новый первичный ключ для данных, содержащихся в полезной нагрузке запроса, перед их вставкой.

Поля, для которых включена функция « nullable », можно опустить в запросе ` upsert `, если они не требуют обновления.

Upsert в режиме слиянияCompatible with Milvus v2.6.2+

Вы также можете использовать флаг partial_update, чтобы запрос на обновление или вставку работал в режиме слияния. Это позволяет включать в тело запроса только те поля, которые требуют обновления.

Upsert In Merge Mode Upsert в режиме слияния

Для выполнения слияния установите флаг ` partial_update ` в значение ` True ` в запросе ` upsert `, указав при этом первичный ключ и поля, которые необходимо обновить, с их новыми значениями.

При получении такого запроса Milvus выполняет запрос с жесткой согласованностью для извлечения сущности, обновляет значения полей на основе данных в запросе, вставляет измененные данные, а затем удаляет существующую сущность с исходным первичным ключом, переданным в запросе.

Для коллекции с включенным режимом слияния ( autoID ) режим слияния сохраняет первичный ключ, указанный в запросе, вместо генерации нового. Это отличается от режима переопределения (override mode), в котором Milvus генерирует новый первичный ключ для заменяемого объекта. Первичный ключ в запросе в режиме слияния должен идентифицировать существующий объект; в противном случае Milvus отклоняет запрос вместо вставки нового объекта.

Для полей типа « 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 версии 2.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+

В следующем примере кода показано, как выполнять upsert сущностей с частичным обновлением. Укажите только те поля, которые необходимо обновить, и их новые значения, а также явно укажите флаг частичного обновления.

В приведенном ниже примере поле « issue » сущностей, указанных в запросе на обновление или добавление, будет обновлено до значений, включенных в запрос.

При выполнении операции «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