Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
N
Neo Query
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Back End
Neo Query
Commits
617108d4
Commit
617108d4
authored
Sep 10, 2026
by
谢宇轩
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: update docker network setting
parent
d0ec3142
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
677 additions
and
2 deletions
+677
-2
.env.example
.env.example
+4
-0
README.md
README.md
+8
-1
compose.yaml
compose.yaml
+3
-1
USAGE_GUIDE.md
docs/USAGE_GUIDE.md
+662
-0
No files found.
.env.example
0 → 100644
View file @
617108d4
# Host-side Compose interpolation only. This value is not injected into the
# query-api container. It must name the Docker network already joined by the
# target Neo4j container.
NEO4J_DOCKER_NETWORK=neo4jagent_default
README.md
View file @
617108d4
...
@@ -2,6 +2,12 @@
...
@@ -2,6 +2,12 @@
面向组内共享的轻量查询服务。适用于 Neo4j Community:调用方只持有 Workspace API Key,通过预定义模板查询图数据。
面向组内共享的轻量查询服务。适用于 Neo4j Community:调用方只持有 Workspace API Key,通过预定义模板查询图数据。
## 文档入口
完整的部署、调用、协作与运维流程见
[
使用手册
](
docs/USAGE_GUIDE.md
)
。它覆盖首次部署、源码运行、配置覆盖、Key 生命周期、模板与快捷查询、结果解码、备份恢复、故障排查和测试验收。
本文保留产品边界、配置参考和快速命令,适合作为项目首页;需要按角色执行操作时,请从使用手册开始。
## 能力与边界
## 能力与边界
-
一个 Key 对应一个 Workspace;一个 Workspace 对应一个 Neo4j URI 与数据库。
-
一个 Key 对应一个 Workspace;一个 Workspace 对应一个 Neo4j URI 与数据库。
...
@@ -16,10 +22,11 @@
...
@@ -16,10 +22,11 @@
## Docker 启动
## Docker 启动
镜像:
`neo4j-query-service:0.1.0`
。示例 Compose 接入已有的
`neo4jagent_default`
网络,不启动或修改已有 Neo4j
。
镜像:
`neo4j-query-service:0.1.0`
。示例 Compose 接入已有的
Neo4j Docker 网络,不启动或修改已有 Neo4j。默认网络名为
`neo4jagent_default`
;若部署环境不同,请在项目目录的
`.env`
中设置
`NEO4J_DOCKER_NETWORK=<实际网络名>`
(可从
`.env.example`
复制),或在启动命令前设置同名环境变量
。
```
bash
```
bash
cp
examples/workspaces.yaml config.local.yaml
cp
examples/workspaces.yaml config.local.yaml
cp
.env.example .env
# 编辑 URI、数据库、账号和密码。已有本地容器可使用 bolt://neo4j-agent:7687。
# 编辑 URI、数据库、账号和密码。已有本地容器可使用 bolt://neo4j-agent:7687。
docker compose up
-d
--build
docker compose up
-d
--build
docker compose
exec
query-api query-service healthcheck
docker compose
exec
query-api query-service healthcheck
...
...
compose.yaml
View file @
617108d4
...
@@ -21,6 +21,8 @@ services:
...
@@ -21,6 +21,8 @@ services:
networks
:
networks
:
neo4j
:
neo4j
:
external
:
true
external
:
true
name
:
neo4jagent_default
# Set NEO4J_DOCKER_NETWORK on the deployment host when the existing
# Neo4j Compose project uses a different network name.
name
:
${NEO4J_DOCKER_NETWORK:-neo4jagent_default}
volumes
:
volumes
:
query-data
:
query-data
:
docs/USAGE_GUIDE.md
0 → 100644
View file @
617108d4
# Neo4j Workspace Query Service 使用手册
本手册对应当前
`0.1.0`
实现。它面向三类人:部署服务的运维人员、维护可信查询模板的维护者,以及通过 API 读取图数据的普通调用方。
服务的基本模式是:
**一个 API Key 只属于一个 Workspace;调用方只能执行该 Workspace 中已经保存的模板或快捷查询,不能提交任意 Cypher。**
Neo4j 账号、密码和原始配置留在服务端,元数据(Key 摘要、模板、快捷查询、审计日志)保存在服务自己的 SQLite 数据库中。
## 目录
-
[
1. 先判断是否适用
](
#1-先判断是否适用
)
-
[
2. 角色、数据与生命周期
](
#2-角色数据与生命周期
)
-
[
3. 快速启动:Docker
](
#3-快速启动docker
)
-
[
4. 源码开发与本地运行
](
#4-源码开发与本地运行
)
-
[
5. 配置完整参考
](
#5-配置完整参考
)
-
[
6. API Key 管理
](
#6-api-key-管理
)
-
[
7. 模板管理:维护者场景
](
#7-模板管理维护者场景
)
-
[
8. 数据读取:调用方场景
](
#8-数据读取调用方场景
)
-
[
9. 快捷查询协作场景
](
#9-快捷查询协作场景
)
-
[
10. 返回结果与错误处理
](
#10-返回结果与错误处理
)
-
[
11. 运行限制与安全边界
](
#11-运行限制与安全边界
)
-
[
12. 日常运维:健康、备份、恢复与升级
](
#12-日常运维健康备份恢复与升级
)
-
[
13. 故障排查
](
#13-故障排查
)
-
[
14. 测试与验收
](
#14-测试与验收
)
## 1. 先判断是否适用
适用场景:
-
组内应用需要读取 Neo4j,但不应持有数据库账号和密码。
-
能由可信维护者预先定义查询,并由使用者只传业务参数。
-
希望按 Workspace 隔离 API Key、模板、快捷查询和审计记录。
-
使用 Neo4j Community,或不依赖数据库端的细粒度权限模型。
不适用场景:
-
需要创建、更新、删除节点/关系、管理索引或执行任意 Cypher。
-
要求调用方临时编写查询、调用过程、APOC 或自定义函数。
-
要求高可用多副本、跨实例共享 SQLite 或把 SQLite WAL 放到网络文件系统。
服务不会把同一个 Neo4j 数据库自动切分为多个数据集。两个 Workspace 指向同一物理数据库时,看到的图数据仍相同;隔离的仅是 Key、模板和快捷查询。
## 2. 角色、数据与生命周期
| 对象 | 谁能操作 | 生命周期与含义 |
|---|---|---|
| Workspace | 部署人员通过配置 | 启动时加载;修改源配置或环境变量后必须重建/重启服务 |
|
`reader`
Key | 部署人员创建 | 可查看模板、执行模板、管理本 Workspace 的快捷查询 |
|
`maintainer`
Key | 部署人员创建 | 包含
`reader`
权限,另可创建、更新和禁用模板 |
| 模板 |
`maintainer`
| 更新会新增不可变版本;禁用后不可重新启用 |
| 快捷查询 | 同 Workspace 的任意有效 Key | 固定模板版本与补齐后的参数;模板升级不会改写它 |
| SQLite 元数据 | 服务自身 | 保存 Key 摘要、模板、快捷查询、审计;不保存图数据 |
删除某个 Workspace 的配置不会删除 SQLite 中历史 Key、模板或快捷查询,但这些对象立刻不可访问。以后以同一 Workspace ID 重新启用,会恢复对原有对象的访问;若切换到不同业务数据集,应使用新的 ID。
## 3. 快速启动:Docker
前提:已安装 Docker,且目标 Neo4j 容器已经运行并位于某个 Docker 网络中。此项目不会启动、修改或重建 Neo4j。默认网络名是
`neo4jagent_default`
,但生产环境常因 Neo4j 项目名不同而使用其他名称;启动前必须按下面步骤确认实际网络名。
### 3.1 确认已有 Neo4j 网络
在
**部署目标机器**
执行:
```
bash
docker network
ls
docker ps
--format
'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Networks}}'
```
从输出中找到目标 Neo4j 容器所在的网络。例如容器在
`prod-neo4j_default`
,就在项目目录创建
`.env`
:
```
bash
cp
.env.example .env
# 编辑 .env:
NEO4J_DOCKER_NETWORK
=
prod-neo4j_default
```
也可以不创建
`.env`
,只对本次启动指定:
```
bash
NEO4J_DOCKER_NETWORK
=
prod-neo4j_default docker compose up
-d
--build
```
这个变量只在部署主机上由 Docker Compose 用来选择外部网络,
**不会传入 `query-api` 容器**
,也不会覆盖
`NQ_`
服务配置。若
`docker network ls`
中完全找不到承载目标 Neo4j 的网络,应先部署/启动 Neo4j;不要只创建一个同名空网络,因为服务仍无法解析或连接数据库。
### 3.2 准备 Workspace 配置
```
bash
cp
examples/workspaces.yaml config.local.yaml
```
编辑
`config.local.yaml`
中的 Workspace。以下是最小可用示例;
`uri`
、数据库、用户和密码必须替换成实际值。
```
yaml
config_version
:
1
db_path
:
/data/service.db
workspaces
:
-
id
:
team
name
:
Team graph
neo4j
:
uri
:
bolt://neo4j-agent:7687
database
:
neo4j
username
:
neo4j
password
:
replace-with-a-real-password
```
`id`
必须以小写字母开头,只能使用小写字母、数字、
`_`
和
`-`
,长度不超过 64。不要将真实密码提交到 Git。
### 3.3 启动并验证
```
bash
docker compose up
-d
--build
docker compose
exec
query-api query-service healthcheck
curl http://127.0.0.1:8080/health/live
curl http://127.0.0.1:8080/health/ready
```
默认 HTTP 地址是
`http://127.0.0.1:8080`
,交互式 API 页面是
`http://127.0.0.1:8080/docs`
,OpenAPI 文档为
`/openapi.json`
。示例仅绑定本机回环地址;若要给组成员访问,应先通过反向代理提供 TLS、身份边界与网络访问控制,再有意修改 Compose 的端口映射。
### 3.4 创建首把 Key
`seed`
生成的完整 Key
**只会在这一次输出**
,应立即保存到受保护的密钥管理系统。
```
bash
docker compose
exec
query-api query-service keys seed
\
--config
/run/query-service/config.yaml
\
--workspace
team
--name
owner
--role
maintainer
```
把返回 JSON 里的
`key`
赋给本地环境变量(不要提交该变量值):
```
bash
export
MAINTAINER_KEY
=
'nq_<key-id>.<secret>'
```
至此可以进入
[
模板管理
](
#7-模板管理维护者场景
)
。
## 4. 源码开发与本地运行
要求 Python 3.12 或 3.13 与
`uv`
。开发模式也必须显式指定配置文件;服务进程本身不会读取环境变量。
```
bash
uv
sync
--frozen
--python
3.12
uv run query-service db migrate
--config
/absolute/path/config.local.yaml
uv run query-service serve
--config
/absolute/path/config.local.yaml
```
用于开发的
`db_path`
必须是绝对路径。停止服务后,使用相同配置文件再次运行
`serve`
即可。服务对同一个 SQLite 元数据文件使用进程锁;第二个服务进程会启动失败,不能通过增加 Uvicorn worker 或启动多个容器来扩容。
若要模拟 Docker 的“源配置 + 环境覆盖 + 私有运行配置”流程,可先运行:
```
bash
NQ_CONFIG_SOURCE
=
/absolute/path/config.local.yaml
\
NQ_CONFIG_OUTPUT
=
/tmp/neoquery-runtime.yaml
\
query-service-bootstrap
```
该命令会接管进程并启动服务,适合验证启动流程;日常源码调试通常直接使用
`query-service serve --config`
更直观。
## 5. 配置完整参考
### 5.1 YAML 字段
```
yaml
config_version
:
1
listen_host
:
0.0.0.0
port
:
8080
db_path
:
/data/service.db
limits
:
query_timeout_seconds
:
10
request_timeout_seconds
:
12
max_concurrency
:
16
workspace_concurrency
:
4
key_concurrency
:
2
max_rows
:
1000
max_response_bytes
:
5242880
max_request_bytes
:
262144
log_retention_days
:
14
log_max_records
:
100000
workspaces
:
-
id
:
team
name
:
Team graph
neo4j
:
uri
:
bolt://neo4j-agent:7687
database
:
neo4j
username
:
neo4j
password
:
replace-at-deployment
limits
:
max_concurrency
:
3
max_rows
:
500
```
全局限制的可配置范围如下。所有超时单位为秒,所有字节限制单位为字节。
| 字段 | 默认值 | 有效范围 / 作用 |
|---|---:|---|
|
`query_timeout_seconds`
| 10 |
`>0`
且最多 300;每个 Neo4j 事务时限 |
|
`request_timeout_seconds`
| 12 |
`>0`
且最多 360;整个 HTTP 请求时限 |
|
`max_concurrency`
| 16 | 1–1024;所有 Workspace 合计的执行查询数 |
|
`workspace_concurrency`
| 4 | 1–1024;单 Workspace 执行查询数 |
|
`key_concurrency`
| 2 | 1–1024;单 Key 执行查询数 |
|
`max_rows`
| 1000 | 1–100000;单次返回行数上限 |
|
`max_response_bytes`
| 5 MiB | 1024–100 MiB;完整响应大小上限 |
|
`max_request_bytes`
| 256 KiB | 1024–10 MiB;业务路由请求体上限 |
|
`log_retention_days`
| 14 | 1–3650;审计记录最长保留天数 |
|
`log_max_records`
| 100000 | 至少 100;审计记录数量上限 |
Workspace 可覆盖
`query_timeout_seconds`
、
`request_timeout_seconds`
、
`max_concurrency`
、
`key_concurrency`
、
`max_rows`
与
`max_response_bytes`
,但只能收紧:最终取全局与 Workspace 值的较小者。Workspace 的
`max_concurrency`
对应该 Workspace 的并发上限;
`max_request_bytes`
不能按 Workspace 覆盖。
Neo4j URI 只允许
`bolt://`
、
`neo4j://`
及其
`+s`
/
`+ssc`
变体,且只能包含协议、主机和端口。不能使用
`system`
数据库,也不能把用户名、密码、路径、查询串或片段放进 URI。
### 5.2 Docker 环境覆盖
优先级为:内置默认值 < YAML < 明确设置的
`NQ_`
环境变量。未知的
`NQ_`
环境变量会让启动失败,不会静默忽略;错误配置也不会回退到旧的运行配置。
| 环境变量 | 覆盖内容 |
|---|---|
|
`NQ_CONFIG_SOURCE`
| 源配置,默认
`/etc/query-service/workspaces.yaml`
|
|
`NQ_CONFIG_OUTPUT`
| 启动生成的私有配置,默认
`/run/query-service/config.yaml`
|
|
`NQ_LISTEN_HOST`
、
`NQ_PORT`
、
`NQ_DB_PATH`
| 监听地址、端口、SQLite 路径 |
|
`NQ_QUERY_TIMEOUT_SECONDS`
、
`NQ_REQUEST_TIMEOUT_SECONDS`
| 两类超时 |
|
`NQ_MAX_CONCURRENCY`
、
`NQ_WORKSPACE_CONCURRENCY`
、
`NQ_KEY_CONCURRENCY`
| 三层查询并发上限 |
|
`NQ_MAX_ROWS`
、
`NQ_MAX_RESPONSE_BYTES`
、
`NQ_MAX_REQUEST_BYTES`
| 请求与返回限制 |
|
`NQ_LOG_RETENTION_DAYS`
、
`NQ_LOG_MAX_RECORDS`
| 审计保留策略 |
|
`NQ_WORKSPACE_OVERRIDES_JSON`
| 对已有 Workspace 的
`neo4j`
、
`limits`
、
`name`
进行覆盖 |
`NQ_WORKSPACE_OVERRIDES_JSON`
不能创建新的 Workspace ID。典型用途是在部署系统中替换密码或收紧某一 Workspace 的配额:
```
json
{
"team"
:
{
"neo4j"
:
{
"password"
:
"provided-by-the-deployment-platform"
},
"limits"
:
{
"max_rows"
:
100
}
}
}
```
### 5.3 配置文件和持久化目录权限
容器以 UID/GID
`10001`
运行。配置源必须允许该 UID 读取;生成的
`/run/query-service/config.yaml`
始终为
`0600`
,不写入日志。Compose 使用 tmpfs 保存
`/run/query-service`
,并将整个
`/data`
持久化,因为 SQLite WAL/SHM 文件必须与主数据库一起保留。
不要把 SQLite 数据库或 WAL 放在 NFS 等网络文件系统上,也不要让两个服务进程共享同一份元数据数据库。
## 6. API Key 管理
### 6.1 创建、查看与撤销
```
bash
# 创建无期限维护者 Key
docker compose
exec
query-api query-service keys seed
\
--config
/run/query-service/config.yaml
--workspace
team
--name
owner
--role
maintainer
# 创建 90 天有效的调用方 Key
docker compose
exec
query-api query-service keys seed
\
--config
/run/query-service/config.yaml
--workspace
team
--name
member-a
\
--role
reader
--expires-in-days
90
# 仅列出元数据,不会显示完整 Key
docker compose
exec
query-api query-service keys list
\
--config
/run/query-service/config.yaml
--workspace
team
# 按 Key ID 撤销,重复撤销安全
docker compose
exec
query-api query-service keys revoke
\
--config
/run/query-service/config.yaml
--key-id
<key-id>
```
在源码模式中,保留相同子命令,把配置路径换成实际绝对路径即可。创建、过期和撤销均在下一个请求即时生效,无需重启服务;已经开始的查询会在其原有超时范围内结束。
### 6.2 轮换 Key
1.
创建新 Key。
2.
将调用方配置切换到新 Key,并确认健康调用成功。
3.
撤销旧 Key。
快捷查询属于 Workspace,不属于创建它的 Key,因此轮换 Key 后仍然存在。丢失完整 Key 时不能找回,只能新建并替换。
### 6.3 HTTP 认证格式
所有业务路由都要求:
```
http
Authorization: Bearer nq_<key-id>.<secret>
```
下面命令约定:
```
bash
export
BASE_URL
=
'http://127.0.0.1:8080'
export
WORKSPACE
=
'team'
export
API_KEY
=
'nq_<key-id>.<secret>'
alias
nqcurl
=
'curl -sS -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json"'
```
业务 API 的公共前缀为:
`/api/v1/workspaces/{workspace_id}`
。Key 与路径中的 Workspace 不一致时返回 403。
## 7. 模板管理:维护者场景
模板是受信任的只读查询定义。只有
`maintainer`
能管理模板;服务会先进行保守的词法检查、参数约束和
`EXPLAIN`
校验,随后才保存定义。
### 7.1 创建模板
先根据实际图模型复制并修改
`examples/template.json`
,或创建以下文件
`template.json`
:
```
json
{
"name"
:
"Entity neighbors"
,
"description"
:
"Return one-hop paths for a business entity"
,
"cypher"
:
"MATCH p=(n {entity_id: $entity_id})-[r]-(m) RETURN p LIMIT $limit"
,
"parameter_schema"
:
{
"type"
:
"object"
,
"additionalProperties"
:
false
,
"properties"
:
{
"entity_id"
:
{
"type"
:
"string"
,
"minLength"
:
1
,
"maxLength"
:
128
},
"limit"
:
{
"type"
:
"integer"
,
"minimum"
:
1
,
"maximum"
:
100
,
"default"
:
50
}
},
"required"
:
[
"entity_id"
]
},
"example_params"
:
{
"entity_id"
:
"company_123"
}
}
```
提交:
```
bash
nqcurl
-X
POST
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/templates"
\
--data-binary
@template.json
```
成功返回 201,并包含模板 ID、
`version: 1`
、定义、创建者和时间。请保存返回的
`id`
:后续执行、更新与禁用均使用它。
### 7.2 参数 Schema 规则
`parameter_schema`
使用受限 JSON Schema:
-
每一层都必须明确单一
`type`
;只允许
`string`
、
`integer`
、
`number`
、
`boolean`
、
`null`
、
`array`
、
`object`
。
-
对象必须设置
`additionalProperties: false`
;数组必须提供
`items`
。
-
支持
`required`
、
`enum`
、
`default`
、
`description`
、数值范围、字符串长度、数组数量、对象属性数量。
-
不支持
`pattern`
、
`$ref`
、组合条件、自定义验证或类型自动转换。
-
每一个 Cypher
`$parameter`
必须在顶层
`properties`
声明,且名称完全相同;每个参数必须是 required,或具备 default。
-
JSON 整数必须是真正的整数而非字符串/布尔值,且在 Neo4j 64 位整数范围内;浮点数不能为 NaN 或无穷大。
-
嵌套 Schema 最多 12 层,实际参数最多 32 层。
`example_params`
会补齐默认值并传给
`EXPLAIN`
,仅用于创建/更新时验证,不会执行查询。
### 7.3 更新模板(生成新版本)
先读取当前模板,拿到
`version`
:
```
bash
nqcurl
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/templates/<template-id>"
```
在完整定义中添加
`expected_version`
,再用
`PUT`
提交。不能只提交发生变化的字段。
```
json
{
"name"
:
"Entity neighbors"
,
"description"
:
"Updated description"
,
"cypher"
:
"MATCH p=(n {entity_id: $entity_id})-[r]-(m) RETURN p LIMIT $limit"
,
"parameter_schema"
:
{
"type"
:
"object"
,
"additionalProperties"
:
false
,
"properties"
:
{
"entity_id"
:
{
"type"
:
"string"
},
"limit"
:
{
"type"
:
"integer"
,
"default"
:
50
}},
"required"
:
[
"entity_id"
]},
"example_params"
:
{
"entity_id"
:
"company_123"
},
"expected_version"
:
1
}
```
```
bash
nqcurl
-X
PUT
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/templates/<template-id>"
\
--data-binary
@template-v2.json
```
成功后返回版本 2。若其他维护者已先更新,或模板已被禁用,返回 409;应重新读取当前版本、合并变更并再次提交。历史版本仍可读取和执行,直到模板被禁用。
### 7.4 禁用模板
```
bash
nqcurl
-X
POST
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/templates/<template-id>/disable"
\
--data
'{}'
```
禁用是永久操作:所有历史版本、引用该模板的快捷查询都会停止执行并返回 409;当前版本不再出现在模板列表中。本版本没有重新启用或删除模板的接口,执行前请确认这是预期行为。
### 7.5 可信模板的查询边界
模板只能是一个只读查询。服务拒绝写入、管理语句、过程调用(包括子查询)、APOC、命名空间/自定义函数、
`LOAD CSV`
、
`USE`
、
`EXPLAIN`
/
`PROFILE`
前缀、分号与多语句。内置函数采用白名单,Cypher 还会以只读模式
`EXPLAIN`
校验,并在回滚事务中执行。
这是一层 API 防护,而不是 Neo4j 数据库 RBAC 的替代品。维护者 Key 应视为可信管理权限;仍应为服务使用的 Neo4j 账号配置最小权限并控制网络访问。
## 8. 数据读取:调用方场景
### 8.1 查看可用模板与详情
```
bash
# 分页:默认 20,limit 最大 100
nqcurl
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/templates?offset=0&limit=20"
# 当前版本
nqcurl
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/templates/<template-id>"
# 指定历史版本
nqcurl
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/templates/<template-id>?version=1"
```
列表仅返回已启用模板的当前版本;按
`created_at`
、ID 排序。详情可读取指定历史版本,即使模板已经禁用,但不能执行禁用模板。
### 8.2 执行模板:必填参数、默认值与历史版本
```
bash
nqcurl
-X
POST
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/templates/<template-id>/execute"
\
--data
'{
"params": {"entity_id": "company_123", "limit": 10},
"version": 1,
"max_rows": 10
}'
```
`params`
可省略为
`{}`
,但没有默认值的参数必须提供。
`version`
省略时执行当前版本;指定版本可回放历史模板。
`max_rows`
省略时使用该 Workspace 的行数上限,指定值只能进一步收紧,不能超过配置上限。
当行数达到上限时,服务额外读取一行判断是否存在更多结果,并以
`truncated: true`
返回;不会读取整个结果集。若响应体会超过字节上限,服务返回错误而不会截断 JSON。
### 8.3 常见调用方处理逻辑
1.
通过
`/templates`
展示可选能力,或由业务配置持久化模板 ID。
2.
按模板 Schema 构造严格 JSON 参数,不传未知字段,也不做字符串到数字的隐式转换。
3.
执行请求后检查 HTTP 状态和
`truncated`
;若截断,使用业务支持的筛选、分页参数或更小查询范围重新请求。
4.
依赖
`X-Request-ID`
/响应中的
`request_id`
关联故障与审计,不向日志写入 API Key 或业务参数。
5.
对 429 按指数退避后重试;对 503/504 仅在业务允许时有限重试;对 401/403/409/422 不应盲目重试。
## 9. 快捷查询协作场景
快捷查询适合把“模板 + 某组固定业务参数 + 固定模板版本”分享给同一 Workspace 的成员。
`reader`
和
`maintainer`
都可以管理它们。
### 9.1 创建固定查询
```
bash
nqcurl
-X
POST
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/shortcuts"
\
--data
'{
"name": "Company 123 neighbors",
"template_id": "<template-id>",
"template_version": 1,
"params": {"entity_id": "company_123", "limit": 10}
}'
```
可省略
`template_version`
,服务会解析为创建时的当前具体版本。服务会验证参数并补齐默认值,返回的快捷查询包含
`id`
与初始
`revision: 1`
。模板后续升级不会改变该快捷查询。
### 9.2 查看与执行
```
bash
nqcurl
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/shortcuts?offset=0&limit=20"
nqcurl
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/shortcuts/<shortcut-id>"
nqcurl
-X
POST
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/shortcuts/<shortcut-id>/execute"
\
--data
'{}'
```
快捷查询执行只接受空对象
`{}`
或无请求体;传入
`params`
、
`max_rows`
或其他覆盖字段会返回 422。这样可确保共享快捷查询可复现,且不被调用者改写参数或版本。
### 9.3 修改与删除
修改需要提交完整的新定义和当前
`expected_revision`
,以避免覆盖他人的变更:
```
bash
nqcurl
-X
PUT
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/shortcuts/<shortcut-id>"
\
--data
'{
"name": "Company 123 neighbors (top 20)",
"template_id": "<template-id>",
"template_version": 1,
"params": {"entity_id": "company_123", "limit": 20},
"expected_revision": 1
}'
nqcurl
-X
DELETE
"
$BASE_URL
/api/v1/workspaces/
$WORKSPACE
/shortcuts/<shortcut-id>"
```
并发修改返回 409;先重新读取详情、基于新 revision 合并后再提交。删除后不可恢复,其他调用方读取或执行会得到 404。
## 10. 返回结果与错误处理
### 10.1 成功结果
执行模板或快捷查询成功时,结构如下:
```
json
{
"request_id"
:
"a-request-id"
,
"template_id"
:
"template-id"
,
"template_version"
:
1
,
"columns"
:
[
"n"
],
"rows"
:
[[{
"type"
:
"node"
,
"value"
:
{
"element_id"
:
"4:..."
,
"labels"
:
[
"Company"
],
"properties"
:
{
"name"
:
{
"type"
:
"string"
,
"value"
:
"Example"
}
}
}
}]],
"row_count"
:
1
,
"truncated"
:
false
,
"duration_ms"
:
7
}
```
每一个单元格都采用
`{ "type": "…", "value": … }`
,因此 Neo4j 属性名不会与协议字段冲突。
|
`type`
|
`value`
表示 |
|---|---|
|
`null`
、
`boolean`
、
`string`
| 对应 JSON 值 |
|
`integer`
| 十进制字符串,避免 JavaScript 精度丢失 |
|
`float`
| 普通数值;非有限值为字符串 |
|
`map`
、
`list`
| 递归编码的对象或数组 |
|
`bytes`
| Base64 字符串 |
|
`date`
、
`time`
、
`datetime`
| ISO 格式字符串 |
|
`duration`
|
`months`
、
`days`
、
`seconds`
、
`nanoseconds`
|
|
`point`
|
`srid`
与
`coordinates`
|
|
`node`
|
`element_id`
、有序
`labels`
、递归编码
`properties`
|
|
`relationship`
|
`element_id`
、类型、起止节点
`element_id`
、属性 |
|
`path`
| 有序
`nodes`
与
`relationships`
|
`element_id`
只用于同一响应内的节点/关系关联,不应保存为长期业务 ID。
### 10.2 错误格式和状态码
所有业务路由错误使用同一格式,并在响应头提供
`X-Request-ID`
:
```
json
{
"error"
:
{
"code"
:
"invalid_params"
,
"message"
:
"Parameters do not match the template schema"
},
"request_id"
:
"a-request-id"
}
```
| HTTP 状态 | 常见 code / 含义 | 调用方处理 |
|---:|---|---|
| 401 |
`invalid_key`
| 检查认证头、Key 是否过期或已撤销;不要重试同一 Key |
| 403 |
`workspace_forbidden`
、
`workspace_disabled`
、
`role_forbidden`
| 修正 Workspace/角色或联系部署人员 |
| 404 |
`not_found`
、
`key_not_found`
| 检查资源 ID 与所在 Workspace |
| 409 |
`version_conflict`
、
`template_disabled`
| 重新读取状态;禁用模板不可恢复 |
| 413 |
`request_too_large`
| 缩小请求体 |
| 422 |
`invalid_request`
、
`invalid_params`
、
`invalid_template`
、
`invalid_query`
、
`response_too_large`
| 修正请求、模板或查询范围,勿盲目重试 |
| 429 |
`concurrency_limit`
| 退避后重试;服务不排队 |
| 500 |
`internal_error`
| 保存 request ID 并联系服务维护者 |
| 503 |
`neo4j_unavailable`
、
`storage_unavailable`
、
`audit_unavailable`
| 短暂故障可按业务策略有限重试 |
| 504 |
`request_timeout`
、
`query_timeout`
| 缩小/优化可信模板查询;必要时联系维护者 |
客户端断开请求时服务会取消数据库会话、回收并发名额,并在审计中记录 499;这不是可交付给客户端的正常 HTTP 响应。
## 11. 运行限制与安全边界
-
服务以单实例、单 worker 运行。全局、Workspace 与 Key 三层并发已满时立即返回 429,不建立队列。
-
请求超时或客户端断开会取消 Neo4j 会话;服务不会自动重试查询。
-
原始 Key、参数值、Cypher 正文和查询结果不进入访问审计。审计只记录请求 ID、Key ID、Workspace、路由模式、资源/版本、状态、耗时、行数和错误码。
-
审计日志每分钟清理一次,因此记录数可能在两次清理之间短暂超过阈值。
-
健康检查不写业务审计;业务请求若无法写审计,会返回 503,避免“执行了但无记录”。
-
若一个 Workspace 的 Neo4j 不可达,其他 Workspace 仍可使用;驱动按需连接。
-
结果行数和字节数限制不能替代成本控制:单条巨大记录、无界聚合或低选择性匹配仍可能昂贵。维护者应写入足够的筛选条件、范围和
`LIMIT`
。
## 12. 日常运维:健康、备份、恢复与升级
### 12.1 健康检查
```
bash
# 进程存活;不检查 SQLite
curl
-f
http://127.0.0.1:8080/health/live
# SQLite 可用;Docker healthcheck 使用此接口
curl
-f
http://127.0.0.1:8080/health/ready
# 容器内 CLI 检查当前配置的端口
docker compose
exec
query-api query-service healthcheck
\
--config
/run/query-service/config.yaml
```
`/health/ready`
只验证本地元数据存储,不逐一探测每个 Neo4j Workspace;某一图库不可达时具体业务调用会返回 503。
### 12.2 在线备份
使用 CLI 生成一致性的单个 SQLite 备份文件,目标路径必须不存在:
```
bash
docker compose
exec
query-api query-service db backup
\
--config
/run/query-service/config.yaml
\
--output
/data/backup-20260910.db
```
在源码模式使用:
```
bash
uv run query-service db backup
\
--config
/absolute/path/config.local.yaml
\
--output
/absolute/path/backup-20260910.db
```
不要在服务运行时仅复制主
`.db`
文件;WAL 中可能仍有数据。应使用上述在线备份命令,并将备份复制到独立、安全的存储位置。
### 12.3 恢复
恢复会替换整个服务元数据,因此应在维护窗口内进行:
1.
停止服务。
2.
备份当前整个 SQLite 目录,保留主库、
`-wal`
与
`-shm`
文件。
3.
将已验证的备份文件放到配置中的
`db_path`
。
4.
移走与旧数据库对应的
`-wal`
、
`-shm`
文件,避免使用旧日志恢复新库。
5.
启动服务,检查
`/health/ready`
,并用一个有效 Key 执行只读调用。
恢复不修改 Neo4j 图数据。因为完整 Key 从不存库,恢复后只能继续使用已在外部保存的 Key;若原 Key 已遗失,创建新 Key。
### 12.4 升级、配置变更与停用 Workspace
1.
先在线备份 SQLite 元数据。
2.
更新镜像或源码依赖;数据库迁移会在
`serve`
启动时自动运行,也可先手工执行
`query-service db migrate`
。
3.
对配置/环境变量变更,重建或重启容器;运行中不会热加载。
4.
使用
`healthcheck`
、Key 鉴权和一个低风险模板执行验证。
首版不支持破坏性 downgrade。若升级失败,停止服务并从备份恢复元数据,随后回退镜像/代码;不要手工修改服务 SQLite 表。
## 13. 故障排查
| 现象 | 优先检查 | 处理方向 |
|---|---|---|
| 容器立即退出并提示启动配置无效 | YAML 语法、未知
`NQ_`
变量、Workspace 重复、URI、密码、绝对
`db_path`
| 修正源配置/环境变量后重新启动;错误配置不会使用旧运行配置 |
|
`health/live`
成功但
`health/ready`
为 503 |
`/data`
挂载、SQLite 文件权限或损坏 | 检查目录可写、数据卷和运行日志;必要时按恢复流程处理 |
| Key 返回 401 |
`Authorization`
格式、Key 是否过期/撤销 | 使用新创建的完整 Key;无法恢复丢失的 Key |
| Key 返回 403 | URL Workspace 与 Key 所属 Workspace、角色、配置是否包含该 Workspace | 使用正确路径/Key,或让部署人员创建对应角色 Key |
| 模板/快捷查询 404 | ID、Workspace、是否已删除 | 先列表或详情确认资源所在 Workspace |
| 更新返回 409 |
`expected_version`
/
`expected_revision`
是否过期,模板是否禁用 | 重新读取并合并;禁用模板没有重新启用路径 |
| 模板创建返回 422 | Cypher 是否只读、参数名与 Schema 是否严格一致、
`example_params`
是否有效 | 从错误场景收窄模板,先用最小只读查询验证 |
| 执行返回 429 | 全局/Workspace/Key 并发是否达到配置限制 | 调用方退避;运维人员按容量评估后调整配置并重启 |
| 执行返回 503 | 目标 Neo4j 连通性、凭据、SQLite 审计存储 | 检查服务网络和 Neo4j;对暂时故障有限重试 |
| 执行返回 504 | 查询范围、索引、参数选择性、配置超时 | 优化可信模板或缩小范围,不要简单无限增大超时 |
| 执行返回
`response_too_large`
| 行数、单行体积、图路径/属性大小 | 传更低
`max_rows`
、收窄返回字段或拆分模板 |
| 第二个服务启动失败 | 是否共享了相同
`db_path`
| 保持一份元数据数据库只由一个服务进程提供 HTTP |
排障时记录
`X-Request-ID`
,并检查服务 stdout 日志。不要为排障记录或粘贴 API Key、真实密码、业务参数和完整查询结果。
## 14. 测试与验收
常规静态检查与单元测试:
```
bash
uv run ruff check src migrations tests
uv run pytest
-q
```
真实 Neo4j 集成测试只连接专用的
`127.0.0.1:17687`
,会创建并删除带
`NQAcceptance`
标签的测试数据。切勿将该端口映射到现有业务库。
```
bash
docker run
-d
--name
neoquery-acceptance-db
-p
127.0.0.1:17687:7687
\
-e
NEO4J_AUTH
=
neo4j/acceptance-only-2026
\
-e
NEO4J_server_memory_heap_initial__size
=
256m
\
-e
NEO4J_server_memory_heap_max__size
=
256m
\
-e
NEO4J_server_memory_pagecache_size
=
128m neo4j:5.26.0
uv run pytest
-q
--integration
docker
rm
-f
neoquery-acceptance-db
```
`tests/docker_acceptance.py`
是容器端到端验收脚本。它需要 alpha、beta、local 三个 Workspace:alpha/beta 必须连接隔离库;local 才能指向实际图数据库,且脚本只执行限量读取,不输出业务数据。详细验证结果见
[
验收记录
](
../ACCEPTANCE.md
)
。
## 参考入口
-
项目首页的
[
README
](
../README.md
)
:能力边界、快速命令和配置摘要。
-
OpenAPI 页面:服务启动后访问
`http://127.0.0.1:8080/docs`
;若部署到了其他地址,请替换主机和端口。
-
[
Neo4j Python Driver 事务文档
](
https://neo4j.com/docs/python-manual/current/transactions/
)
:了解读取事务和驱动行为。
-
[
SQLite WAL 文档
](
https://www.sqlite.org/wal.html
)
:了解持久化与备份注意事项。
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment