• À propos de Milvus
  • Commencer
  • Concepts
  • Guide de l'utilisateur
  • Importation de données
  • Outils d'IA
  • Guide d'administration
  • Outils
  • Intégrations
  • Tutoriels
  • Foire aux questions
  • API Reference

Insertion ou mise à jour d'entités

L'opération « upsert » offre un moyen pratique d'insérer ou de mettre à jour des entités dans une collection.

Présentation

Vous pouvez utiliser « upsert » pour insérer une nouvelle entité ou mettre à jour une entité existante, selon que la clé primaire fournie dans la requête « upsert » existe ou non dans la collection. Si la clé primaire n’est pas trouvée, une opération d’insertion est effectuée. Dans le cas contraire, une opération de mise à jour est effectuée.

Dans Milvus, l’opération « upsert » fonctionne soit en mode « override », soit en mode « merge ».

Upsert en mode « override »

Une requête d’ upsert fonctionnant en mode « override » combine une insertion et une suppression. Lorsqu’une requête d’ concernant une entité existante est reçue, Milvus insère les données contenues dans la charge utile de la requête et supprime simultanément l’entité existante dont la clé primaire d’origine est spécifiée dans les données.

Upsert In Override Mode Upsert en mode « override »

Si l’ autoID est activée sur le champ primaire de la collection cible, la requête d’ upsert doit tout de même inclure la clé primaire de l’entité cible. Milvus utilise la clé primaire fournie pour localiser l’entité à remplacer, puis génère une nouvelle clé primaire pour les données contenues dans la charge utile de la requête avant de les insérer.

Pour les champs dont l’ nullable est activée, vous pouvez les omettre dans la requête d’ upsert s’ils ne nécessitent aucune mise à jour.

Upsert en mode fusionCompatible with Milvus v2.6.2+

Vous pouvez également utiliser l’indicateur « partial_update » pour qu’une requête d’upsert fonctionne en mode fusion. Cela vous permet de n’inclure dans le corps de la requête que les champs nécessitant une mise à jour.

Upsert In Merge Mode Upsert en mode fusion

Pour effectuer une fusion, définissez ` partial_update ` sur ` True ` dans la requête ` upsert `, en indiquant la clé primaire et les champs à mettre à jour avec leurs nouvelles valeurs.

À la réception d’une telle requête, Milvus effectue une requête avec une cohérence forte pour récupérer l’entité, met à jour les valeurs des champs en fonction des données de la requête, insère les données modifiées, puis supprime l’entité existante dont la clé primaire d’origine figurait dans la requête.

Pour les champs de type « ARRAY », le mode «merge» prend en charge deux opérateurs dans Milvus v2.6.17 et versions ultérieures : « ARRAY_APPEND » et « ARRAY_REMOVE ». Ces opérateurs vous permettent d’ajouter des éléments à un champ « ARRAY » existant ou d’en supprimer les éléments correspondants, sans avoir à interroger au préalable l’entité pour récupérer sa valeur actuelle. Pour plus de détails, consultez la section «Upsert des champs ARRAY en mode merge».

Comportements de l'Upsert : remarques particulières

