JSON 与哈希存储
使用RedisVL存储JSON和哈希
开箱即用,Redis 提供了多种数据结构,可用于您的特定领域应用和用例。 在本文档中,您将学习如何将 RedisVL 与哈希和JSON数据一起使用。
注意:
本文档是这个Jupyter笔记本的转换形式。在开始之前,请确保以下事项:
- 您已经安装了RedisVL并激活了该环境。
- 您有一个运行中的Redis实例,具备Redis查询引擎功能。
# import necessary modules
import pickle
from redisvl.redis.utils import buffer_to_array
from jupyterutils import result_print, table_print
from redisvl.index import SearchIndex
# load in the example data and printing utils
data = pickle.load(open("hybrid_example_data.pkl", "rb"))
table_print(data)
用户 | 年龄 | 工作 | 信用评分 | 办公室位置 | 用户嵌入 |
---|---|---|---|---|---|
john | 18 | 工程师 | 高 | -122.4194,37.7749 | b'\xcd\xcc\xcc=\xcd\xcc\xcc=\x00\x00\x00?' |
derrick | 14 | 医生 | 低 | -122.4194,37.7749 | b'\xcd\xcc\xcc=\xcd\xcc\xcc=\x00\x00\x00?' |
nancy | 94 | 医生 | 高 | -122.4194,37.7749 | b'333?\xcd\xcc\xcc=\x00\x00\x00?' |
tyler | 100 | 工程师 | 高 | -122.0839,37.3861 | b'\xcd\xcc\xcc=\xcd\xcc\xcc>\x00\x00\x00?' |
tim | 12 | 皮肤科医生 | 高 | -122.0839,37.3861 | b'\xcd\xcc\xcc>\xcd\xcc\xcc>\x00\x00\x00?' |
taimur | 15 | 首席执行官 | 低 | -122.0839,37.3861 | b'\x9a\x99\x19?\xcd\xcc\xcc=\x00\x00\x00?' |
joe | 35 | 牙医 | 中等 | -122.0839,37.3861 | b'fff?fff?\xcd\xcc\xcc=' |
哈希还是JSON - 如何选择?
两种存储选项都提供了多种功能和权衡。下面,您将通过一个虚拟数据集来学习何时以及如何使用这两种数据类型。
处理哈希
Redis中的哈希是简单的字段-值对集合。可以将其视为一个可变的、单层级的字典,其中包含多个“行”:
{
"model": "Deimos",
"brand": "Ergonom",
"type": "Enduro bikes",
"price": 4972,
}
哈希最适合具有以下特征的用例:
- 性能(速度)和存储空间(内存消耗)是最重要的关注点。
- 数据可以轻松地标准化并建模为单级字典。
哈希通常是默认的推荐。
# define the hash index schema
hash_schema = {
"index": {
"name": "user-hash",
"prefix": "user-hash-docs",
"storage_type": "hash", # default setting -- HASH
},
"fields": [
{"name": "user", "type": "tag"},
{"name": "credit_score", "type": "tag"},
{"name": "job", "type": "text"},
{"name": "age", "type": "numeric"},
{"name": "office_location", "type": "geo"},
{
"name": "user_embedding",
"type": "vector",
"attrs": {
"dims": 3,
"distance_metric": "cosine",
"algorithm": "flat",
"datatype": "float32"
}
}
],
}
# construct a search index from the hash schema
hindex = SearchIndex.from_dict(hash_schema)
# connect to local redis instance
hindex.connect("redis://localhost:6379")
# create the index (no data yet)
hindex.create(overwrite=True)
# show the underlying storage type
hindex.storage_type
<StorageType.HASH: 'hash'>
向量作为字节字符串
在Redis中使用哈希时的一个细微差别是,所有向量化数据必须作为字节字符串传递(以实现高效的存储、索引和处理)。下面可以看到一个例子:
# show a single entry from the data that will be loaded
data[0]
{'user': 'john',
'age': 18,
'job': 'engineer',
'credit_score': 'high',
'office_location': '-122.4194,37.7749',
'user_embedding': b'\xcd\xcc\xcc=\xcd\xcc\xcc=\x00\x00\x00?'}
# load hash data
keys = hindex.load(data)
$ rvl stats -i user-hash
Statistics:
╭─────────────────────────────┬─────────────╮
│ Stat Key │ Value │
├─────────────────────────────┼─────────────┤
│ num_docs │ 7 │
│ num_terms │ 6 │
│ max_doc_id │ 7 │
│ num_records │ 44 │
│ percent_indexed │ 1 │
│ hash_indexing_failures │ 0 │
│ number_of_uses │ 1 │
│ bytes_per_record_avg │ 3.40909 │
│ doc_table_size_mb │ 0.000767708 │
│ inverted_sz_mb │ 0.000143051 │
│ key_table_size_mb │ 0.000248909 │
│ offset_bits_per_record_avg │ 8 │
│ offset_vectors_sz_mb │ 8.58307e-06 │
│ offsets_per_term_avg │ 0.204545 │
│ records_per_doc_avg │ 6.28571 │
│ sortable_values_size_mb │ 0 │
│ total_indexing_time │ 0.587 │
│ total_inverted_index_blocks │ 18 │
│ vector_index_sz_mb │ 0.0202332 │
╰─────────────────────────────┴─────────────╯
执行查询
一旦索引创建完成并且数据加载到正确的格式中,您就可以对索引运行查询:
from redisvl.query import VectorQuery
from redisvl.query.filter import Tag, Text, Num
t = (Tag("credit_score") == "high") & (Text("job") % "enginee*") & (Num("age") > 17)
v = VectorQuery([0.1, 0.1, 0.5],
"user_embedding",
return_fields=["user", "credit_score", "age", "job", "office_location"],
filter_expression=t)
results = hindex.query(v)
result_print(results)
向量距离 | 用户 | 信用评分 | 年龄 | 工作 | 办公地点 |
---|---|---|---|---|---|
0 | john | 高 | 18 | 工程师 | -122.4194,37.7749 |
0.109129190445 | tyler | 高 | 100 | 工程师 | -122.0839,37.3861 |
# clean up
hindex.delete()
处理JSON
Redis 还支持原生的 JSON 对象。这些对象可以是多级(嵌套)的,并且完全支持 JSONPath 用于检索和更新子元素:
{
"name": "bike",
"metadata": {
"model": "Deimos",
"brand": "Ergonom",
"type": "Enduro bikes",
"price": 4972,
}
}
JSON 最适合具有以下特征的用例:
- 易用性和数据模型灵活性是首要关注点。
- 应用程序数据已经是原生JSON。
- 替换另一个文档存储/数据库解决方案。
完整的JSON路径支持
因为Redis支持完整的JSONPath,所以在创建索引模式时,需要通过指向数据在对象中位置的所需name
和path
来索引和选择元素。
注意:
默认情况下,如果未在JSON字段模式中提供路径,RedisVL将假定路径为 $.{name}
。# define the json index schema
json_schema = {
"index": {
"name": "user-json",
"prefix": "user-json-docs",
"storage_type": "json", # JSON storage type
},
"fields": [
{"name": "user", "type": "tag"},
{"name": "credit_score", "type": "tag"},
{"name": "job", "type": "text"},
{"name": "age", "type": "numeric"},
{"name": "office_location", "type": "geo"},
{
"name": "user_embedding",
"type": "vector",
"attrs": {
"dims": 3,
"distance_metric": "cosine",
"algorithm": "flat",
"datatype": "float32"
}
}
],
}
# construct a search index from the JSON schema
jindex = SearchIndex.from_dict(json_schema)
# connect to a local redis instance
jindex.connect("redis://localhost:6379")
# create the index (no data yet)
jindex.create(overwrite=True)
# note the multiple indices in the same database
$ rvl index listall
20:23:08 [RedisVL] INFO Indices:
20:23:08 [RedisVL] INFO 1. user-json
#### Vectors as float arrays
Vectorized data stored in JSON must be stored as a pure array (e.g., a Python list) of floats. Modify your sample data to account for this below:
```python
import numpy as np
json_data = data.copy()
for d in json_data:
d['user_embedding'] = buffer_to_array(d['user_embedding'], dtype=np.float32)
# inspect a single JSON record
json_data[0]
{'user': 'john',
'age': 18,
'job': 'engineer',
'credit_score': 'high',
'office_location': '-122.4194,37.7749',
'user_embedding': [0.10000000149011612, 0.10000000149011612, 0.5]}
keys = jindex.load(json_data)
# we can now run the exact same query as above
result_print(jindex.query(v))
向量距离 | 用户 | 信用评分 | 年龄 | 工作 | 办公地点 |
---|---|---|---|---|---|
0 | john | 高 | 18 | 工程师 | -122.4194,37.7749 |
0.109129190445 | tyler | 高 | 100 | 工程师 | -122.0839,37.3861 |
清理
jindex.delete()