Nullbare Felder

Milvus unterstützt löschbare Felder, bei denen ein Feldwert fehlen oder explizit auf NULL gesetzt werden kann. Die Löschbarkeit wird auf Schemaebene definiert und gilt konsistent für Dateneingabe, Indizierung, Suche und Abfrageoperationen.

Verwenden Sie löschbare Felder, wenn:

  • Daten aus externen Systemen eingelesen werden, die fehlende Werte zulassen.
  • Einige Metadaten sind optional oder nur für einen Teil des Datensatzes verfügbar.
  • Vektoreinbettungen werden asynchron generiert und später eingefügt.

Grenzwerte

  • Vektorfelder, die NULL-Werte zulassen, unterstützen keine IS NULL oder IS NOT NULL Filterausdrücke. Sie können Entitäten nicht explizit danach filtern, ob ein Vektorfeldwert NULL ist.

  • Array of Structs Felder unterstützen keine NULL-Werte. Sie können ein Array of Structs Feld oder ein darin verschachteltes Feld nicht als löschbar markieren.

  • Das Attribut löschbar wird bei der Erstellung eines Feldes definiert und kann danach nicht mehr geändert werden. Sie können die Löschbarkeit für ein bestehendes Feld nicht aktivieren oder deaktivieren.

  • Als löschbar gekennzeichnete Felder können nicht als Partitionsschlüssel verwendet werden. Partitionsschlüsselfelder müssen immer gültige Werte enthalten, die nicht null sind. Weitere Informationen finden Sie unter Partitionsschlüssel verwenden.

Was ist ein löschbares Feld?

Ob ein Feld einen NULL-Wert speichern darf, wird in Milvus durch ein Feldattribut auf Schemaebene namens nullable gesteuert.

Wenn ein Feld mit nullable=True definiert ist, erlaubt Milvus, dass der Feldwert während der Datenaufnahme fehlt. In der Praxis behandelt Milvus die folgenden beiden Eingaben als gleichwertig und speichert den Feldwert als NULL:

  • Das Feld wird in der Eingabeentität ausgelassen.
  • Das Feld wird explizit auf NULL gesetzt (z. B. None in Python).

Wenn ein Feld nicht als nullbar definiert ist (das Standardverhalten), muss jede Entität einen gültigen Wert für dieses Feld angeben. Das Weglassen des Feldes oder die explizite Zuweisung eines NULL-Wertes führt dazu, dass der Einfüge- oder Importvorgang fehlschlägt.

Das Attribut "nullable" wird sowohl für skalare als auch für Vektorfelder in einem Auflistungsschema unterstützt. Array of Structs-Felder unterstützen das nullable-Attribut jedoch nicht.

Das Attribut Nullable bestimmt, ob ein Feldwert fehlen darf; es definiert nicht, welcher Wert verwendet wird, wenn ein Feld fehlt.

  • Wenn ein löschbares Feld ohne einen Standardwert konfiguriert ist, führt das Auslassen des Feldes zu einem gespeicherten NULL-Wert.
  • Wenn ein Standardwert konfiguriert ist, kann Milvus stattdessen den Standardwert speichern. Für Details siehe Standardwerte.

Definieren Sie ein löschbares Feld im Sammlungsschema

Um löschbare Felder zu verwenden, müssen Sie das Attribut löschbar bei der Definition des Sammlungsschemas aktivieren.

In diesem Beispiel definiert das Auflistungsschema ein Vektorfeld namens embedding mit nullable=True. Dadurch können Entitäten in der Sammlung den Vektorwert weglassen oder ihn während der Dateneingabe explizit auf NULL setzen.

from pymilvus import MilvusClient, DataType

client = MilvusClient(
    uri="http://localhost:19530",
    token="root:Milvus"
)

# Define schema fields
schema = client.create_schema()
schema.add_field("id", DataType.INT64, is_primary=True)  # Primary field
schema.add_field(
    field_name="embedding",
    datatype=DataType.FLOAT_VECTOR,
    dim=4,
    nullable=True,  # Enable the nullable attribute; defaults to False
)

client.create_collection(
    collection_name="my_collection",
    schema=schema,
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;

import java.util.Collections;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .token("root:Milvus")
        .build());

CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.addField(AddFieldReq.builder()
        .fieldName("id")
        .dataType(DataType.Int64)
        .isPrimaryKey(true)
        .build());
schema.addField(AddFieldReq.builder()
        .fieldName("embedding")
        .dataType(DataType.FloatVector)
        .dimension(4)
        .isNullable(true)
        .build());

CreateCollectionReq requestCreate = CreateCollectionReq.builder()
        .collectionName("my_collection")
        .collectionSchema(schema)
        .indexParams(Collections.emptyList())
        .build();
client.createCollection(requestCreate);
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";

const client = new MilvusClient({
  address: "http://localhost:19530",
  token: "root:Milvus",
});

await client.createCollection({
  collection_name: "my_collection",
  fields: [
    {
      name: "id",
      data_type: DataType.Int64,
      is_primary_key: true,
      autoID: false,
    },
    {
      name: "embedding",
      data_type: DataType.FloatVector,
      dim: 4,
      nullable: true,
    },
  ],
});
import (
    "context"
    "fmt"

    "github.com/milvus-io/milvus/client/v2/entity"
    "github.com/milvus-io/milvus/client/v2/milvusclient"
)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
    Address: "localhost:19530",
})
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
defer client.Close(ctx)

schema := entity.NewSchema()
schema.WithField(entity.NewField().
    WithName("id").
    WithDataType(entity.FieldTypeInt64).
    WithIsPrimaryKey(true),
).WithField(entity.NewField().
    WithName("embedding").
    WithDataType(entity.FieldTypeFloatVector).
    WithDim(4).
    WithNullable(true),
)

err = client.CreateCollection(ctx,
    milvusclient.NewCreateCollectionOption("my_collection", schema))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
export TOKEN="root:Milvus"
export CLUSTER_ENDPOINT="http://localhost:19530"

export pkField='{
  "fieldName": "id",
  "dataType": "Int64",
  "isPrimary": true
}'

export embeddingField='{
  "fieldName": "embedding",
  "dataType": "FloatVector",
  "typeParams": {"dim": "4"},
  "nullable": true
}'

curl --request POST \
  --url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
  --header "Authorization: Bearer ${TOKEN}" \
  --header "Content-Type: application/json" \
  -d "{
    \"collectionName\": \"my_collection\",
    \"schema\": {
      \"fields\": [
        $pkField,
        $embeddingField
      ]
    }
  }"

In diesem Schema:

  • Das Feld embedding ist explizit als nullbar markiert.
  • Entitäten können das Feld embedding auslassen oder ihm während der Einfügung einen NULL-Wert zuweisen.
  • Die Entscheidung, NULL-Werte zuzulassen, wird zum Zeitpunkt der Erstellung der Sammlung festgelegt.

Der Klarheit halber konzentrieren sich die folgenden Beispiele auf ein löschbares Vektorfeld (embedding). Die Definition von löschbaren Skalarfeldern ist optional und nicht erforderlich, um den Rest dieses Leitfadens zu befolgen.

Optional: Definieren Sie ein löschbares Skalarfeld

Skalarfelder können auch als löschbar definiert werden, indem dasselbe nullable Attribut verwendet wird und die gleichen Regeln bei der Aufnahme befolgt werden. Zum Beispiel:

schema.add_field(
    field_name="age",
    datatype=DataType.INT64,
    nullable=True,
)
schema.addField(AddFieldReq.builder()
        .fieldName("age")
        .dataType(DataType.Int64)
        .isNullable(true)
        .build());
// Add to the fields array when calling createCollection:
// { name: "age", data_type: DataType.Int64, nullable: true },
schema.WithField(entity.NewField().
    WithName("age").
    WithDataType(entity.FieldTypeInt64).
    WithNullable(true),
)
# Add another field object to the schema "fields" array, for example:
# { "fieldName": "age", "dataType": "Int64", "nullable": true }

Einfügeverhalten bei fehlenden oder NULL-Werten