Il existe plusieurs remarques particulières à prendre en compte avant d’utiliser la fonctionnalité de fusion. Les cas suivants partent du principe que vous disposez d’une collection comportant deux champs scalaires nommés title et issue, ainsi qu’une clé primaire id et un champ vectoriel appelé vector.

  • Mise à jour ou insertion de champs avec l'option « nullable » activée.

    Supposons que le champ issue puisse être nul. Lorsque vous effectuez un « upsert » sur ces champs, notez que :

    • Si vous omettez le champ « issue » dans la requête « upsert » et désactivez « partial_update », le champ « issue » sera mis à jour en « null » au lieu de conserver sa valeur d’origine.

    • Pour conserver la valeur d’origine du champ « issue », vous devez soit activer l’option « partial_update » et omettre le champ « issue », soit inclure le champ « issue » avec sa valeur d’origine dans la requête « upsert ».

  • Clés d'upsert dans le champ dynamique.

    Supposons que vous ayez activé la clé dynamique dans la collection d’exemple, et que les paires clé-valeur du champ dynamique d’une entité soient similaires à {"author": "John", "year": 2020, "tags": ["fiction"]}.

    Lorsque vous effectuez un « upsert » de l’entité avec des clés telles que author, year ou tags, ou que vous ajoutez d’autres clés, notez que :

    • Si vous effectuez un « upsert » alors que l’option « partial_update » est désactivée, le comportement par défaut consiste à écraser la valeur existante. Cela signifie que la valeur du champ dynamique sera remplacée par toutes les valeurs des champs non définis dans le schéma inclus dans la requête.

      Par exemple, si les données incluses dans la requête sont {"author": "Jane", "genre": "fantasy"}, les paires clé-valeur du champ dynamique de l’entité cible seront mises à jour en conséquence.

    • Si vous effectuez une opération « upsert » avec l’option « partial_update » activée, le comportement par défaut est la fusion. Cela signifie que la valeur du champ dynamique sera fusionnée avec tous les champs non définis par le schéma inclus dans la requête, ainsi qu’avec leurs valeurs.

      Par exemple, si les données incluses dans la requête sont {"author": "John", "year": 2020, "tags": ["fiction"]}, les paires clé-valeur du champ dynamique de l’entité cible deviendront {"author": "John", "year": 2020, "tags": ["fiction"], "genre": "fantasy"} après l’« upsert ».

  • Upsert d’un champ JSON.

    Supposons que la collection d’exemple comporte un champ JSON défini par le schéma nommé extras, et que les paires clé-valeur de ce champ JSON d’une entité soient similaires à {"author": "John", "year": 2020, "tags": ["fiction"]}.

    Lorsque vous effectuez un « upsert » sur le champ extras d’une entité avec des données JSON modifiées, notez que le champ JSON est traité dans son ensemble et que vous ne pouvez pas mettre à jour des clés individuelles de manière sélective. En d’autres termes, le champ JSON NE PREND PAS en charge l’« upsert » en mode fusion.

  • Effectuer une opération «upsert » sur un champ « ARRAY ».

    Par défaut, un champ « ARRAY » en mode « merge » suit la sémantique REPLACE: la valeur transmise dans la requête écrase le tableau existant. Pour des mises à jour plus précises, Milvus v2.6.17 et versions ultérieures prennent également en charge deux opérateurs :

    • ARRAY_APPEND ajoute les éléments contenus dans la charge utile de la requête au tableau existant.

    • ARRAY_REMOVE supprime de la table existante tous les éléments correspondant à une valeur contenue dans la charge utile de la requête.

    Pour la syntaxe des opérateurs, les types d’éléments pris en charge et les autres contraintes, consultez la section « Upsert des champs ARRAY en mode fusion ».

  • Upsert d’un champ StructArray.

    L’upsert d’un champ StructArray dans une entité écrase la valeur du champ. Pour ce faire, vous devez fournir une liste de dictionnaires, chacun contenant tous les sous-champs définis dans le schéma de la structure, même lorsque vous effectuez l’upsert en mode fusion.

    Pour plus de détails, consultez la section « Upsert d’un champ StructArray en mode fusion ».

Limites et restrictions

Compte tenu de ce qui précède, plusieurs limites et restrictions doivent être respectées :

  • La requête d’« upsert » doit toujours inclure les clés primaires des entités cibles, même lorsque l’option « autoID » est activée. Pour les collections « autoID », les clés primaires de la requête identifient les entités existantes à remplacer. Milvus génère de nouvelles clés primaires pour les entités de remplacement insérées.

  • La collection cible doit être chargée et accessible pour les requêtes.

  • Tous les champs spécifiés dans la requête doivent exister dans le schéma de la collection cible.

  • Les valeurs de tous les champs spécifiés dans la requête doivent correspondre aux types de données définis dans le schéma.

  • Pour tout champ dérivé d’un autre à l’aide de fonctions, Milvus supprimera le champ dérivé lors de l’upsert afin de permettre un nouveau calcul.

Upsert d’entités dans une collection

Dans cette section, nous allons effectuer un « upsert » d’entités dans une collection nommée my_collection. Cette collection ne comporte que deux champs, nommés id, vector, title et issue. Le champ id est le champ principal, tandis que les champs title et issue sont des champs scalaires.

Si ces trois entités existent déjà dans la collection, elles seront remplacées par celles incluses dans la requête d’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 d’entités dans une partition

Vous pouvez également effectuer un « upsert » d’entités dans une partition spécifiée. Les extraits de code suivants partent du principe que votre collection contient une partition nommée PartitionA.

