NULL 許容フィールド
Zilliz Cloud は NULL 許容フィールドをサポートしており、フィールド値を欠落させるか、明示的に NULL に設定することができます。NULL 許容性はスキーマレベルで定義され、データの取り込み、インデックス作成、検索、およびクエリ操作全体で一貫して適用されます。
以下のケースで NULL 許容フィールドを使用します:
-
欠損値を許可する外部システムからデータを取り込む場合
-
メタデータの一部がオプションであるか、データセットの一部でのみ利用可能な場合
-
ベクトル埋め込みが非同期に生成され、後で挿入される場合
制限
-
NULL 値を許可するベクトルフィールドは、
IS NULLまたはIS NOT NULLフィルター式をサポートしません。ベクトルフィールドの値が NULL かどうかに基づいてエンティティを明示的にフィルタリングすることはできません。 -
構造体の配列フィールドは NULL 値をサポートしません。構造体の配列フィールド、またはその内部にネストされた任意のフィールドを NULL 許容としてマークすることはできません。
-
nullable属性はフィールド作成時に定義され、後から変更することはできません。既存のフィールドに対して NULL 許容性を有効化または無効化することはできません。 -
NULL 許容としてマークされたフィールドはパーティションキーとして使用できません。パーティションキーフィールドは常に有効な非 NULL 値を含む必要があります。
NULL 許容フィールドとは何か?
Zilliz Cloud では、フィールドが NULL 値を格納できるかどうかは、nullable という名前のスキーマレベルのフィールド属性によって制御されます。
フィールドが nullable=True で定義されている場合、Zilliz Cloud はデータ取り込み中にそのフィールド値の欠落を許可します。実際には、Zilliz Cloud は以下の 2 つの入力を同等として扱い、フィールド値を NULL として格納します:
-
入力エンティティからフィールドが省略されている場合
-
フィールドが明示的に NULL に設定されている場合(例:Python での
None)
フィールドが NULL 許容として定義されていない場合(デフォルトの動作)、すべてのエンティティはそのフィールドに対して有効な値を提供する必要があります。フィールドを省略するか、明示的に NULL 値を割り当てると、挿入またはインポート操作は失敗します。
NULL 許容属性は、コレクションスキーマ内のスカラーフィールドとベクトルフィールドの両方でサポートされています。ただし、構造体の配列フィールドは NULL 許容属性をサポートしません。
NULL 許容性はフィールド値が欠落してもよいかどうかを決定しますが、フィールドが欠落した場合に使用される値を定義するものではありません。
NULL 許容フィールドがデフォルト値なしで構成されている場合、フィールドを省略すると格納される値は NULL になります。
デフォルト値が構成されている場合、Zilliz Cloud は代わりにデフォルト値を格納することがあります。詳細については、デフォルト値 を参照してください。
コレクションスキーマで NULL 許容フィールドを定義する
NULL 許容フィールドを使用するには、コレクションスキーマを定義する際に nullable 属性を有効にする必要があります。
この例では、コレクションスキーマが nullable=True の embedding という名前のベクトルフィールドを定義しています。これにより、コレクション内のエンティティはデータ取り込み中にベクトル値を省略するか、明示的に NULL に設定することができます。
- Python
- Java
- NodeJS
- Go
- cURL
from pymilvus import MilvusClient, DataType
client = MilvusClient(
uri="YOUR_CLUSTER_ENDPOINT",
token="YOUR_CLUSTER_TOKEN"
)
# 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;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("YOUR_CLUSTER_ENDPOINT")
.token("YOUR_CLUSTER_TOKEN")
.build());
CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
.build();
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());
client.createCollection(CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(schema)
.build());
import { MilvusClient, DataType } from '@zilliz/milvus2-sdk-node';
const client = new MilvusClient({
address: 'YOUR_CLUSTER_ENDPOINT',
token: 'YOUR_CLUSTER_TOKEN'
});
await client.createCollection({
collection_name: 'my_collection',
fields: [
{
name: 'id',
data_type: DataType.Int64,
is_primary_key: true
},
{
name: 'embedding',
data_type: DataType.FloatVector,
dim: 4,
nullable: true // Enable the nullable attribute; defaults to false
}
]
});
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: "YOUR_CLUSTER_ENDPOINT",
APIKey: "YOUR_CLUSTER_TOKEN",
})
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
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"collectionName": "my_collection",
"schema": {
"autoID": false,
"fields": [
{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true
},
{
"fieldName": "embedding",
"dataType": "FloatVector",
"elementTypeParams": {
"dim": "4"
},
"nullable": true
}
]
}
}'
このスキーマでは、以下のようになります。
-
embeddingフィールドは明示的に NULL 許容としてマークされています。 -
エンティティは挿入時に
embeddingフィールドを省略するか、NULL 値を割り当てることができます。 -
NULL 値を許容するかどうかの決定は、コレクション作成時に固定されます。
わかりやすくするため、以下の例では NULL 許容のベクトルフィールド(embedding)に焦点を当てています。スカラーフィールドを NULL 許容として定義することは任意であり、このガイドの残りの部分に従うために必須ではありません。
任意:NULL 許容のスカラーフィールドを定義する
スカラーフィールドも同様に nullable 属性を使用して NULL 許容として定義でき、取り込み時にも同じルールに従います。例えば:
- Python
- Java
- NodeJS
- Go
- cURL
schema.add_field(
field_name="age",
datatype=DataType.INT64,
nullable=True,
)
schema.addField(AddFieldReq.builder()
.fieldName("age")
.dataType(DataType.Int64)
.isNullable(true)
.build());
const ageField = {
name: 'age',
data_type: DataType.Int64,
nullable: true
};
schema.WithField(entity.NewField().
WithName("age").
WithDataType(entity.FieldTypeInt64).
WithNullable(true),
)
{
"fieldName": "age",
"dataType": "Int64",
"nullable": true
}
欠損値または NULL 値を含む挿入動作
コレクションスキーマでフィールドが nullable として定義されると、Zilliz Cloud はデータ取り込み時にフィールド値が欠損している場合、または明示的に NULL に設定されていることを許可します。
以下の例では、ステップ 1 で作成したコレクションに 3 つのエンティティを挿入し、これらの異なるケースを示しています。
- Python
- Java
- NodeJS
- Go
- cURL
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.JsonNull;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
import java.util.Arrays;
import java.util.List;
Gson gson = new Gson();
JsonObject row1 = new JsonObject();
row1.addProperty("id", 1);
row1.add("embedding", gson.toJsonTree(Arrays.asList(0.1f, 0.2f, 0.3f, 0.4f)));
JsonObject row2 = new JsonObject();
row2.addProperty("id", 2);
row2.add("embedding", JsonNull.INSTANCE); // Explicitly set to NULL
JsonObject row3 = new JsonObject();
row3.addProperty("id", 3); // Field omitted; stored as NULL
List<JsonObject> data = Arrays.asList(row1, row2, row3);
client.insert(InsertReq.builder()
.collectionName("my_collection")
.data(data)
.build());
const data = [
{
id: 1,
embedding: [0.1, 0.2, 0.3, 0.4]
},
{
id: 2,
embedding: null // Explicitly set to NULL
},
{
id: 3 // Field omitted; stored as NULL
}
];
await client.insert({
collection_name: 'my_collection',
data
});
import (
"fmt"
"github.com/milvus-io/milvus/client/v2/column"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
embeddingCol, err := column.NewNullableColumnFloatVector(
"embedding",
4,
[][]float32{{0.1, 0.2, 0.3, 0.4}},
[]bool{true, false, false},
)
if err != nil {
fmt.Println(err.Error())
// handle error
}
_, err = client.Insert(ctx, milvusclient.NewColumnBasedInsertOption(
"my_collection",
column.NewColumnInt64("id", []int64{1, 2, 3}),
embeddingCol,
))
if err != nil {
fmt.Println(err.Error())
// handle error
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"collectionName": "my_collection",
"data": [
{
"id": 1,
"embedding": [0.1, 0.2, 0.3, 0.4]
},
{
"id": 2,
"embedding": null
},
{
"id": 3
}
]
}'
この例では:
-
エンティティ id = 1 は有効なベクトル値を提供します。
-
エンティティ id = 2 は埋め込みフィールドに明示的に NULL 値を割り当てます。
-
エンティティ id = 3 は埋め込みフィールドを完全に省略します。Zilliz Cloud はこれを NULL として保存します。
インデックスの動作(nullable フィールド)
データを挿入した後、通常どおり nullable フィールドにインデックスを構築できます。主な違いは、インデックス構築中に Zilliz Cloud が NULL 値をどのように処理するかです:
-
非 NULL 値を持つエンティティのみがインデックスに追加されます。
-
NULL 値を持つエンティティはスキップされ、インデックス構築に参加しません。
nullable ベクトルフィールドの場合、これは有効なベクトルを持つエンティティのみがベクトル類似性によって検索可能になることを意味します。
- Python
- Java
- NodeJS
- Go
- cURL
# 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.Collections;
IndexParam indexParam = IndexParam.builder()
.fieldName("embedding")
.indexName("embedding_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.COSINE)
.build();
client.createIndex(CreateIndexReq.builder()
.collectionName("my_collection")
.indexParams(Collections.singletonList(indexParam))
.build());
client.loadCollection(LoadCollectionReq.builder()
.collectionName("my_collection")
.build());
await client.createIndex({
collection_name: 'my_collection',
field_name: 'embedding',
index_type: 'AUTOINDEX',
metric_type: 'COSINE'
});
await client.loadCollection({
collection_name: 'my_collection'
});
import (
"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"
)
indexTask, err := client.CreateIndex(ctx, milvusclient.NewCreateIndexOption(
"my_collection",
"embedding",
index.NewAutoIndex(entity.COSINE),
))
if err != nil {
fmt.Println(err.Error())
// handle error
}
err = indexTask.Await(ctx)
if err != nil {
fmt.Println(err.Error())
// handle error
}
loadTask, err := client.LoadCollection(ctx, milvusclient.NewLoadCollectionOption("my_collection"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
err = loadTask.Await(ctx)
if err != nil {
fmt.Println(err.Error())
// handle error
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/indexes/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"collectionName": "my_collection",
"indexParams": [
{
"fieldName": "embedding",
"indexName": "embedding_index",
"indexType": "AUTOINDEX",
"metricType": "COSINE"
}
]
}'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/load" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"collectionName": "my_collection"
}'
この時点で:
-
有効な
embedding値を持つエンティティはインデックス化され、検索可能な状態になります。 -
embeddingが NULL のエンティティはコレクションに残りますが、ベクトルインデックスには含まれません。
nullable フィールドにおける検索動作
nullable フィールドに対して検索操作を実行すると、Zilliz Cloud は検索に使用されるフィールドの値が NULL でないエンティティのみを評価します。ベクトルフィールドが NULL のエンティティは自動的にスキップされます。
この例のように embedding などの nullable ベクトルフィールドの場合:
-
有効なベクトル値を持つエンティティのみが評価され、ランク付けされます。
-
NULL ベクトルを持つエンティティによってエラーが発生することはありません。
-
有効なベクトルの数が要求された topK(
limit)より少ない場合、Zilliz Cloud はlimitよりも少ない結果を返す可能性があります。
次の例では、nullable ベクトルフィールド embedding に対してベクトル検索を実行します:
- Python
- Java
- NodeJS
- Go
- cURL
res = client.search(
collection_name="my_collection",
data=[[0.1, 0.2, 0.3, 0.4]],
anns_field="embedding",
limit=3,
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.Arrays;
import java.util.Collections;
SearchResp res = client.search(SearchReq.builder()
.collectionName("my_collection")
.data(Collections.singletonList(new FloatVec(Arrays.asList(0.1f, 0.2f, 0.3f, 0.4f))))
.annsField("embedding")
.limit(3)
.outputFields(Collections.singletonList("embedding"))
.build());
System.out.println(res);
const res = await client.search({
collection_name: 'my_collection',
data: [[0.1, 0.2, 0.3, 0.4]],
anns_field: 'embedding',
limit: 3,
output_fields: ['embedding']
});
console.log(res);
import (
"fmt"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
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())
// handle error
}
fmt.Println(resultSets)
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"collectionName": "my_collection",
"data": [[0.1, 0.2, 0.3, 0.4]],
"annsField": "embedding",
"limit": 3,
"outputFields": ["embedding"]
}'
この検索では:
-
embeddingの値が NULL でないエンティティのみが候補として考慮されます。 -
embeddingの値が NULL であるエンティティは評価から除外されます。 -
返される結果の数は、コレクション内に存在する有効なベクトルの数に依存します。
クエリとフィルタリングへの影響
前述の例はベクトルフィールドに焦点を当てています。このセクションでは、スカラー フィルター式における NULL 値の動作について説明します。
スカラー フィールドは nullable=True として定義でき、ベクトルフィールドと同様の取り込みルールに従います。ただし、NULL のスカラー値はフィルター式において常に false と評価されます。
たとえば、nullable なスカラー フィールド age がある場合、次のフィルターは age が 18 より大きいエンティティを選択します:
- Python
- Java
- NodeJS
- Go
- cURL
expr = "age > 18"
String filter = "age > 18";
const filter = 'age > 18';
filter := "age > 18"
"filter": "age > 18"
age が NULL であるエンティティは、NULL 値がフィルター条件を満たさないため、結果から除外されます。
同様に、等価チェックは NULL 値と一致しません。例えば:
- Python
- Java
- NodeJS
- Go
- cURL
expr = "status == \"active\""
String filter = "status == \"active\"";
const filter = 'status == "active"';
filter := \`status == "active"\`
"filter": "status == \"active\""
status が NULL であるエンティティは結果から除外されます。
Applicable rules
フィールドに対して nullable と default_value の両方が構成されている場合、以下のルールにより、挿入時に Zilliz Cloud が NULL 入力または欠落したフィールド値をどのように処理するかが決定されます。
NULL 許容 | デフォルト値 | ユーザー入力 | 結果 |
|---|---|---|---|
✅ | ✅ (NULL 以外) | NULL または省略 | デフォルト値が使用されます |
✅ | ❌ | NULL または省略 | NULL として保存されます |
❌ | ✅ (NULL 以外) | NULL または省略 | デフォルト値が使用されます |
❌ | ❌ | NULL または省略 | エラーが発生します |
❌ | ✅ (NULL) | NULL または省略 | エラーが発生します |
主なポイント:
-
フィールドに NULL 以外のデフォルト値がある場合、
nullableが有効かどうかに関わらず、その値が使用されます。 -
nullable=Trueだがデフォルト値が設定されていない場合、フィールドには NULL が保存されます。 -
nullable=Falseかつデフォルト値が設定されていない場合、挿入はエラーで失敗します。 -
NULL 許容ではないフィールドに NULL のデフォルト値を設定することは無効であり、エラーの原因となります。