// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// 移植自 milvus-io/milvus client/entity/schema.go(`ReadProto` /
// `ProtoMessage`)与 client/milvusclient/collection_options.go
// (Apache-2.0)。
//
// 协议无关的领域类型在 `@entity`,wire 类型在生成的 `@schema` 里,
// 这一层是两者的桥。数值常量与 `milvus.proto.schema.DataType` 逐条对齐,
// 改动等于改协议。
///|
/// `@entity.DataType` → `@schema.DataType`。
///
/// 生成的枚举把 `JSON` / `UUID` 拼成全大写,与上游 proto 一致;
/// `@entity` 侧用惯常的 `Json` / `Uuid`。两边新增类型时这里都要动。
pub fn data_type_to_proto(value : @entity.DataType) -> @schema.DataType {
match value {
None => @schema.DataType::None
Bool => @schema.DataType::Bool
Int8 => @schema.DataType::Int8
Int16 => @schema.DataType::Int16
Int32 => @schema.DataType::Int32
Int64 => @schema.DataType::Int64
Float => @schema.DataType::Float
Double => @schema.DataType::Double
String => @schema.DataType::String
VarChar => @schema.DataType::VarChar
Array => @schema.DataType::Array
Json => @schema.DataType::JSON
Geometry => @schema.DataType::Geometry
Text => @schema.DataType::Text
Timestamptz => @schema.DataType::Timestamptz
Mol => @schema.DataType::Mol
Date => @schema.DataType::Date
Time => @schema.DataType::Time
Decimal => @schema.DataType::Decimal
Uuid => @schema.DataType::UUID
BinaryVector => @schema.DataType::BinaryVector
FloatVector => @schema.DataType::FloatVector
Float16Vector => @schema.DataType::Float16Vector
BFloat16Vector => @schema.DataType::BFloat16Vector
SparseFloatVector => @schema.DataType::SparseFloatVector
Int8Vector => @schema.DataType::Int8Vector
ArrayOfVector => @schema.DataType::ArrayOfVector
ArrayOfStruct => @schema.DataType::ArrayOfStruct
Struct => @schema.DataType::Struct
}
}
///|
/// `@schema.DataType` → `@entity.DataType?`。
///
/// 认不出的值(协议比本地新)返回 `None`,由调用方决定是跳过这个字段还是
/// 报错 —— 静默当成某个已知类型会把未知字段读歪。
pub fn data_type_from_proto(value : @schema.DataType) -> @entity.DataType? {
match value {
@schema.DataType::None => Some(@entity.DataType::None)
@schema.DataType::Bool => Some(@entity.DataType::Bool)
@schema.DataType::Int8 => Some(@entity.DataType::Int8)
@schema.DataType::Int16 => Some(@entity.DataType::Int16)
@schema.DataType::Int32 => Some(@entity.DataType::Int32)
@schema.DataType::Int64 => Some(@entity.DataType::Int64)
@schema.DataType::Float => Some(@entity.DataType::Float)
@schema.DataType::Double => Some(@entity.DataType::Double)
@schema.DataType::String => Some(@entity.DataType::String)
@schema.DataType::VarChar => Some(@entity.DataType::VarChar)
@schema.DataType::Array => Some(@entity.DataType::Array)
@schema.DataType::JSON => Some(@entity.DataType::Json)
@schema.DataType::Geometry => Some(@entity.DataType::Geometry)
@schema.DataType::Text => Some(@entity.DataType::Text)
@schema.DataType::Timestamptz => Some(@entity.DataType::Timestamptz)
@schema.DataType::Mol => Some(@entity.DataType::Mol)
@schema.DataType::Date => Some(@entity.DataType::Date)
@schema.DataType::Time => Some(@entity.DataType::Time)
@schema.DataType::Decimal => Some(@entity.DataType::Decimal)
@schema.DataType::UUID => Some(@entity.DataType::Uuid)
@schema.DataType::BinaryVector => Some(@entity.DataType::BinaryVector)
@schema.DataType::FloatVector => Some(@entity.DataType::FloatVector)
@schema.DataType::Float16Vector => Some(@entity.DataType::Float16Vector)
@schema.DataType::BFloat16Vector => Some(@entity.DataType::BFloat16Vector)
@schema.DataType::SparseFloatVector =>
Some(@entity.DataType::SparseFloatVector)
@schema.DataType::Int8Vector => Some(@entity.DataType::Int8Vector)
@schema.DataType::ArrayOfVector => Some(@entity.DataType::ArrayOfVector)
@schema.DataType::ArrayOfStruct => Some(@entity.DataType::ArrayOfStruct)
@schema.DataType::Struct => Some(@entity.DataType::Struct)
_ => None
}
}
///|
/// 一个 `@entity.Field` → `@schema.FieldSchema`。
///
/// `dim` / `max_length` 不是 `FieldSchema` 的字段:服务端从 `type_params`
/// 里读,所以这里也把它俩合并进 `type_params`,顺序是 dim 在前。
pub fn field_to_proto(field : @entity.Field) -> @schema.FieldSchema {
let params : Array[@common.KeyValuePair] = []
match field.dim {
Some(dim) =>
params.push(
@common.KeyValuePair::KeyValuePair(@entity.dim_key, dim.to_string()),
)
None => ()
}
match field.max_length {
Some(len) =>
params.push(
@common.KeyValuePair::KeyValuePair(
@entity.max_length_key,
len.to_string(),
),
)
None => ()
}
for pair in field.type_params {
params.push(@common.KeyValuePair::KeyValuePair(pair.0, pair.1))
}
let element_type = match field.element_type {
Some(t) => data_type_to_proto(t)
None => @schema.DataType::None
}
@schema.FieldSchema::{
field_id: field.field_id,
name: field.name,
is_primary_key: field.is_primary_key,
description: field.description,
data_type: data_type_to_proto(field.data_type),
type_params: params,
index_params: [],
auto_id: field.auto_id,
state: @schema.FieldState::FieldCreated,
element_type,
default_value: None,
is_dynamic: field.is_dynamic,
is_partition_key: field.is_partition_key,
is_clustering_key: field.is_clustering_key,
nullable: field.nullable,
is_function_output: false,
external_field: "",
type_schema: None,
element_nullable: false,
}
}
///|
/// `@entity.CollectionSchema` → `@schema.CollectionSchema`。
///
/// `name` 单独传:`@entity.CollectionSchema` 里没有集合名这一项,
/// 上游也是把名字留在 `CreateCollectionOption` 上再补进 proto。
pub fn schema_to_proto(
name : String,
schema : @entity.CollectionSchema,
) -> @schema.CollectionSchema {
@schema.CollectionSchema::{
name,
description: schema.description,
auto_id: false,
fields: schema.fields.map(field_to_proto),
enable_dynamic_field: schema.enable_dynamic_field,
properties: [],
functions: [],
db_name: "",
struct_array_fields: [],
version: 0,
external_source: "",
external_spec: "",
do_physical_backfill: false,
file_resource_ids: [],
enable_namespace: false,
}
}
///|
/// 编码 `CreateCollectionRequest.schema` 那个 `bytes` 字段。
pub fn serialize_collection_schema(
name : String,
schema : @entity.CollectionSchema,
) -> Bytes raise ClientError {
encode_request(schema_to_proto(name, schema))
}
///|
/// `type_params` 里某个键的整数值;缺键或非数字都算 0。
///
/// 上游 `entity.ReadProto` 用 `strconv.ParseInt` 并吞掉错误,
/// 这里同样不报错:`type_params` 是自由格式,服务端可能塞进任意键。
fn type_param_int(params : Array[@common.KeyValuePair], key : String) -> Int {
for pair in params {
if pair.key == key {
return @string.parse_int(pair.value[:]) catch { _ => 0 }
}
}
0
}
///|
/// `@schema.FieldSchema` → `@entity.Field`。
///
/// 生成物的 `element_type` 是必填枚举、缺省 `None`,所以这里用
/// `data_type_from_proto(...)` 之后再判断是否真的设了元素类型。
pub fn field_from_proto(
proto : @schema.FieldSchema,
) -> @entity.Field raise ClientError {
let data_type = match data_type_from_proto(proto.data_type) {
Some(t) => t
None =>
raise ClientError::Decode(
"field \{proto.name} has unknown data type \{proto.data_type.to_enum().0}",
)
}
let mut element_type : @entity.DataType? = None
if proto.element_type != @schema.DataType::None {
match data_type_from_proto(proto.element_type) {
Some(t) => element_type = Some(t)
None =>
raise ClientError::Decode(
"field \{proto.name} has unknown element type \{proto.element_type.to_enum().0}",
)
}
}
let dim = type_param_int(proto.type_params, @entity.dim_key)
let max_length = type_param_int(proto.type_params, @entity.max_length_key)
let extra : Array[(String, String)] = []
for pair in proto.type_params {
if pair.key != @entity.dim_key && pair.key != @entity.max_length_key {
extra.push((pair.key, pair.value))
}
}
{
name: proto.name,
data_type,
field_id: proto.field_id,
is_primary_key: proto.is_primary_key,
auto_id: proto.auto_id,
description: proto.description,
is_dynamic: proto.is_dynamic,
is_partition_key: proto.is_partition_key,
is_clustering_key: proto.is_clustering_key,
nullable: proto.nullable,
element_type,
dim: if dim == 0 {
None
} else {
Some(dim)
},
max_length: if max_length == 0 {
None
} else {
Some(max_length)
},
type_params: extra,
}
}
///|
/// `@entity.SchemaError` 的文本。它在 `@entity` 里只 derive 了 `Eq, Debug`,
/// 包外拿不到内部字符串,所以这里按 Debug 展开取出来。
/// 校验错误是给人看的,包一层 `Debug(...)` 也比只写「schema 不合法」强。
pub fn entity_schema_message(err : @entity.SchemaError) -> String {
match err {
SchemaError(msg) => msg
}
}