• Informazioni su Milvus
  • Iniziare
  • Concetti
  • Guida per l'utente
  • Importazione dei dati
  • Strumenti AI
  • Guida all'amministrazione
  • Strumenti
  • Integrazioni
  • Tutorial
  • Domande frequenti
  • API Reference

Inserimento di entità

L'operazione upsert fornisce un modo pratico per inserire o aggiornare le entità in una raccolta.

Panoramica

È possibile utilizzare upsert per inserire una nuova entità o aggiornarne una esistente, a seconda che la chiave primaria fornita nella richiesta di upsert esista o meno nell'insieme. Se la chiave primaria non viene trovata, viene eseguita un'operazione di inserimento. In caso contrario, viene eseguita un'operazione di aggiornamento.

Un upsert in Milvus funziona in modalità override o merge.

Upsert in modalità override

Una richiesta di upsert che funziona in modalità override combina un inserimento e una cancellazione. Quando viene ricevuta una richiesta upsert per un'entità esistente, Milvus inserisce i dati contenuti nel payload della richiesta e cancella contemporaneamente l'entità esistente con la chiave primaria originale specificata nei dati.

Upsert In Override Mode Upsert in modalità Override

Se la collezione di destinazione ha autoid abilitato sul suo campo primario, Milvus genererà una nuova chiave primaria per i dati trasportati nel payload della richiesta prima di inserirli.

Per i campi con nullable abilitato, è possibile ometterli nella richiesta upsert se non richiedono aggiornamenti.

Upsert in modalità mergeCompatible with Milvus v2.6.2+

È possibile utilizzare il flag partial_update anche per far funzionare una richiesta di upsert in modalità merge. Ciò consente di includere nel payload della richiesta solo i campi che devono essere aggiornati.

Upsert In Merge Mode Upsert in modalità unione

Per eseguire una fusione, impostare partial_update su True nella richiesta upsert insieme alla chiave primaria e ai campi da aggiornare con i loro nuovi valori.

Quando riceve una richiesta di questo tipo, Milvus esegue una query con coerenza forte per recuperare l'entità, aggiorna i valori dei campi in base ai dati della richiesta, inserisce i dati modificati e poi cancella l'entità esistente con la chiave primaria originale riportata nella richiesta.

Comportamenti di Upsert: note speciali

Ci sono diverse note speciali da considerare prima di usare la funzione di unione. Nei casi seguenti si ipotizza di avere una collezione con due campi scalari denominati title e issue, insieme a una chiave primaria id e a un campo vettoriale denominato vector.

  • Campi Upsert con nullable abilitato.

    Si supponga che il campo issue possa essere nullo. Quando si effettua l'upsert di questi campi, si noti che:

    • Se si omette il campo issue nella richiesta upsert e si disabilita partial_update, il campo issue verrà aggiornato a null invece di mantenere il suo valore originale.

    • Per conservare il valore originale del campo issue, è necessario abilitare partial_update e omettere il campo issue oppure includere il campo issue con il suo valore originale nella richiesta upsert.

  • Integrare le chiavi nel campo dinamico.

    Si supponga di aver abilitato la chiave dinamica nell'insieme di esempio e che le coppie chiave-valore nel campo dinamico di un'entità siano simili a {"author": "John", "year": 2020, "tags": ["fiction"]}.

    Quando si effettua l'upsert dell'entità con chiavi come author, year, o tags, o si aggiungono altre chiavi, si noti che:

    • Se si esegue l'upsert con partial_update disabilitato, il comportamento predefinito è quello di sovrascrivere. Ciò significa che il valore del campo dinamico sarà sovrascritto da tutti i campi non definiti dallo schema inclusi nella richiesta e dai loro valori.

      Ad esempio, se i dati inclusi nella richiesta sono {"author": "Jane", "genre": "fantasy"}, le coppie chiave-valore nel campo dinamico dell'entità di destinazione saranno aggiornate a tale valore.

    • Se si esegue l'upsert con partial_update abilitato, il comportamento predefinito è l'unione. Ciò significa che il valore del campo dinamico si fonderà con tutti i campi non definiti dallo schema inclusi nella richiesta e i loro valori.

      Ad esempio, se i dati inclusi nella richiesta sono {"author": "John", "year": 2020, "tags": ["fiction"]}, le coppie chiave-valore nel campo dinamico dell'entità di destinazione diventeranno {"author": "John", "year": 2020, "tags": ["fiction"], "genre": "fantasy"} dopo l'upsert.

  • Upsert di un campo JSON.

    Si supponga che l'insieme di esempio abbia un campo JSON definito dallo schema e denominato extras, e che le coppie chiave-valore in questo campo JSON di un'entità siano simili a {"author": "John", "year": 2020, "tags": ["fiction"]}.

    Quando si effettua l'upsert del campo extras di un'entità con dati JSON modificati, si noti che il campo JSON viene trattato nel suo complesso e non è possibile aggiornare le singole chiavi in modo selettivo. In altre parole, il campo JSON NON supporta l'upsert in modalità unione.

Limiti e restrizioni

Sulla base del contenuto di cui sopra, ci sono diversi limiti e restrizioni da seguire:

  • La richiesta upsert deve sempre includere le chiavi primarie delle entità di destinazione.

  • L'insieme di destinazione deve essere caricato e disponibile per le query.

  • Tutti i campi specificati nella richiesta devono esistere nello schema dell'insieme di destinazione.

  • I valori di tutti i campi specificati nella richiesta devono corrispondere ai tipi di dati definiti nello schema.

  • Per qualsiasi campo derivato da un altro tramite funzioni, Milvus rimuoverà il campo derivato durante l'upsert per consentire il ricalcolo.

Upsert di entità in una collezione

In questa sezione, si effettuerà l'upsert di entità in una collezione denominata my_collection. Questa collezione ha solo due campi, denominati id, vector, title e issue. Il campo id è il campo primario, mentre i campi title e issue sono campi scalari.

Le tre entità, se esistenti nella collezione, saranno sovrascritte da quelle incluse nella richiesta di 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" \
-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,
#         ]
#     }
# }

Aggiungere entità in una partizione

È anche possibile eseguire l'upsert di entità in una partizione specificata. I seguenti frammenti di codice ipotizzano che nella collezione sia presente una partizione denominata PartitionA.

Le tre entità, se esistenti nella partizione, saranno sostituite da quelle incluse nella richiesta.

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

Upsert di entità in modalità mergeCompatible with Milvus v2.6.2+

Il seguente esempio di codice mostra come effettuare l'upsert di entità con aggiornamenti parziali. Fornire solo i campi da aggiornare e i loro nuovi valori, insieme al flag di aggiornamento parziale esplicito.

Nell'esempio seguente, il campo issue delle entità specificate nella richiesta di upsert verrà aggiornato con i valori inclusi nella richiesta.

Quando si esegue un upsert in modalità unione, assicurarsi che le entità coinvolte nella richiesta abbiano lo stesso insieme di campi. Supponiamo che ci siano due o più entità da upserire, come mostrato nel seguente frammento di codice, è importante che includano campi identici per evitare errori e mantenere l'integrità dei dati.

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 "Authorization: Bearer ${TOKEN}" \
  -d "{
    \"collectionName\": \"${COLLECTION_NAME}\",
    \"data\": ${UPSERT_DATA},
    \"partialUpdate\": true
  }"

# {
#     "code": 0,
#     "data": {
#         "upsertCount": 2,
#         "upsertIds": [
#              3,
#             12,
#         ]
#     }
# }