Sobald ein Feld im Sammlungsschema als löschbar definiert ist, erlaubt Milvus, dass der Feldwert während der Dateneingabe fehlt oder explizit auf NULL gesetzt wird.

Das folgende Beispiel fügt drei Entitäten in die unter Definieren eines löschbaren Feldes im Sammlungsschema erstellte Sammlung ein und demonstriert diese verschiedenen Fälle.

data = [
    {
        "id": 1,
        "embedding": [0.1, 0.2, 0.3, 0.4],
    },
    {
        "id": 2,
        "embedding": None,  # Explicitly set to NULL
    },
    {
        "id": 3,  # Field omitted → stored as NULL
    },
]

client.insert(
    collection_name="my_collection",
    data=data,
)
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
import io.milvus.v2.service.vector.response.InsertResp;

import java.util.ArrayList;
import java.util.List;

List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
rows.add(gson.fromJson("{\"id\": 1, \"embedding\": [0.1, 0.2, 0.3, 0.4]}", JsonObject.class));
rows.add(gson.fromJson("{\"id\": 2, \"embedding\": null}", JsonObject.class));
rows.add(gson.fromJson("{\"id\": 3}", JsonObject.class));

InsertResp insertR = client.insert(InsertReq.builder()
        .collectionName("my_collection")
        .data(rows)
        .build());
const data = [
  { id: 1, embedding: [0.1, 0.2, 0.3, 0.4] },
  { id: 2, embedding: null },
  { id: 3 },
];

await client.insert({
  collection_name: "my_collection",
  data: data,
});
import (
    "context"
    "fmt"

    "github.com/milvus-io/milvus/client/v2/milvusclient"
)

// Assumes `client` is the Milvus client from the Go schema example above.
ctx := context.Background()

rows := []any{
    map[string]any{"id": int64(1), "embedding": []float32{0.1, 0.2, 0.3, 0.4}},
    map[string]any{"id": int64(2), "embedding": nil},
    map[string]any{"id": int64(3)},
}

_, err := client.Insert(ctx, milvusclient.NewRowBasedInsertOption("my_collection", rows...))
if err != nil {
    fmt.Println(err.Error())
}
curl --request POST \
  --url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
  --header "Authorization: Bearer ${TOKEN}" \
  --header "Content-Type: application/json" \
  -d '{
    "collectionName": "my_collection",
    "data": [
      {"id": 1, "embedding": [0.1, 0.2, 0.3, 0.4]},
      {"id": 2, "embedding": null},
      {"id": 3}
    ]
  }'

In diesem Beispiel:

  • Entity id = 1 liefert einen gültigen Vektorwert.
  • Entity id = 2 weist dem Feld embedding explizit einen NULL-Wert zu.
  • Entity id = 3 lässt das Feld embedding komplett weg; Milvus speichert es als NULL.

Index-Verhalten bei löschbaren Feldern

Nach dem Einfügen von Daten können Sie wie gewohnt einen Index für ein löschbares Feld erstellen. Der Hauptunterschied ist, wie Milvus NULL-Werte während des Indexaufbaus behandelt:

  • Nur Entitäten mit Nicht-Null-Werten werden dem Index hinzugefügt.
  • Entitäten mit NULL-Werten werden übersprungen und nehmen nicht am Indexaufbau teil.

Für ein nullbares Vektorfeld bedeutet dies, dass nur Entitäten mit gültigen Vektoren durch Vektorähnlichkeit durchsuchbar werden.

# Set index parameters
index_params = client.prepare_index_params()
index_params.add_index(
    field_name="embedding",
    index_type="AUTOINDEX",
    metric_type="COSINE",
)

# Create index
client.create_index(
    collection_name="my_collection",
    index_params=index_params,
)

# Load collection for future search operations
client.load_collection(collection_name="my_collection")
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.collection.request.LoadCollectionReq;
import io.milvus.v2.service.index.request.CreateIndexReq;

import java.util.ArrayList;
import java.util.List;

List<IndexParam> indexes = new ArrayList<>();
indexes.add(IndexParam.builder()
        .fieldName("embedding")
        .indexName("embedding_idx")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .metricType(IndexParam.MetricType.COSINE)
        .build());

