Redis 作为矢量数据库快速入门指南
了解如何使用 Redis 作为矢量数据库
本快速入门指南可帮助您:
- 了解矢量数据库是什么
- 创建Redis向量数据库
- 创建向量嵌入并存储向量
- 查询数据并执行向量搜索
了解矢量数据库
数据通常是非结构化的,这意味着它没有用明确定义的模式来描述。非结构化数据的示例包括文本段落、图像、视频或音频。存储和搜索非结构化数据的一种方法是使用向量嵌入。
什么是向量?在机器学习和人工智能中,向量是表示数据的数字序列。它们是模型的输入和输出,以数字形式封装底层信息。向量将非结构化数据(例如文本、图像、视频和音频)转换为机器学习模型可以处理的格式。
- 为什么它们如此重要?向量可以捕捉数据中固有的复杂模式和语义含义,使其成为各种应用的强大工具。它们使机器学习模型能够更有效地理解和操作非结构化数据。
- 增强传统搜索。传统的关键字或词汇搜索依赖于单词或短语的精确匹配,这可能会受到限制。相比之下,向量搜索或语义搜索利用向量嵌入中捕获的丰富信息。通过将数据映射到向量空间,相似的项目会根据其含义彼此靠近。这种方法可以实现更准确、更有意义的搜索结果,因为它考虑了查询的上下文和语义内容,而不仅仅是使用的确切单词。
创建Redis向量数据库
您可以使用Redis Stack作为矢量数据库。它允许您:
- 将向量和相关元数据存储在哈希或JSON文档中
- 创建并配置搜索的二级索引
- 执行矢量搜索
- 更新向量和元数据
- 删除和清理
最简单的入门方法是使用 Redis Cloud:
-
创建一个免费帐户。
-
按照说明创建一个免费数据库。
这个免费的 Redis Cloud 数据库开箱即用,具有 Redis Stack 的所有功能。
您也可以使用安装指南在本地机器上安装 Redis Stack。
您需要为您的 Redis 服务器配置以下功能:JSON 和搜索查询。
安装所需的 Python 包
创建 Python 虚拟环境并使用以下依赖项安装pip:
redis:您可以redis-py在此文档站点的客户端部分找到有关客户端库的更多详细信息。pandas:Pandas 是一个数据分析库。sentence-transformers:您将使用SentenceTransformers框架在全文上生成嵌入。tabulate:pandas用于tabulate渲染Markdown。
您还需要在 Python 代码中进行以下导入:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
连接
连接到 Redis。默认情况下,Redis 返回二进制响应。要解码它们,请将decode_responses参数集传递给True:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
us-east-1并侦听端口 16379 的 Cloud 数据库的示例连接字符串:redis-16379.c283.us-east-1-4.ec2.cloud.redislabs.com:16379。连接字符串的格式为host:port。您还必须复制并粘贴 Cloud 数据库的用户名和密码。然后,用于使用默认用户连接的代码行将更改为client = redis.Redis(host="redis-16379.c283.us-east-1-4.ec2.cloud.redislabs.com", port=16379, password="your_password_here" decode_responses=True)。准备演示数据集
本快速入门指南还使用了自行车数据集。以下是其中的一个示例文档:
{
"model": "Jigger",
"brand": "Velorim",
"price": 270,
"type": "Kids bikes",
"specs": {
"material": "aluminium",
"weight": "10"
},
"description": "Small and powerful, the Jigger is the best ride for the smallest of tikes! ..."
}
该description字段包含自行车的自由格式的文本描述,并将用于创建矢量嵌入。
1. 获取演示数据
您首先需要将演示数据集作为 JSON 数组获取:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
检查其中一个自行车 JSON 文档的结构:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
2. 将演示数据存储在 Redis 中
现在使用JSON.SET命令迭代数组bikes ,将数据存储为Redis 中的JSON文档。以下代码使用管道来最大限度地减少网络往返时间:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
加载后,您可以使用JSONPath表达式从 Redis 中的某个 JSON 文档中检索特定属性:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
3. 选择文本嵌入模型
HuggingFace拥有大量可通过SentenceTransformers框架在本地提供的文本嵌入模型。这里我们使用在搜索引擎、聊天机器人和其他 AI 应用中广泛使用的MS MARCO模型。
from sentence_transformers import SentenceTransformer
embedder = SentenceTransformer('msmarco-distilbert-base-v4')
4. 生成文本嵌入
迭代具有前缀的所有 Redis 键bikes::
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
使用键作为JSON.MGET命令的输入,以及字段$.description,以列表形式收集描述。然后,将描述列表传递给方法.encode():
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
使用JSON.SET命令将矢量化描述插入 Redis 中的自行车文档。以下命令将新字段插入 JSONPath 下的每个文档中$.description_embeddings。再次使用管道执行此操作以避免不必要的网络往返:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
使用JSON.GET命令检查其中一份更新的自行车文档:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
创建索引
1. 使用向量字段创建索引
您必须创建索引才能查询文档元数据或执行向量搜索。使用FT.CREATE命令:
FT.CREATE idx:bikes_vss ON JSON
PREFIX 1 bikes: SCORE 1.0
SCHEMA
$.model TEXT WEIGHT 1.0 NOSTEM
$.brand TEXT WEIGHT 1.0 NOSTEM
$.price NUMERIC
$.type TAG SEPARATOR ","
$.description AS description TEXT WEIGHT 1.0
$.description_embeddings AS vector VECTOR FLAT 6 TYPE FLOAT32 DIM 768 DISTANCE_METRIC COSINE"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
以下是字段定义的细分VECTOR:
$.description_embeddings AS vector:矢量字段的JSON路径及其字段别名vector。FLAT:指定索引方法,可以是平面索引,也可以是分层可导航小世界图(HNSW)。TYPE FLOAT32:设置向量组件的浮点精度,在本例中为 32 位浮点数。DIM 768:嵌入的长度或尺寸,由所选的嵌入模型决定。DISTANCE_METRIC COSINE:选择的距离函数:余弦距离。
您可以在矢量参考文档中找到有关所有这些选项的更多详细信息。
2.检查索引的状态
执行FT.CREATE命令后,索引过程将在后台运行。在很短的时间内,所有 JSON 文档都应已编入索引并可供查询。要验证这一点,您可以使用FT.INFO命令,该命令提供有关索引的详细信息和统计信息。特别值得关注的是成功编入索引的文档数量和失败的文档数量:
FT.INFO idx:bikes_vss"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
执行矢量搜索
本快速入门指南主要介绍向量搜索。不过,您可以在文档数据库快速入门指南中了解有关如何基于文档元数据进行查询的更多信息。
1. 嵌入您的查询
以下代码片段显示了在 Redis 中执行向量搜索所使用的文本查询列表:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
首先,使用相同的 SentenceTransformers 模型将每个输入查询编码为向量嵌入:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
2. K最近邻(KNN)搜索
KNN 算法根据所选的距离函数计算查询向量与 Redis 中每个向量之间的距离。然后返回与查询向量距离最小的前 K 个项目。这些是语义上最相似的项目。
现在构建一个查询来执行此操作:
query = (
Query('(*)=>[KNN 3 @vector $query_vector AS vector_score]')
.sort_by('vector_score')
.return_fields('vector_score', 'id', 'brand', 'model', 'description')
.dialect(2)
)
让我们分解一下上面的查询模板:
- 过滤表达式的
(*)意思是all。换句话说,没有应用过滤。您可以将其替换为按其他元数据过滤的表达式。 KNN查询部分搜索前 3 个最近的邻居。- 必须将查询向量作为参数传入
query_vector。 - 到查询向量的距离返回为
vector_score。 - 结果按此排序
vector_score。 - 最后,它返回每个结果的字段
vector_score、id、brand、model和。description
FT.SEARCH,必须指定 DIALECT 2 或更高版本。您必须将矢量化查询作为带有参数 name 的字节数组传递query_vector。以下代码从查询向量创建一个 Python NumPy 数组,并将其转换为紧凑的字节级表示,可以将其作为参数传递给查询:
client.ft('idx:bikes_vss').search(
query,
{
'query_vector': np.array(encoded_query, dtype=np.float32).tobytes()
}
).docs
有了查询模板后,您可以循环执行所有查询。请注意,脚本将vector_score每个结果的计算为1 - doc.vector_score。由于使用余弦距离作为度量,因此距离最小的项目更接近查询,因此与查询更相似。
然后,循环匹配的文档并创建一个结果列表,该列表可以转换为 Pandas 表以可视化结果:
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
查询结果显示各个查询的前三个匹配项(我们的 K 参数)以及每个查询的自行车的 ID、品牌和型号。
例如,对于查询“最适合儿童的山地自行车”,相似度得分最高 ( 0.54),因此最接近的匹配是 'Nord' 品牌的 'Chook air 5' 自行车型号,描述如下:
Chook Air 5 为 6 岁及以上的儿童提供了一款耐用且超轻的山地自行车,让他们首次体验在赛道上骑行以及轻松穿越森林和田野的乐趣。较低的上管让您可以在任何情况下轻松上下车,让您的孩子在小径上更加安全。Chook Air 5 是山地自行车的完美入门之选。
从描述来看,这辆自行车非常适合年幼的孩子,并且嵌入准确地捕捉到了描述的语义。
"""
Code samples for vector database quickstart pages:
https://redis.github.net.cn/docs/latest/develop/get-started/vector-database/
"""
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()
json.dumps(bikes[0], indent=2)
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
res = client.ping()
# >>> True
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }
schema = (
TextField("$.model", no_stem=True, as_name="model"),
TextField("$.brand", no_stem=True, as_name="brand"),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type"),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": VECTOR_DIMENSION,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
res = client.ft("idx:bikes_vss").create_index(fields=schema, definition=definition)
# >>> 'OK'
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures
query = Query("@brand:Peaknetic")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950', 'description_embeddings': ...
query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:008',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Soothe Electric bike',
# 'price': '1950'
# },
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
"id", "brand", "model", "price"
)
res = client.ft("idx:bikes_vss").search(query).docs
# print(res)
# >>> [
# Document {
# 'id': 'bikes:009',
# 'payload': None,
# 'brand': 'Peaknetic',
# 'model': 'Secto',
# 'price': '430'
# }
# ]
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
Creates a query table.
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# Optional: convert the table to Markdown using Pandas
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...
hybrid_query = (
Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(hybrid_query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.3 | bikes:008...
range_query = (
Query(
"@vector:[VECTOR_RANGE $range $query_vector]=>"
"{$YIELD_DISTANCE_AS: vector_score}"
)
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.paging(0, 4)
.dialect(2)
)
table = create_query_table(
range_query, queries[:1],
encoded_queries[:1],
{"range": 0.55}
)
print(table)
# >>> | Bike for small kids | 0.52 | bikes:001 | Velorim |...
| 询问 | 分数 | ID | 品牌 | 模型 | 描述 |
|---|---|---|---|---|---|
| 最适合儿童的山地自行车 | 0.54 | 自行车:003 | 诺德 | 鸡空气 5 | Chook Air 5 为 6 岁及以上的儿童提供了一款耐用且超轻的山地自行车,让他们首次体验在赛道上骑行以及轻松穿越森林和田野的乐趣。较低的上管让您可以在任何情况下轻松上下车,让您的孩子在小径上更加安全。Chook Air 5 是山地自行车的完美入门之选。 |
| 0.51 | 自行车:010 | 希尔 | 首脑 | nHill 的这款经济型山地车在自行车道和小径上均表现良好。行程为 100 毫米的前叉可吸收崎岖地形。宽大的 Kenda Booster 轮胎让您在转弯和湿滑的小径上都能抓地。Shimano Tourney 传动系统提供了足够的齿轮,让您能够以舒适的速度骑行上坡,而 Tektro 液压盘式制动器制动平稳。无论您是想要一辆既可以上班又可以在周末进行越野骑行的经济型自行车,还是您只是想要一辆稳定的自行车…… | |
| 0.46 | 自行车:001 | 维洛里姆 | 跳汰机 | Jigger 体型小巧,动力十足,是小孩子的最佳选择!这是市场上最小的儿童踏板自行车,没有脚刹,是少数顽强的小骑手的首选。我们说罕见,是因为这款小巧的自行车不适合紧张的初次骑手,但对于真正的速度狂来说,它是一款真正的刺激之车。Jigger 是一款 12 英寸的轻型儿童自行车,它将满足您的小朋友对速度的需求。它是单轮车…… |
后续步骤Next steps
- 您可以通过阅读向量参考文档了解有关查询选项的更多信息,例如过滤器和向量范围查询。
- 完整的搜索和查询文档可能会让您感兴趣。
- 如果您想以更具交互的方式关注代码示例,那么您可以使用启发本快速入门指南的Jupyter 笔记本。
- 如果您想查看 Redis 矢量数据库的更多高级实际示例,请访问GitHub 上的Redis AI 资源页面。