Oracle AI 向量搜索:向量存储¶
Oracle AI 向量搜索专为人工智能(AI)工作负载设计,允许您基于语义而非关键词进行数据查询。Oracle AI 向量搜索的最大优势之一在于,它能在单一系统中将非结构化数据的语义搜索与业务数据的关系型搜索相结合。这不仅功能强大,而且显著提升了效率——您无需额外部署专用向量数据库,从而消除了多系统间数据碎片化的痛点。
此外,您的向量数据还能充分受益于 Oracle 数据库的所有核心优势功能,包括:
- 分区支持
- 真正应用集群的可扩展性
- Exadata 智能扫描
- 跨地理分布式数据库的分片处理
- 事务处理
- 并行 SQL
- 灾难恢复
- 安全性
- Oracle 机器学习
- Oracle 图数据库
- Oracle 空间与图
- Oracle 区块链
- JSON
本指南将演示如何在 Oracle AI 向量搜索中使用向量功能。
若您刚接触 Oracle 数据库,建议尝试免费的 Oracle 23 AI 版本,该版本提供了配置数据库环境的绝佳入门指引。操作数据库时,通常应避免默认使用系统用户,您可创建专属用户以增强安全性和定制化。关于用户创建的具体步骤,请参阅我们的端到端指南,其中也包含 Oracle 用户设置方法。此外,理解用户权限对有效管理数据库安全至关重要,您可通过官方Oracle 指南深入了解用户账户与安全管理。
前提条件¶
请安装 Oracle Python 客户端驱动程序以将 Llama Index 与 Oracle AI 向量搜索结合使用。
%pip install llama-index-vector-stores-oracledb
import oracledb
# please update with your username, password, hostname and service_name
username = "<username>"
password = "<password>"
dsn = "<hostname>/<service_name>"
try:
connection = oracledb.connect(user=username, password=password, dsn=dsn)
print("Connection successful!")
except Exception as ex:
print("Exception occurred while index creation", ex)
导入使用 Oracle AI 向量搜索所需的依赖项¶
import sys
import os
from llama_index.core.schema import NodeRelationship, RelatedNodeInfo, TextNode
from llama_index.core.vector_stores.types import (
ExactMatchFilter,
MetadataFilters,
VectorStoreQuery,
)
from llama_index.vector_stores.oracledb import base as orallamavs
from llama_index.vector_stores.oracledb import OraLlamaVS, DistanceStrategy
加载文档¶
# Define a list of documents (These dummy examples are 4 random documents )
text_json_list = [
{
"text": "If the answer to any preceding questions is yes, then the database stops the search and allocates space from the specified tablespace; otherwise, space is allocated from the database default shared temporary tablespace.",
"id_": "cncpt_15.5.3.2.2_P4",
"embedding": [1.0, 0.0],
"relationships": "test-0",
"metadata": {
"weight": 1.0,
"rank": "a",
"url": "https://docs.oracle.com/en/database/oracle/oracle-database/23/cncpt/logical-storage-structures.html#GUID-5387D7B2-C0CA-4C1E-811B-C7EB9B636442",
},
},
{
"text": "A tablespace can be online (accessible) or offline (not accessible) whenever the database is open.\nA tablespace is usually online so that its data is available to users. The SYSTEM tablespace and temporary tablespaces cannot be taken offline.",
"id_": "cncpt_15.5.5_P1",
"embedding": [0.0, 1.0],
"relationships": "test-1",
"metadata": {
"weight": 2.0,
"rank": "c",
"url": "https://docs.oracle.com/en/database/oracle/oracle-database/23/cncpt/logical-storage-structures.html#GUID-D02B2220-E6F5-40D9-AFB5-BC69BCEF6CD4",
},
},
{
"text": "The database stores LOBs differently from other data types. Creating a LOB column implicitly creates a LOB segment and a LOB index. The tablespace containing the LOB segment and LOB index, which are always stored together, may be different from the tablespace containing the table.\nSometimes the database can store small amounts of LOB data in the table itself rather than in a separate LOB segment.",
"id_": "cncpt_22.3.4.3.1_P2",
"embedding": [1.0, 1.0],
"relationships": "test-2",
"metadata": {
"weight": 3.0,
"rank": "d",
"url": "https://docs.oracle.com/en/database/oracle/oracle-database/23/cncpt/concepts-for-database-developers.html#GUID-3C50EAB8-FC39-4BB3-B680-4EACCE49E866",
},
},
{
"text": "The LOB segment stores data in pieces called chunks. A chunk is a logically contiguous set of data blocks and is the smallest unit of allocation for a LOB. A row in the table stores a pointer called a LOB locator, which points to the LOB index. When the table is queried, the database uses the LOB index to quickly locate the LOB chunks.",
"id_": "cncpt_22.3.4.3.1_P3",
"embedding": [2.0, 1.0],
"relationships": "test-3",
"metadata": {
"weight": 4.0,
"rank": "e",
"url": "https://docs.oracle.com/en/database/oracle/oracle-database/23/cncpt/concepts-for-database-developers.html#GUID-3C50EAB8-FC39-4BB3-B680-4EACCE49E866",
},
},
]
# Create Llama Text Nodes
text_nodes = []
for text_json in text_json_list:
# Construct the relationships using RelatedNodeInfo
relationships = {
NodeRelationship.SOURCE: RelatedNodeInfo(
node_id=text_json["relationships"]
)
}
# Prepare the metadata dictionary; you might want to exclude certain metadata fields if necessary
metadata = {
"weight": text_json["metadata"]["weight"],
"rank": text_json["metadata"]["rank"],
}
# Create a TextNode instance
text_node = TextNode(
text=text_json["text"],
id_=text_json["id_"],
embedding=text_json["embedding"],
relationships=relationships,
metadata=metadata,
)
text_nodes.append(text_node)
print(text_nodes)
使用 AI 向量搜索创建具有不同距离策略的向量存储库¶
首先我们将创建三个采用不同距离函数的向量存储库。由于尚未在其中创建索引,目前只会生成空表。后续我们将使用这些向量存储库来创建 HNSW 索引。
您可以手动连接 Oracle 数据库,此时会看到三个表: Documents_DOT、Documents_COSINE 和 Documents_EUCLIDEAN。
接着我们将创建另外三个表 Documents_DOT_IVF、Documents_COSINE_IVF 和 Documents_EUCLIDEAN_IVF, 这些表将用于创建 IVF 索引而非 HNSW 索引。
要深入了解 Oracle AI 向量搜索支持的不同索引类型,请参阅以下指南
# Ingest documents into Oracle Vector Store using different distance strategies
vector_store_dot = OraLlamaVS.from_documents(
text_nodes,
table_name="Documents_DOT",
client=connection,
distance_strategy=DistanceStrategy.DOT_PRODUCT,
)
vector_store_max = OraLlamaVS.from_documents(
text_nodes,
table_name="Documents_COSINE",
client=connection,
distance_strategy=DistanceStrategy.COSINE,
)
vector_store_euclidean = OraLlamaVS.from_documents(
text_nodes,
table_name="Documents_EUCLIDEAN",
client=connection,
distance_strategy=DistanceStrategy.EUCLIDEAN_DISTANCE,
)
# Ingest documents into Oracle Vector Store using different distance strategies
vector_store_dot_ivf = OraLlamaVS.from_documents(
text_nodes,
table_name="Documents_DOT_IVF",
client=connection,
distance_strategy=DistanceStrategy.DOT_PRODUCT,
)
vector_store_max_ivf = OraLlamaVS.from_documents(
text_nodes,
table_name="Documents_COSINE_IVF",
client=connection,
distance_strategy=DistanceStrategy.COSINE,
)
vector_store_euclidean_ivf = OraLlamaVS.from_documents(
text_nodes,
table_name="Documents_EUCLIDEAN_IVF",
client=connection,
distance_strategy=DistanceStrategy.EUCLIDEAN_DISTANCE,
)
演示文本的增删操作及基础相似性搜索¶
def manage_texts(vector_stores):
"""
Adds texts to each vector store, demonstrates error handling for duplicate additions,
and performs deletion of texts. Showcases similarity searches and index creation for each vector store.
Args:
- vector_stores (list): A list of OracleVS instances.
"""
for i, vs in enumerate(vector_stores, start=1):
# Adding texts
try:
vs.add_texts(text_nodes, metadata)
print(f"\n\n\nAdd texts complete for vector store {i}\n\n\n")
except Exception as ex:
print(
f"\n\n\nExpected error on duplicate add for vector store {i}\n\n\n"
)
# Deleting texts using the value of 'id'
vs.delete("test-1")
print(f"\n\n\nDelete texts complete for vector store {i}\n\n\n")
# Similarity search
query = VectorStoreQuery(
query_embedding=[1.0, 1.0], similarity_top_k=3
)
results = vs.query(query=query)
print(
f"\n\n\nSimilarity search results for vector store {i}: {results}\n\n\n"
)
vector_store_list = [
vector_store_dot,
vector_store_max,
vector_store_euclidean,
vector_store_dot_ivf,
vector_store_max_ivf,
vector_store_euclidean_ivf,
]
manage_texts(vector_store_list)
演示为每种距离策略创建特定参数的索引¶
def create_search_indices(connection):
"""
Creates search indices for the vector stores, each with specific parameters tailored to their distance strategy.
"""
# Index for DOT_PRODUCT strategy
# Notice we are creating a HNSW index with default parameters
# This will default to creating a HNSW index with 8 Parallel Workers and use the Default Accuracy used by Oracle AI Vector Search
orallamavs.create_index(
connection,
vector_store_dot,
params={"idx_name": "hnsw_idx1", "idx_type": "HNSW"},
)
# Index for COSINE strategy with specific parameters
# Notice we are creating a HNSW index with parallel 16 and Target Accuracy Specification as 97 percent
orallamavs.create_index(
connection,
vector_store_max,
params={
"idx_name": "hnsw_idx2",
"idx_type": "HNSW",
"accuracy": 97,
"parallel": 16,
},
)
# Index for EUCLIDEAN_DISTANCE strategy with specific parameters
# Notice we are creating a HNSW index by specifying Power User Parameters which are neighbors = 64 and efConstruction = 100
orallamavs.create_index(
connection,
vector_store_euclidean,
params={
"idx_name": "hnsw_idx3",
"idx_type": "HNSW",
"neighbors": 64,
"efConstruction": 100,
},
)
# Index for DOT_PRODUCT strategy with specific parameters
# Notice we are creating an IVF index with default parameters
# This will default to creating an IVF index with 8 Parallel Workers and use the Default Accuracy used by Oracle AI Vector Search
orallamavs.create_index(
connection,
vector_store_dot_ivf,
params={
"idx_name": "ivf_idx1",
"idx_type": "IVF",
},
)
# Index for COSINE strategy with specific parameters
# Notice we are creating an IVF index with parallel 32 and Target Accuracy Specification as 90 percent
orallamavs.create_index(
connection,
vector_store_max_ivf,
params={
"idx_name": "ivf_idx2",
"idx_type": "IVF",
"accuracy": 90,
"parallel": 32,
},
)
# Index for EUCLIDEAN_DISTANCE strategy with specific parameters
# Notice we are creating an IVF index by specifying Power User Parameters which is neighbor_part = 64
orallamavs.create_index(
connection,
vector_store_euclidean_ivf,
params={
"idx_name": "ivf_idx3",
"idx_type": "IVF",
"neighbor_part": 64,
},
)
print("Index creation complete.")
create_search_indices(connection)
现在我们将对所有六种向量存储执行一系列高级搜索。以下三种搜索各包含带过滤条件和不带过滤条件的版本。过滤条件仅筛选出ID为101的文档,并排除其他所有内容¶
# Conduct advanced searches after creating the indices
def conduct_advanced_searches(vector_stores):
# Constructing a filter for direct comparison against document metadata
# This filter aims to include documents whose metadata 'id' is exactly '2'
for i, vs in enumerate(vector_stores, start=1):
def query_without_filters_returns_all_rows_sorted_by_similarity():
print(f"\n--- Vector Store {i} Advanced Searches ---")
# Similarity search without a filter
print("\nSimilarity search results without filter:")
query = VectorStoreQuery(
query_embedding=[1.0, 1.0], similarity_top_k=3
)
print(vs.query(query=query))
query_without_filters_returns_all_rows_sorted_by_similarity()
def query_with_filters_returns_multiple_matches():
print(f"\n--- Vector Store {i} Advanced Searches ---")
# Similarity search with filter
print("\nSimilarity search results without filter:")
filters = MetadataFilters(
filters=[ExactMatchFilter(key="rank", value="c")]
)
query = VectorStoreQuery(
query_embedding=[1.0, 1.0], filters=filters, similarity_top_k=1
)
result = vs.query(query)
print(result.ids)
query_with_filters_returns_multiple_matches()
def query_with_filter_applies_top_k():
print(f"\n--- Vector Store {i} Advanced Searches ---")
# Similarity search with a filter
print("\nSimilarity search results with filter:")
filters = MetadataFilters(
filters=[ExactMatchFilter(key="rank", value="c")]
)
query = VectorStoreQuery(
query_embedding=[1.0, 1.0], filters=filters, similarity_top_k=1
)
result = vs.query(query)
print(result.ids)
query_with_filter_applies_top_k()
def query_with_filter_applies_node_id_filter():
print(f"\n--- Vector Store {i} Advanced Searches ---")
# Similarity search with a filter
print("\nSimilarity search results with filter:")
filters = MetadataFilters(
filters=[ExactMatchFilter(key="rank", value="c")]
)
query = VectorStoreQuery(
query_embedding=[1.0, 1.0],
filters=filters,
similarity_top_k=3,
node_ids=["452D24AB-F185-414C-A352-590B4B9EE51B"],
)
result = vs.query(query)
print(result.ids)
query_with_filter_applies_node_id_filter()
def query_with_exact_filters_returns_single_match():
print(f"\n--- Vector Store {i} Advanced Searches ---")
# Similarity search with a filter
print("\nSimilarity search results with filter:")
filters = MetadataFilters(
filters=[
ExactMatchFilter(key="rank", value="c"),
ExactMatchFilter(key="weight", value=2),
]
)
query = VectorStoreQuery(
query_embedding=[1.0, 1.0], filters=filters
)
result = vs.query(query)
print(result.ids)
query_with_exact_filters_returns_single_match()
def query_with_contradictive_filter_returns_no_matches():
filters = MetadataFilters(
filters=[
ExactMatchFilter(key="weight", value=2),
ExactMatchFilter(key="weight", value=3),
]
)
query = VectorStoreQuery(
query_embedding=[1.0, 1.0], filters=filters
)
result = vs.query(query)
print(result.ids)
query_with_contradictive_filter_returns_no_matches()
def query_with_filter_on_unknown_field_returns_no_matches():
print(f"\n--- Vector Store {i} Advanced Searches ---")
# Similarity search with a filter
print("\nSimilarity search results with filter:")
filters = MetadataFilters(
filters=[ExactMatchFilter(key="unknown_field", value="c")]
)
query = VectorStoreQuery(
query_embedding=[1.0, 1.0], filters=filters
)
result = vs.query(query)
print(result.ids)
query_with_filter_on_unknown_field_returns_no_matches()
def delete_removes_document_from_query_results():
vs.delete("test-1")
query = VectorStoreQuery(
query_embedding=[1.0, 1.0], similarity_top_k=2
)
result = vs.query(query)
print(result.ids)
delete_removes_document_from_query_results()
conduct_advanced_searches(vector_store_list)
端到端演示¶
请参阅我们的完整演示指南 Oracle AI 向量搜索端到端演示指南,借助 Oracle AI 向量搜索构建端到端的 RAG 流程。