写点什么

Milvus JSON 实用手册大放送:更简便、更灵活、更贴心

作者:Zilliz
  • 2023-06-30
    上海
  • 本文字数:4605 字

    阅读完需:约 15 分钟

作为全球领先的开源向量数据库,Milvus 一直致力于满足不同用户的场景和需求,聆听社区的声音。最近,我们发现,很多用户的数据中常常包含各种不确定类型的数据,也有用户提出希望以 RESTful API 的方式访问 Milvus。

为此,我们引入了 dynamic field 的概念。通过开启 dynamic field,用户可以自由插入各种类型的数据,包括 Milvus 支持的所有数据类型。即使对于同一个键,在不同行中,其对应的值的类型也可以不同。然而,对于 Milvus 来说,动态增加列的方式过于复杂。因此,Milvus 选择用 JSON DataType 来实现 dynamic field。

简单来说,当用户插入 dynamic field 数据时,Milvus 会将这些数据整合到一个 JSON 中,并在表中新增一个名为 $meta 的列(请注意,不允许用户的动态列中出现以 $meta 为键的数据)。用户的 dynamic data 将会插入到 $meta 列中。

通过这种方式,Milvus 保证了使用的简便性,并提供了动态字段的灵活性,以满足不同用户的需求。

Dynamic Field 定义

所谓 dynamic field 是指不在用户定义的 schema 中的扩展列。例如用户的 collection 只有 id(int64), vector(float_vector) 列,但用户 enable_dynamic_field = True. 这个时候如果用户插入的数据是 {id: 0, vector:[1.0,2.0,...,100.0], x: "abc"},这行数据里多一个 x 列,这个 x 列就会被当做 dynamic field。

Dynamic Field 和 JSON DataType 在 collection 中的体现(以 python sdk 为例)

CreateCollection

要想使用 dynamic field 的功能,需要在建表时开启。其字段名称为 enable_dynamic_field = True.

from pymilvus import (    connections,    utility,    FieldSchema, CollectionSchema, DataType,    Collection,)
connections.connect()
int64_field = FieldSchema(name="int64", dtype=DataType.INT64, is_primary=True)json_field = FieldSchema(name="json", dtype=DataType.JSON)float_vector = FieldSchema(name="float_vector", dtype=DataType.FLOAT_VECTOR, dim=128)
schema = CollectionSchema(fields=[int64_field, json_field, float_vector], enable_dynamic_field=True)hello_milvus = Collection("hello_milvus", schema=schema)
复制代码

在这个例子中,明确指定了三列 schema,其类型分别为 INT64、 JSON、 FLOAT_VECTOR。但由于 enable_dynamic_field = True, Milvus 会在后台新建一列类型为 JSON 的列来存放 动态数据,这个列的列名是 $meta,类型是 JSON。 describe_collection 是不会看到这一列的。

>>> hello_milvus<Collection>:-------------<name>: hello_milvus<description>: <schema>: {'auto_id': False, 'description': '', 'fields': [{'name': 'int64', 'description': '', 'type': <DataType.INT64: 5>, 'is_primary': True, 'auto_id': False}, {'name': 'json', 'description': '', 'type': <DataType.JSON: 23>}, {'name': 'float_vector', 'description': '', 'type': <DataType.FLOAT_VECTOR: 101>, 'params': {'dim': 128}}], 'enable_dynamic_field': True}

复制代码

Insert

可以通过行式插入的方式插入动态数据。

import numpy as nprng = np.random.default_rng(seed=19530)rows = []rows.append({"int64": 0, "float_vector": rng.random((1, 128))[0], "json": {"a": 1, "b": True, "c": "abcd", "d": [1,2,3,4], "e": 100}, "x": "1234", "y": 123, "z": 10.12})rows.append({"int64": 1, "float_vector": rng.random((1, 128))[0], "json": {"a": 2, "b": True, "c": "abc", "d": [2,3,4,5,6], "f": "hh"}, "x": "abcd", "y": 10})rows.append({"int64": 2, "float_vector": rng.random((1, 128))[0], "json": {"a": 3, "b": True, "c": "ab", "d": ["a", "b", "c"], "g": "aa"}, "x": "1234", "z": 228})rows.append({"int64": 3, "float_vector": rng.random((1, 128))[0], "json": {"a": 4, "b": True, "c": "a", "d": ["a", "e", "f"]}, "y": "abcd", "z": "zyx"})rows.append({"int64": 4, "float_vector": rng.random((1, 128))[0], "json": {"a": 5, "b": True, "c": "aa", "d": [8,7,6,5]}, "$x": "1234", "y": 123})rows.append({"int64": 5, "float_vector": rng.random((1, 128))[0], "json": {"a": 6, "b": True, "c": "aaa", "d": [1]}, "x": "abcd", "$y": "haha"})# invalid data, the key of a dynamic field cannot be "$meta".# rows.append({"int64": 0, "float_vector": rng.random((1, 128))[0], "json": {"a": 1, "b": True, "c": "abcd", "d": [1,2,3,4]}, "x": "abcd", "y": "haha", "$meta": "abc"})hello_milvus.insert(rows)
复制代码

在上面的例子中,插入了 6 行数据,其中每一行的数据中都包含 int64, float_vector 和 json 之外,还包含一些其他的动态数据,"x", "y", "z", "�","y" 等。其中 int64, float_vector 和 json 的数据会插入到对应的列中,后面的动态数据每一行会组织成一个 json 格式插入 列中。如第一行����列中。如第一行 meta 列中插入的数据为 {"x": "1234", "y": 123, "z": 10.12}。第二行的数据为 {"x": "abcd", "y": 10},注意在插入的数据中动态数据的 key 不能为 $meta, 否则插入时会报错。

CreateIndex and Load

index_type = "IVF_FLAT"index_params = {"nlist": 128}hello_milvus.create_index("float_vector",                          index_params={"index_type": index_type, "params": index_params, "metric_type": "L2"})hello_milvus.load()
复制代码

Note: 请注意:我们现在还不支持 json 列构建索引。

Search or Query

search 或者 query 时都可以对 JSON field 或者 dynamic field 进行表达式过滤,过滤的方式与之前的标量列类似。但需要注意的几点是:

  1. 不可以直接对 json 列进行过滤,如表达式 json == 1 是非法的。

  2. 如果要访问 json 列的 key,需要明确写清楚列名+[key], 如 json["a"]

expr = r'json["a"] > 3'res = hello_milvus.search([rng.random((1, 128))[0]], "float_vector", {"metric_type": "L2"}, limit=6, expr=expr, output_fields=["$meta"])print(res)
['["id: 3, distance: 17.838302612304688, entity: {\'y\': \'abcd\', \'z\': \'zyx\'}", "id: 4, distance: 19.294292449951172, entity: {\'$x\': \'1234\', \'y\': 123}", "id: 5, distance: 22.257793426513672, entity: {\'x\': \'abcd\', \'$y\': \'haha\'}"]']
复制代码
  1. 如果访问的是 dynamic field 对应的 key,是可以忽略不写 列名的。如上面的 y,表达式可以是 y > 10.milvus 后台会将其 fullback 到 列的,也就是该表达式等价于����列的,也就是该表达式等价于‘meta["y"] > 10`

  2. 如果 json 底层的实际数据与 表达式中的常量类型不匹配,算不命中,不会报错。

expr = r'y > 10'res = hello_milvus.search([rng.random((1, 128))[0]], "float_vector", {"metric_type": "L2"}, limit=3, expr=expr, output_fields=["$meta"])print(res)
['["id: 4, distance: 21.50458526611328, entity: {\'$x\': \'1234\', \'y\': 123}", "id: 0, distance: 21.527101516723633, entity: {\'x\': \'1234\', \'y\': 123, \'z\': 10.12}"]']

复制代码

如该例子中只返回两条 y 的类型是 int 并且大于 10 的数据。

  1. 如果想访问 json 中的 array 数据,可以通过下标来访问,暂不支持直接拿来与 array 直接比较。

  • 表达式 json["d"] == [1,2,3,4] 是不合法的。

  • 表达式可以是 json["d"][0] > 1

expr = r'json["d"][0] > 1'res = hello_milvus.search([rng.random((1, 128))[0]], "float_vector", {"metric_type": "L2"}, limit=6, expr=expr, output_fields=["json"])print(res)
['[\'id: 4, distance: 0.0, entity: {\\\'json\\\': b\\\'{"a":5,"b":true,"c":"aa","d":[8,7,6,5]}\\\'}\', \'id: 1, distance: 21.50458526611328, entity: {\\\'json\\\': b\\\'{"a":2,"b":true,"c":"abc","d":[2,3,4,5,6],"f":"hh"}\\\'}\']']
复制代码
  1. 从上面的例子中可以看到,outputFields 可以指定 schema 中的列,也可以指定是 $meta, 当然也可以是 dynamic field 的 key。如:

expr = r'y > 10'res = hello_milvus.search([rng.random((1, 128))[0]], "float_vector", {"metric_type": "L2"}, limit=6, expr=expr, output_fields=["y"])print(res)
['["id: 0, distance: 18.593538284301758, entity: {\'y\': 123}", "id: 4, distance: 19.290794372558594, entity: {\'y\': 123}"]']
复制代码

这个例子中就只取回了 y 列。

  1. 当我们只想查询 json 或者 dynamic field 中包含某个 key 的数据时,就可以用 exists 表达式进行过滤。

expr = r'exists x'res = hello_milvus.search([rng.random((1, 128))[0]], "float_vector", {"metric_type": "L2"}, limit=6, expr=expr, output_fields=["$meta"])print(res)
['["id: 1, distance: 21.053813934326172, entity: {\'x\': \'abcd\', \'y\': 10}", "id: 0, distance: 21.699125289916992, entity: {\'x\': \'1234\', \'y\': 123, \'z\': 10.12}", "id: 5, distance: 22.007081985473633, entity: {\'x\': \'abcd\', \'$y\': \'haha\'}", "id: 2, distance: 25.92767333984375, entity: {\'x\': \'1234\', \'z\': 228}"]']

expr = r'exists json["g"]'res = hello_milvus.search([rng.random((1, 128))[0]], "float_vector", {"metric_type": "L2"}, limit=6, expr=expr, output_fields=["json"])print(res)
['[\'id: 2, distance: 21.64354705810547, entity: {\\\'json\\\': b\\\'{"a":3,"b":true,"c":"ab","d":["a","b","c"],"g":"aa"}\\\'}\']']
复制代码
  1. 当 dynamic field 的 key 中包含特殊字符时(除 identitier 之外的,即「非字母数字下划线组合」),例如 +, -, *, /, $ 等等特殊符号,如果想用表达式做过滤,需要明确写 $meta[key],例如现在要想对上面插入的 $x 做过滤,表达式只能是 $meta["$x"] == "1234" 这种类型,不能使用 fullback 的那种方式。

expr = r'$meta["$x"] == "1234"'res = hello_milvus.search([rng.random((1, 128))[0]], "float_vector", {"metric_type": "L2"}, limit=6, expr=expr, output_fields=["$meta"])print(res)
['["id: 4, distance: 21.93911361694336, entity: {\'$x\': \'1234\', \'y\': 123}"]']
复制代码

本文作者张财系 Zilliz 软件工程师 🌟全托管 Milvus SaaS/PaaS 即将上线,由 Zilliz 原厂打造!覆盖阿里云、百度智能云、腾讯云、金山云。目前已支持申请试用,企业用户 PoC 申请或其他商务合作请联系 business@zilliz.com。


  • 如果在使用 Milvus 或 Zilliz 产品有任何问题,可添加小助手微信 “zilliz-tech” 加入交流群。

  • 欢迎关注微信公众号“Zilliz”,了解最新资讯。

用户头像

Zilliz

关注

Data Infrastructure for AI Made Easy 2021-10-09 加入

还未添加个人简介

评论

发布
暂无评论
Milvus JSON 实用手册大放送:更简便、更灵活、更贴心_json_Zilliz_InfoQ写作社区