client.createIndex(CreateIndexReq.builder()
        .collectionName("my_collection")
        .indexParams(indexes)
        .build());

LoadCollectionReq loadReq = LoadCollectionReq.builder()
        .collectionName("my_collection")
        .build();
client.loadCollection(loadReq);
await client.createIndex({
  collection_name: "my_collection",
  field_name: "embedding",
  index_name: "embedding_idx",
  index_type: "AUTOINDEX",
  metric_type: "COSINE",
});

await client.loadCollection({
  collection_name: "my_collection",
});
import (
    "context"
    "fmt"

    "github.com/milvus-io/milvus/client/v2/entity"
    "github.com/milvus-io/milvus/client/v2/index"
    "github.com/milvus-io/milvus/client/v2/milvusclient"
)

// Assumes `client` is the Milvus client from the Go schema example above.
ctx := context.Background()

indexOption := milvusclient.NewCreateIndexOption("my_collection", "embedding",
    index.NewAutoIndex(entity.COSINE))

_, err := client.CreateIndex(ctx, indexOption)
if err != nil {
    fmt.Println(err.Error())
}

_, err = client.LoadCollection(ctx, milvusclient.NewLoadCollectionOption("my_collection"))
if err != nil {
    fmt.Println(err.Error())
}
curl --request POST \
  --url "${CLUSTER_ENDPOINT}/v2/vectordb/indexes/create" \
  --header "Authorization: Bearer ${TOKEN}" \
  --header "Content-Type: application/json" \
  -d '{
    "collectionName": "my_collection",
    "indexParams": [
      {
        "fieldName": "embedding",
        "metricType": "COSINE",
        "indexType": "AUTOINDEX"
      }
    ]
  }'

curl --request POST \
  --url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/load" \
  --header "Authorization: Bearer ${TOKEN}" \
  --header "Content-Type: application/json" \
  -d '{"collectionName": "my_collection"}'

Zu diesem Zeitpunkt:

  • Entitäten mit gültigen Einbettungswerten sind indiziert und für die Suche bereit.
  • Entitäten, deren Einbettung NULL ist, bleiben in der Sammlung, aber sie werden nicht in den Vektorindex aufgenommen.

Suchverhalten bei löschbaren Feldern

Wenn Sie Suchoperationen auf einem löschbaren Feld durchführen, wertet Milvus nur Entitäten mit Nicht-Null-Werten für das in der Suche verwendete Feld aus. Entitäten, deren Vektorfeld NULL ist, werden automatisch übersprungen.

Für ein löschbares Vektorfeld wie embedding in diesem Beispiel:

  • Nur Entitäten mit gültigen Vektorwerten werden ausgewertet und eingestuft.
  • Entitäten mit NULL-Vektoren verursachen keine Fehler.
  • Wenn die Anzahl der gültigen Vektoren kleiner ist als die angeforderte topK (limit), kann Milvus weniger Ergebnisse als limit zurückgeben.

Das folgende Beispiel führt eine Vektorsuche für das nullbare Vektorfeld embedding durch:

res = client.search(
    collection_name="my_collection",
    data=[[0.1, 0.2, 0.3, 0.4]],
    anns_field="embedding",
    limit=3,
    search_params={"metric_type": "COSINE"},
    output_fields=["embedding"],
)

print(res)
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

Map<String, Object> searchParams = new HashMap<>();
searchParams.put("metric_type", "COSINE");

SearchResp resp = client.search(SearchReq.builder()
        .collectionName("my_collection")
        .annsField("embedding")
        .data(Collections.singletonList(new FloatVec(new float[]{0.1f, 0.2f, 0.3f, 0.4f})))
        .topK(3)
        .searchParams(searchParams)
        .outputFields(Collections.singletonList("embedding"))
        .build());

System.out.println(resp.getSearchResults());
const res = await client.search({
  collection_name: "my_collection",
  data: [[0.1, 0.2, 0.3, 0.4]],
  anns_field: "embedding",
  limit: 3,
  search_params: { metric_type: "COSINE" },
  output_fields: ["embedding"],
});