Si les trois entités existent déjà dans la partition, elles seront remplacées par celles incluses dans la requête.

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,
#         ]
#     }
# }

Insérer ou mettre à jour des entités en mode fusionCompatible with Milvus v2.6.2+

L'exemple de code suivant montre comment effectuer un « upsert » d'entités avec des mises à jour partielles. Indiquez uniquement les champs à mettre à jour et leurs nouvelles valeurs, ainsi que l'indicateur explicite de mise à jour partielle.

Dans l’exemple suivant, le champ « issue » des entités spécifiées dans la requête d’upsert sera mis à jour avec les valeurs incluses dans la requête.

Lorsque vous effectuez un « upsert » en mode fusion, assurez-vous que les entités concernées par la requête possèdent le même ensemble de champs. Supposons qu’il y ait deux entités ou plus à mettre à jour ou à insérer, comme le montre l’extrait de code suivant : il est important qu’elles comportent des champs identiques afin d’éviter les erreurs et de préserver l’intégrité des données.

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 des champs ARRAY en mode fusionCompatible with Milvus 2.6.17+

Avant la version 2.6.17 de Milvus, la mise à jour d’une partie d’un champ de type « ARRAY » nécessitait un flux « lecture-modification-écriture » côté client : interroger le tableau existant, le modifier dans le code de l’application, puis effectuer un « upsert » avec la valeur de remplacement complète. Les opérateurs de mise à jour partielle (ARRAY_APPEND et ARRAY_REMOVE) vous permettent d’envoyer uniquement les éléments à ajouter ou à supprimer, ce qui réduit la logique côté client et évite la lecture supplémentaire avant l’upsert.

Supposons que l’entité dont la clé primaire est 1 possède déjà tags = ["new", "trial"]. Avant l’apparition des opérateurs de mise à jour partielle, l’ajout de l’élément "premium" à un tableau nécessitait l’upsert du tableau de remplacement complet :

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

Avec ` ARRAY_APPEND`, il suffit d’envoyer uniquement l’élément à ajouter :

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

Avec ` ARRAY_REMOVE`, envoyez uniquement l'élément correspondant à supprimer :

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

L'association de l'un ou l'autre de ces opérateurs à un champ via ` field_ops ` active implicitement la sémantique de mise à jour partielle. Par conséquent, vous n' avez pas besoin de passer ` partial_update=True ` en plus de ` field_ops`.

Contraintes

  • Les valeurs de la charge utile doivent correspondre aux types de données ( element_type ) du champ cible ( ARRAY ). Par exemple, si le champ cible est de type ARRAY<VARCHAR>, la charge utile doit contenir des valeurs de type chaîne de caractères.

  • Dans Milvus v2.6.17 et versions ultérieures, les champs « ARRAY_APPEND » et « ARRAY_REMOVE » prennent en charge les champs « ARRAY » dont l’ element_type est « BOOL », « INT8 », « INT16 », « INT32 », « INT64 », « FLOAT », « DOUBLE » ou « VARCHAR ».

  • Après une opération « ARRAY_APPEND », la longueur du tableau résultant ne doit pas dépasser la valeur « max_capacity » du champ.

  • Les opérations « upsert » simultanées sur une même entité ne sont pas atomiques d’une requête à l’autre. Si deux requêtes mettent à jour simultanément le même champ ARRAY, l’écriture la plus récente peut écraser la précédente. Utilisez une coordination au niveau de l’application si vous devez conserver toutes les modifications simultanées.

Exemple

L’exemple suivant utilise une petite collection d’ users s avec une clé primaire pk, un champ tags de type ARRAY<VARCHAR> et un champ vectoriel embedding. Il insère d’abord deux entités avec des valeurs initiales tags, puis utilise ARRAY_APPEND et ARRAY_REMOVE pour montrer comment chaque opérateur modifie le tableau stocké.

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 d’un champ StructArray en mode fusion

L’upsert d’un champ StructArray dans une entité écrase la valeur du champ. Cela signifie que vous devez inclure tous les sous-champs définis dans le schéma de la structure lorsque vous effectuez un upsert sur un champ StructArray.

L'exemple suivant montre comment effectuer un « upsert » du champ « chunks » en mode fusion, un champ StructArray comportant 6 sous-champs. Une fois l'opération terminée, le champ « chunks » de l'entité dont l'ID est 1 est défini sur le tableau contenant les structures à deux éléments fournies dans la requête.

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