关于 neo4j-vector-index-skill
帮助开发者在 Neo4j 中创建和使用向量索引的技能,涵盖节点与关系嵌入、相似度搜索和混合搜索。内容包含版本选择、索引配置、过滤策略及查询示例。
主要功能
- 01创建节点与关系向量索引
- 02配置维度、相似度与量化
- 03使用 SEARCH 或向量查询过程
- 04选择索引内及查询后过滤
- 05批量写入嵌入向量
- 06组合向量与全文搜索
适用场景
- 01为节点或关系嵌入建立索引
- 02按查询向量查找相似节点
- 03对搜索结果施加属性过滤
- 04融合语义与全文搜索结果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ neo4j-contrib/ neo4j-skills --skill neo4j-vector-index-skill安装后在 Claude Code 中输入/neo4j-vector-index-skill确认已加载。
附带文件README.mdreferences/hybrid-search.md
详细介绍
适用场景
- 在节点或关系上创建向量索引(
CREATE VECTOR INDEX) - 执行向量相似度/最近邻搜索
- 在数据摄取期间将嵌入存储到图节点上
- 为 GDS 算法已写入的嵌入编制索引/查询
- 选择相似度函数、维度、HNSW 参数或量化方式
- 使用
SEARCH子句(2026.01+)或db.index.vector.queryNodes()(2025.x) - 在模型变更后批量更新嵌入
- 将向量结果与即时图邻域结合(完整的 retrieval_query 流水线 →
neo4j-graphrag-skill) - 结合向量结果与全文搜索或其他已排序来源进行混合搜索
不适用场景
- GraphRAG 流水线(VectorCypherRetriever、HybridCypherRetriever、retrieval_query)→
neo4j-graphrag-skill - 仅全文/关键词搜索(FULLTEXT INDEX、
db.index.fulltext.queryNodes)→neo4j-cypher-skill - 计算 GDS 图嵌入(FastRP、Node2Vec、GraphSAGE)→
neo4j-gds-skill - 索引管理(列出所有索引、删除范围/文本/查找索引)→
neo4j-cypher-skill
预检——确定版本
CALL dbms.components() YIELD versions RETURN versions[0] AS neo4j_version
| 版本 | 用途 |
|---|---|
2026.01 或更高版本 | SEARCH 子句(索引内过滤,首选) |
2025.x | db.index.vector.queryNodes() 过程(2026.04 弃用——在 2026.x 上使用 SEARCH) |
Aura 上的 5.27 | 在 2026.12 LTS 之后(届时改用日历版本)之前,Aura 均报告 5.27,无论引擎版本如何——Aura 运行最新的 2026.x → 使用 SEARCH |
步骤 1 — 创建向量索引
节点索引(单标签):
CYPHER 25
CREATE VECTOR INDEX chunk_embedding IF NOT EXISTS
FOR (c:Chunk) ON (c.embedding)
OPTIONS {
indexConfig: {
`vector.dimensions`: 1536,
`vector.similarity_function`: 'cosine',
`vector.quantization.type`: 'SCALAR',
`vector.hnsw.m`: 16,
`vector.hnsw.ef_construction`: 100
}
}
带可筛选属性的节点索引 [2026.01+] — WITH 用于声明可在 SEARCH ... WHERE 中使用的属性:
CYPHER 25
CREATE VECTOR INDEX chunk_embedding IF NOT EXISTS
FOR (c:Chunk) ON (c.embedding)
WITH [c.source, c.lang, c.published_year] // stored as metadata; filterable in SEARCH WHERE
OPTIONS { indexConfig: { `vector.dimensions`: 1536, `vector.similarity_function`: 'cosine' } }
带可筛选属性的多标签索引 [2026.01+]:
CYPHER 25
CREATE VECTOR INDEX doc_embedding IF NOT EXISTS
FOR (n:Document|Article) ON n.embedding
WITH [n.author, n.published_year, n.lang]
OPTIONS { indexConfig: { `vector.dimensions`: 1536, `vector.similarity_function`: 'cosine' } }
关系索引:
CYPHER 25
CREATE VECTOR INDEX rel_embedding IF NOT EXISTS
FOR ()-[r:HAS_CHUNK]-() ON (r.embedding)
OPTIONS { indexConfig: { `vector.dimensions`: 768, `vector.similarity_function`: 'cosine' } }
WITH 属性类型 — 仅允许标量类型:INTEGER、FLOAT、STRING、BOOLEAN、DATE、ZONED DATETIME、LOCAL DATETIME、ZONED TIME、LOCAL TIME、DURATION。不允许:LIST、POINT 或向量属性本身。
索引配置参考:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
vector.dimensions | INTEGER 1–4096 | 无 | 必填;必须与嵌入模型完全匹配 |
vector.similarity_function | STRING | 'cosine' | 'cosine' 或 'euclidean' |
vector.quantization.type | STRING | 'binary'(2026.08 之前为 'scalar') | 'none'、'scalar'、'binary' [2026.06+,2026.07 正式发布];可减少存储空间;binary 最小(每个维度 1 位),压缩最激进;需要 vector-2.0+(5.18+);显式设置以确保跨版本行为一致 |
vector.quantization.enabled | BOOLEAN | true | 2026.06 弃用 — 请使用 vector.quantization.type;2026.07 之前,若未设置 vector.quantization.type: 'none' 而将其设为 false,则索引创建会失败 |
vector.default_search_expansion_factor | FLOAT 1.0–10000.0 | 1.0(无量化)/ 1.5(scalar)/ 3.0(binary)(2026.07 之前为 2.0) | [2026.06+,2026.07 正式发布];量化向量的值 >1.0 会启用基于全精度向量的自动重新评分(高保真量化搜索,HFQ);无法在查询时设置;现有索引会保留其存储值,直到重建 |
vector.hnsw.m | INTEGER 1–512 | 16 | HNSW 图连接数;值越高 = 召回率越好,内存占用越大 |
vector.hnsw.ef_construction | INTEGER 1–3200 | 100 | 构建时的候选项数;值越高 = 召回率越好,构建越慢 |
未量化的向量:只需设置 vector.quantization.type: 'none' — 在 2026.06 中,若未设置该项而将 vector.quantization.enabled: false,会报错(已在 2026.07 修复)。
相似度函数的选择:
| 使用场景 | 函数 |
|---|---|
| 归一化嵌入(OpenAI、Cohere、Voyage、Google) | 'cosine' |
| 未归一化 / 需要关注原始距离 | 'euclidean' |
索引提供程序 — 自动选择最新版本;无法在 Cypher 25 中指定。使用 SHOW VECTOR INDEXES YIELD name, indexProvider 检查:
| 提供程序 | 量化支持 |
|---|---|
vector-2026.08 | 支持 binary 的高保真量化搜索;默认使用 'binary' |
vector-2026.07 | 支持 scalar 和 binary 的高保真量化搜索 |
vector-2026.06 | 支持 scalar 和 binary;binary + 重新评分所必需 |
vector-3.0(2025.09+)/ vector-2.0(5.18+) | scalar |
更改量化类型、扩展因子或提供程序需要删除、重新创建并重新填充索引。对于在 2026.08 之前构建且很少更新的索引:请重新创建,以获得 2026.09 重新评分 binary/scalar 的速度提升(延迟和吞吐量约提升 5 倍)。
内存 — 向量索引文件位于操作系统文件系统缓存中,而非页面缓存。请为以下内容预留 RAM:HNSW 图 ≈ 8 B × 向量数 × vector.hnsw.m,加上向量值(全精度 ≈ 4 B × 维度数 × 向量数;scalar ÷4;binary ÷32)。仅当查询会返回或重新排序向量属性时,才需要为其设置页面缓存。详情 → 向量索引内存配置。
步骤 2 — 等待索引变为 ONLINE
索引会异步构建 — 在变为 ONLINE 之前切勿查询:
SHOW VECTOR INDEXES YIELD name, state, populationPercent
WHERE name = 'chunk_embedding'
RETURN name, state, populationPercent
每隔 5 秒轮询一次,直到 state = 'ONLINE' 且 populationPercent = 100.0。如果 state = 'FAILED' → 停止并检查日志。
Shell 轮询(cypher-shell):
until cypher-shell -u neo4j -p "$NEO4J_PASSWORD" \
"SHOW VECTOR INDEXES YIELD name, state WHERE name='chunk_embedding' RETURN state" \
| grep -q ONLINE; do
sleep 5
done
步骤 3 — 导入嵌入
批量 UNWIND 模式(节点数 > 100 时使用 — 切勿每个事务只处理一个节点):
from neo4j import GraphDatabase
driver = GraphDatabase.driver(uri, auth=(user, password))
def embed_batch(texts: list[str]) -> list[list[float]]:
response = openai_client.embeddings.create(
model="text-embedding-3-small", input=texts
)
return [r.embedding for r in response.data]
def store_embeddings(records: list[dict], batch_size: int = 500):
expected_dim = 1536 # must match vector.dimensions
texts = [r["text"] for r in records]
embeddings = embed_batch(texts)
for emb in embeddings:
assert len(emb) == expected_dim, f"Dim mismatch: {len(emb)} != {expected_dim}"
rows = [{"id": r["id"], "embedding": emb}
for r, emb in zip(records, embeddings)]
for i in range(0, len(rows), batch_size):
driver.execute_query(
"UNWIND $rows AS row MATCH (c:Chunk {id: row.id}) SET c.embedding = row.embedding",
rows=rows[i:i+batch_size]
)
❌ 切勿在已存储嵌入后才创建索引 — 始终先创建索引。 ✅ 创建索引 → 轮询至 ONLINE → 导入嵌入。
步骤 4 — 运行向量搜索
SEARCH 子句(2026.01+,首选)
CYPHER 25
MATCH (c:Chunk)
SEARCH c IN (
VECTOR INDEX chunk_embedding
FOR $queryEmbedding
LIMIT 10
) SCORE AS score
RETURN c.text, score
ORDER BY score DESC
使用索引内过滤 [2026.01+]——必须在创建索引时于 WITH 中声明属性:
// Index must have been created with: WITH [c.source, c.lang, c.published_year]
CYPHER 25
MATCH (c:Chunk)
SEARCH c IN (
VECTOR INDEX chunk_embedding
FOR $queryEmbedding
WHERE c.source = $source AND c.lang = 'en' AND c.published_year >= 2024
LIMIT 10
) SCORE AS score
RETURN c.text, c.source, score
ORDER BY score DESC
过滤策略——选择一种:
| 策略 | 适用场景 | 权衡 |
|---|---|---|
索引内 WHERE [2026.01+] | 对预先声明的 WITH 属性进行过滤;在索引设计时已知 | 速度快、延迟稳定;必须提前声明属性 |
| 后置过滤(MATCH + procedure) | 任意 Cypher 谓词、图遍历、OR/NOT | 灵活性高;可能先获取过多结果再丢弃 |
| 前置过滤(先 MATCH,再 SEARCH) | 已知候选集较小;在子集中精确查找最近邻 | 结果确定;候选集较大时较慢 |
索引内 WHERE 的硬性限制 [2026.01+]:
- 必须在创建索引时将属性列入
WITH [...]——未声明的属性会静默回退到后置过滤 - 仅支持 AND 谓词——不支持 OR、NOT 和字符串操作。允许使用
IN列表成员判断 [2026.06+] - 仅支持标量类型:
INTEGER、FLOAT、STRING、BOOLEAN、时间类型——不支持 VECTOR/LIST/POINT
后置过滤模式(2025.x 或任意谓词)
CYPHER 25
CALL db.index.vector.queryNodes('chunk_embedding', 50, $queryEmbedding)
YIELD node AS c, score
WHERE c.source = $source // post-filter: fetch more, then filter
RETURN c.text, score
ORDER BY score DESC LIMIT 10
关系索引过程:
CYPHER 25
CALL db.index.vector.queryRelationships('rel_embedding', 5, $queryEmbedding)
YIELD relationship AS r, score
RETURN r.text, score
SEARCH 子句的硬性限制(所有版本):
- 索引名称不能是参数(不允许使用
$indexName——请使用字面字符串) - 绑定变量必须来自外层 MATCH 模式
- 查询向量不能引用绑定变量
步骤 5 — 与图遍历结合(简单场景)
以向量搜索为入口,然后进行图遍历:
CYPHER 25
MATCH (c:Chunk)
SEARCH c IN (
VECTOR INDEX chunk_embedding
FOR $queryEmbedding
LIMIT 10
) SCORE AS score
MATCH (c)<-[:HAS_CHUNK]-(a:Article)
OPTIONAL MATCH (a)-[:MENTIONS]->(org:Organization)
RETURN c.text, a.title, score, collect(DISTINCT org.name) AS organizations
ORDER BY score DESC
对于完整的 retrieval_query 流水线、HybridCypherRetriever 或 neo4j-graphrag 库,请委托给 neo4j-graphrag-skill。
步骤 6 — 混合搜索
当某种信号会遗漏有用的候选项时,请使用混合搜索:语义向量会遗漏精确词项,词法全文搜索会遗漏释义,结构图信号会发现文本中不存在的拓扑结构。 常见模式是向量 + 全文搜索,但同样的方法也适用于多个向量索引、由 GDS 写入的嵌入、图遍历得分,或任意两个及以上的排名/评分来源。 加载 references/hybrid-search.md,并采用其中的查询结构。
规则:
- 独立运行每个来源;按
score DESC, stable_id ASC对每个来源排名。 - 按排名合并,而非按原始分数合并;全文搜索分数与向量分数不可比较。
- 每个
UNION ALL分支返回相同的列:匹配的节点 + 贡献值。 - 使用
sourceK > finalK;在最终限制结果数之前先合并。 - 对每个节点的贡献值求和;最终行按
wrrf DESC, stable_id ASC排序。 - 添加更多来源时,加入额外的
UNION ALL分支和新的sourceWeights键。
嵌入提供方速查表
| 提供方 / 模型 | 维度 | 相似度 | 备注 |
|---|---|---|---|
| OpenAI text-embedding-3-small | 1536 | cosine | 默认;可通过 dimensions= 参数缩减至 256–1536 |
| OpenAI text-embedding-3-large | 3072 | cosine | 可缩减至 256–3072 |
| OpenAI text-embedding-ada-002 | 1536 | cosine | 旧版;建议优先使用 3-small |
| Cohere embed-v3 (English) | 1024 | cosine | 写入时使用 input_type='search_document',查询时使用 'search_query' |
| Voyage voyage-3-large | 1024 | cosine | 质量高;需要 voyage-ai 包 |
| Google text-embedding-004 | 768 | cosine | 通过 Vertex AI 使用 |
| Ollama nomic-embed-text | 768 | cosine | 本地开发/测试 |
| Ollama mxbai-embed-large | 1024 | cosine | 本地使用;可达到生产级质量 |
vector.dimensions 必须与模型输出维度完全一致——不会自动截断。
向量函数
临时计算相似度(不适用于 kNN 搜索——请为此使用索引):
MATCH (a:Chunk {id: $id1}), (b:Chunk {id: $id2})
RETURN vector.similarity.cosine(a.embedding, b.embedding) AS sim
// vector.similarity.euclidean(a, b) — same signature, 0–1 range
// vector_distance (2025.10+) — metrics: EUCLIDEAN, EUCLIDEAN_SQUARED, MANHATTAN, COSINE, DOT, HAMMING
// Returns distance (lower = more similar, inverse of similarity)
RETURN vector_distance(a.embedding, b.embedding, 'COSINE') AS dist
// vector_dimension_count (2025.10+)
RETURN vector_dimension_count(n.embedding) AS dims
// vector_norm (2025.20+) — metrics: EUCLIDEAN, MANHATTAN
RETURN vector_norm(n.embedding, 'EUCLIDEAN') AS norm
将 LIST 转换为类型化 VECTOR:
// vector(value, dimension, coordinateType)
// coordinateType: FLOAT64, FLOAT32, INTEGER8/16/32/64
WITH vector([1.0, 2.0, 3.0], 3, 'FLOAT32') AS v
RETURN vector_dimension_count(v)
索引管理
// Show all vector indexes with config
SHOW VECTOR INDEXES YIELD name, state, populationPercent,
labelsOrTypes, properties, indexConfig
RETURN name, state, populationPercent, labelsOrTypes, properties, indexConfig;
// Drop (node data unchanged — only index structure removed)
DROP INDEX chunk_embedding IF EXISTS;
// No ALTER VECTOR INDEX — to change dimensions or similarity function:
// 1. DROP INDEX old_index IF EXISTS
// 2. CREATE VECTOR INDEX new_index ... with new OPTIONS
// 3. Re-generate all embeddings with new model
// 4. Poll until ONLINE
常见错误
| 错误 | 原因 | 修复方法 |
|---|---|---|
IllegalArgumentException: Index dimension mismatch | 已存储的嵌入维度 ≠ vector.dimensions | 修复嵌入生成;删除并使用正确的维度重新创建索引 |
| 搜索结果不完整 | 索引仍处于 POPULATING 状态 | 轮询,直到 state = 'ONLINE' |
Unknown procedure db.index.vector.queryNodes | Neo4j < 5.11 | 低于 5.11 的版本不支持向量索引;请升级 |
SEARCH clause not available | Neo4j < 2026.01 | 使用 queryNodes() 过程 |
OR/NOT not allowed in SEARCH WHERE | SEARCH 的索引内筛选限制 | 将复杂谓词从 SEARCH 移到外层 WHERE |
| 正确的查询返回零条结果 | 相似度函数错误,或嵌入全为零 | 使用 vector.similarity.cosine() 验证;检查嵌入调用是否成功 |
| 分数始终为 1.0 | 向量全为零或完全相同 | 嵌入生成失败;在导入前添加维度断言 |
vector.quantization.enabled / .type 选项被拒绝 | provider vector-1.0(Neo4j < 5.18) | 省略量化选项,或升级到 5.18+ |
BINARY 量化被拒绝 | provider 早于 vector-2026.06 | 升级到 2026.06+;使用 SHOW VECTOR INDEXES YIELD name, indexProvider 检查(vector-2026.07 增加了 High-Fidelity Quantized 搜索) |
检查清单
-
vector.dimensions与嵌入模型的输出完全一致 - 在导入嵌入前创建向量索引
- 明确选择相似度函数(归一化向量使用
cosine,基于距离的场景使用euclidean) - 首次查询前轮询索引,直到
state = 'ONLINE' - 导入前验证每个嵌入的维度
- Neo4j >= 2026.01 时使用
SEARCH子句(优先);仅在 2025.x 中回退到过程(已于 2026.04 弃用) - SEARCH
WHERE仅使用 AND 谓词和标量类型 - 对 > 100 个节点使用批量 UNWIND 模式
- 如果模型发生变化:删除索引 → 使用新维度重新创建 → 重新生成所有嵌入
在 Cypher 中生成嵌入 — ai.text.embed() [2025.12]
// Syntax (requires CYPHER 25)
CYPHER 25
// ai.text.embed(resource :: STRING, provider :: STRING, configuration :: MAP) :: VECTOR
provider 字符串均为小写('openai'、'vertexai'、'bedrock-titan'、'azure-openai')。完整的 provider 配置 → neo4j-genai-plugin-skill。
完整查询模式 — 在查询时生成嵌入并立即搜索(2025.x 的过程回退方案):
CYPHER 25
WITH ai.text.embed(
"What are good open source projects",
"openai",
{ token: $openaiKey, model: 'text-embedding-3-small' }) AS userEmbedding
CALL db.index.vector.queryNodes('chunk_embedding', 6, userEmbedding) // deprecated 2026.04
YIELD node AS c, score
RETURN c.text, score
ORDER BY score DESC
使用 SEARCH 子句(2026.01+):
CYPHER 25
WITH ai.text.embed("my query", "openai", { token: $openaiKey, model: 'text-embedding-3-small' }) AS userEmbedding
MATCH (c:Chunk)
SEARCH c IN (VECTOR INDEX chunk_embedding FOR userEmbedding LIMIT 6) SCORE AS score
RETURN c.text, score
ORDER BY score DESC
切勿将 API 密钥作为字符串字面量传入 — 使用 $openaiKey 参数(通过驱动程序参数注入)或 apoc.static.get()。
规则:导入时和查询时使用相同的模型 — 不同模型生成的嵌入不可比较。
已弃用(仍可使用,但不要在新代码中使用):
genai.vector.encode()[已弃用] → 使用ai.text.embed()[2025.12]genai.vector.encodeBatch()[已弃用] → 使用CALL ai.text.embedBatch()[2025.12]genai.vector.listEncodingProviders()[已弃用] → 使用CALL ai.text.embed.providers()[2025.12]
完整的 ai.text.* 参考(补全、结构化输出、聊天、分词)→ neo4j-genai-plugin-skill。
基于 Cypher 的嵌入导入 — db.create.setNodeVectorProperty
通过 Cypher 设置向量属性(例如,在 LOAD CSV 或 MERGE 工作流中):
LOAD CSV WITH HEADERS FROM 'https://example.com/data.csv' AS row
MERGE (q:Question {text: row.question})
WITH q, row
CALL db.create.setNodeVectorProperty(q, 'embedding', apoc.convert.fromJsonList(row.question_embedding))
apoc.convert.fromJsonList() 会将 "[0.1,0.2,...]" STRING 转换为 LIST<FLOAT>。Python 生成的嵌入 → 使用 UNWIND 批量模式(步骤 3)。
相似度函数 — 扩展指南
根据训练损失函数选择:
- 查看嵌入模型文档 — 使用余弦损失训练的模型 → 使用
'cosine' - 使用 L2/欧几里得损失训练的模型 → 使用
'euclidean' - 如果文档未说明:默认使用
'cosine'(所有主流托管 API 均使用此函数)
常见陷阱 — 相似度函数错误:
❌ Created index with 'euclidean' but model outputs L2-normalized vectors
→ scores are mathematically correct but rankings differ from expected cosine order
→ no error thrown; wrong results silently returned
✅ Verify: run vector.similarity.cosine(a.embedding, b.embedding) manually on known
similar pairs — score should be > 0.9 for near-duplicate text
创建索引后的健全性检查查询:
MATCH (c:Chunk) WITH c LIMIT 2
WITH collect(c) AS nodes
RETURN vector.similarity.cosine(nodes[0].embedding, nodes[1].embedding) AS cosine_check,
vector.similarity.euclidean(nodes[0].embedding, nodes[1].embedding) AS euclidean_check
如果两者都返回 null → 尚未设置嵌入。如果余弦值返回 1.0 → 向量完全相同(嵌入调用失败)。
注意事项——扩展
| 注意事项 | 详情 | 解决方法 |
|---|---|---|
| 写入时索引尚未 ONLINE | 索引创建前插入节点是允许的——索引会自动填充。但在 POPULATING 期间查询会返回部分结果 | 首次查询前,始终轮询 state = 'ONLINE' |
| 维度错误——静默失败 | 存储的向量维度 ≠ vector.dimensions → 查询时(而非写入时)抛出 IllegalArgumentException | 每次执行 SET c.embedding 前,断言 len(emb) == expected_dim |
| 写入和查询时使用了不同模型 | 不会报错;对于明显相似的文本,余弦相似度约为 0.3–0.5 | 两者使用相同的模型字符串/版本;将模型名称存储为节点元数据 |
| 查询时缺少模型 | 如果提供方配置错误,ai.text.embed 会静默返回 null | 单独测试编码调用;在将其嵌入工作流前,检查 CYPHER 25 RETURN ai.text.embed(...) |
| 单个事务写入量过大 | 一个事务写入 10k 个节点 → 内存溢出或超时 | 使用 UNWIND $rows ... CALL IN TRANSACTIONS OF 500 ROWS 或 Python 批处理循环 |
| 未设置分块重叠 | 相邻分块之间没有重叠 → 边界处的上下文丢失 → 跨段落查询的召回效果较差 | 将 chunk_overlap 设置为不小于 chunk_size 的 10% |
参考资料
按需加载:
- 混合搜索 - 使用 WRRF/RRF 合并语义、词法、结构或其他排序结果源
- 向量索引文档
- SEARCH 子句文档
- 向量函数文档
- ai.text.embed() / GenAI 插件文档 [2025.12] — 替代已弃用的
genai.vector.encode() - db.create.setNodeVectorProperty 文档
- 分块策略、批量嵌入与存储、分割器模式 — 请参阅文档导入技能
- 带过滤器的向量搜索 — 2026.01 预览版