console.log(res);
import (
    "context"
    "fmt"

    "github.com/milvus-io/milvus/client/v2/entity"
    "github.com/milvus-io/milvus/client/v2/milvusclient"
)

// Assumes `client` is the Milvus client from the Go schema example above.
ctx := context.Background()

query := []float32{0.1, 0.2, 0.3, 0.4}
resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
    "my_collection",
    3,
    []entity.Vector{entity.FloatVector(query)},
).WithANNSField("embedding").
    WithOutputFields("embedding"))
if err != nil {
    fmt.Println(err.Error())
}
fmt.Println(resultSets)
curl --request POST \
  --url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
  --header "Authorization: Bearer ${TOKEN}" \
  --header "Content-Type: application/json" \
  -d '{
    "collectionName": "my_collection",
    "data": [[0.1, 0.2, 0.3, 0.4]],
    "annsField": "embedding",
    "limit": 3,
    "searchParams": {"metricType": "COSINE"},
    "outputFields": ["embedding"]
  }'

Bei dieser Suche:

  • Nur Entitäten mit Nicht-Null-Werten für embedding werden als Kandidaten betrachtet.
  • Entitäten mit NULL-Werten für embedding werden von der Auswertung ausgeschlossen.
  • Die Anzahl der zurückgegebenen Ergebnisse hängt davon ab, wie viele gültige Vektoren in der Sammlung vorhanden sind.

Auswirkungen von Abfrage und Filterung

Die vorherigen Beispiele konzentrieren sich auf Vektorfelder. Dieser Abschnitt beschreibt, wie sich NULL-Werte in skalaren Filterausdrücken verhalten.

Skalare Felder können mit nullable=True definiert werden und folgen den gleichen Einleseregeln wie Vektorfelder. Allerdings werden NULL-Skalarwerte in Filterausdrücken immer als false ausgewertet.

Bei einem nullbaren Skalarfeld age wählt der folgende Filter zum Beispiel Entitäten aus, deren Alter größer als 18 ist:

expr = "age > 18"
String expr = "age > 18";
const expr = "age > 18";
filter := "age > 18"
# Use in query/search filter parameter, for example:
# "filter": "age > 18"

Entitäten, bei denen age NULL ist, werden aus den Ergebnissen ausgeschlossen, da ein NULL-Wert die Filterbedingung nicht erfüllt.

Ebenso passen Gleichheitsprüfungen nicht zu NULL-Werten. Ein Beispiel:

expr = 'status == "active"'
String expr = "status == \"active\"";
const expr = 'status == "active"';
filter := `status == "active"`
# "filter": "status == \"active\""

Entitäten, bei denen status NULL ist, werden von den Ergebnissen ausgeschlossen.

Nullbare Felder und Standardwerte

Wenn sowohl nullable als auch default_value für ein Feld konfiguriert sind, bestimmen die folgenden Regeln, wie Milvus NULL-Eingaben oder fehlende Feldwerte beim Einfügen behandelt.

Nullbar aktiviertStandardwertBenutzereingabe (NULL oder ausgelassen)Ergebnis
JaJa (Nicht-NULL)NULL oder weggelassenVerwendet den Standardwert
JaNeinNULL oder weggelassenWird als NULL gespeichert
NeinJa (Nicht-NULL)NULL oder weggelassenVerwendet den Standardwert
NeinNeinNULL oder weggelassenWirft einen Fehler
NeinJa (NULL Standard)NULL oder weggelassenWirft einen Fehler

Wichtigste Erkenntnisse:

  • Wenn ein Feld einen Nicht-NULL-Standardwert hat, wird dieser Wert unabhängig davon verwendet, ob nullable aktiviert ist.
  • Wenn nullable=True, aber kein Standardwert festgelegt ist, speichert das Feld NULL.
  • Wenn nullable=False und kein Standardwert eingestellt ist, schlägt das Einfügen mit einem Fehler fehl.
  • Das Festlegen eines NULL-Standardwerts für ein nicht-nullbares Feld ist ungültig und verursacht einen Fehler.

Vollständige Beispiele und die API-Verwendung für Standardwerte finden Sie unter Standardwerte.