← 返回 Semantica 专题首页 🌱 SEMANTICA · COOKBOOK · 入门系列

Amazon Neptune 图存储

Semantica 官方 Cookbook 中文翻译 · 第 21 / 37 篇

📦 semantica 🕸️ 知识图谱 🔎 GraphRAG

1概览

本 notebook 介绍 Semantica 中的 Amazon Neptune 数据库集成。Amazon Neptune 是一个全托管的图数据库服务,同时支持属性图(通过 OpenCypher/Gremlin)和 RDF 图(通过 SPARQL)。

关键特性

  • IAM 认证:通过 AuthManager 使用 AWS SigV4 签名进行安全访问
  • OpenCypher 支持:使用标准 OpenCypher 语法进行查询
  • Bolt 协议:使用 Neo4j Bolt 驱动进行高效的二进制通信
  • 原生 ~id 支持:利用 Neptune 的原生元素 ID 处理
  • 完整 CRUD 操作:创建、读取、更新、删除节点和关系
  • 自动重试:内置针对瞬时错误带指数退避的重试逻辑

前置条件

  • 一个 Amazon Neptune 数据库集群
  • 已配置的 AWS 凭证(boto3、环境变量或 IAM 角色)
  • 到你的 Neptune 集群的网络访问(VPC、安全组)
  • 你的公网 IP 地址或 VPN/办公室 CIDR(运行 curl ifconfig.me 查找你的公网 IP),用于下文限制数据库访问

使用 CloudFormation 快速设置

如果你没有 Neptune 集群,使用提供的 CloudFormation 模板创建一个带公网端点和 IAM 认证的集群:

# 部署 Neptune 栈(大约需要 15-20 分钟)
# 将 203.0.113.25/32 替换为你自己的公网 IP(运行 `curl ifconfig.me` 查找)
# 或你的办公室/VPN CIDR。这会在网络层面限制谁能访问数据库——
# 除了短期的本地实验外,绝不要将其放宽到 0.0.0.0/0。
aws cloudformation create-stack \
  --stack-name semantica-neptune \
  --template-body file://neptune-setup.yaml \
  --parameters ParameterKey=ClientCidr,ParameterValue=203.0.113.25/32 \
  --capabilities CAPABILITY_NAMED_IAM

# 等待栈创建完成
aws cloudformation wait stack-create-complete --stack-name semantica-neptune

# 获取输出(端点、端口、凭证)
aws cloudformation describe-stacks --stack-name semantica-neptune \
  --query 'Stacks[0].Outputs' --output table

该模板会创建: - 带公网子网、互联网网关和 VPC 流日志(到 CloudWatch Logs)的 VPC - 启用了 IAM 认证的 Neptune 集群(db.t3.medium) - 具有用于 OpenCypher 查询的最小权限访问的 IAM 用户 - 仅允许来自你指定的 ClientCidr 访问 Bolt 协议(端口 8182)的安全组

⚠️ 安全说明:为简化演示/测试环境,此模板创建了一个带静态访问密钥的 IAM 用户。对于生产环境,我们推荐使用 IAM 角色(EC2 实例角色、ECS 任务角色、Lambda 执行角色),它们提供会自动轮换的临时凭证。CloudFormation 输出中的秘密访问密钥以明文提供,以简化初始设置——在生产环境中,请使用 AWS Secrets Manager。ClientCidr 参数是必需的(无默认值),正是为了确保数据库绝不会被静默地暴露给整个互联网。

输出: - NeptuneEndpoint - 集群主机名(用作 NEPTUNE_ENDPOINT) - NeptunePort - 8182(用作 NEPTUNE_PORT) - AwsAccessKeyId - IAM 用户访问密钥(用作 AWS_ACCESS_KEY_ID) - AwsSecretAccessKey - IAM 用户秘密密钥,明文(用作 AWS_SECRET_ACCESS_KEY) - AwsRegion - 部署区域(用作 AWS_REGION

清理:

# 删除 CloudFormation 栈
aws cloudformation delete-stack --stack-name semantica-neptune

预估月度成本(在 100% 利用率下约为每月 100-105 美元):

资源 成本(美元)
Neptune db.t3.medium 实例 约 96/月(0.132/小时)
存储(10 GB) 约 1/月
I/O 请求 约 1-5/月
公网 IPv4 地址 约 3.60/月(0.005/小时)
VPC 流日志(CloudWatch Logs) 约 1-2/月,取决于流量
VPC、子网、路由表、互联网网关、IAM 无额外费用

免费套餐:新的 Neptune 用户可免费使用 30 天(750 小时 db.t3.medium、1000 万次 I/O、1 GB 存储)。不使用时删除该栈以避免产生费用。


2安装

# 安装带 Neptune 支持的 Semantica
pip install semantica

# 所需依赖(自动安装)
pip install boto3 neo4j
# 安装 semantica 包
!pip install semantica

3配置

设置你的 Neptune 集群端点和 AWS 凭证。将占位值替换为你的实际配置。

import os

# Neptune 集群配置 - 替换为你的值
# (从 CloudFormation 栈输出中获取这些值)
os.environ["NEPTUNE_ENDPOINT"] = "your-cluster.us-east-1.neptune.amazonaws.com"
os.environ["NEPTUNE_PORT"] = "8182"
os.environ["AWS_REGION"] = "us-east-1"

# 用于 IAM 认证的 AWS 凭证
# 选项 1:IAM 用户(来自 CloudFormation 模板的静态凭证)
# os.environ["AWS_ACCESS_KEY_ID"] = "AKIA..."  # 来自 AwsAccessKeyId 输出
# os.environ["AWS_SECRET_ACCESS_KEY"] = "..."  # 来自 AwsSecretAccessKey 输出
# 注意:IAM 用户不需要 AWS_SESSION_TOKEN

# 选项 2:IAM 角色 / 临时凭证(例如 STS AssumeRole、EC2 实例角色)
# os.environ["AWS_ACCESS_KEY_ID"] = "ASIA..."  # 临时访问密钥
# os.environ["AWS_SECRET_ACCESS_KEY"] = "..."  # 临时秘密密钥
# os.environ["AWS_SESSION_TOKEN"] = "..."      # 临时凭证必需

print(f"Neptune Endpoint: {os.environ.get('NEPTUNE_ENDPOINT')}")
print(f"AWS Region: {os.environ.get('AWS_REGION')}")

4步骤 1:初始化 Neptune 存储

使用 IAM 认证初始化到你的 Amazon Neptune 集群的连接。

import os
from semantica.graph_store import GraphStore

# 选项 1:使用 GraphStore 工厂(推荐)
neptune_store = GraphStore(
    backend="neptune",
    endpoint=os.environ.get("NEPTUNE_ENDPOINT"),
    port=int(os.environ.get("NEPTUNE_PORT", 8182)),
    region=os.environ.get("AWS_REGION", "us-east-1"),
    iam_auth=True,
)

# 连接到 Neptune
neptune_store.connect()
print("Connected to Amazon Neptune!")

无需 IAM 认证的开发/测试

对于不需要 IAM 认证的开发或测试环境(例如 Neptune notebook 或仅 VPC 访问),你可以禁用 IAM 签名:

# 用于无 IAM 认证的开发/测试环境
neptune_store_dev = GraphStore(
    backend="neptune",
    endpoint=os.environ.get("NEPTUNE_ENDPOINT"),
    port=int(os.environ.get("NEPTUNE_PORT", 8182)),
    region=os.environ.get("AWS_REGION", "us-east-1"),
    iam_auth=False,  # 为开发/测试禁用 IAM 签名
)
neptune_store_dev.connect()

认证选项

IAM 认证(生产环境推荐)自动使用 AWS 凭证链: 1. 环境变量(AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY) 2. AWS 凭证文件(~/.aws/credentials) 3. IAM 角色(用于 EC2、Lambda、ECS)

5步骤 2:节点操作

创建节点

节点代表图中的实体。每个节点可以有: - ID:唯一标识符(自定义或自动生成的 UUID) - 标签:类别/类型(例如 PersonCompany) - 属性:键值对(例如 {"name": "Alice", "age": 30}

# 使用自定义 ID 创建单个节点(id 在属性中)
alice = neptune_store.create_node(
    labels=["Person"],
    properties={"id": "alice", "name": "Alice", "age": 30, "role": "Engineer"}
)
print(f"Created node: {alice}")

# 使用自动生成的 UUID 创建节点(属性中无 id)
bob = neptune_store.create_node(
    labels=["Person"],
    properties={"name": "Bob", "age": 25, "role": "Designer"}
)
print(f"Created node with UUID: {bob['id']}")

# 使用自动生成的 ID 创建公司节点
acme = neptune_store.create_node(
    labels=["Company"],
    properties={"name": "Acme Corp", "industry": "Technology", "founded": 2010}
)
print(f"Created company: {acme}")

批量创建多个节点

# 批量创建节点以获得更好的性能
# 在属性中包含 'id' 以使用自定义 ID
nodes_data = [
    {"labels": ["Person"], "properties": {"id": "charlie", "name": "Charlie", "age": 35}},
    {"labels": ["Person"], "properties": {"id": "diana", "name": "Diana", "age": 28}},
    {"labels": ["Location"], "properties": {"name": "San Francisco", "state": "CA"}},
]

created_nodes = neptune_store.create_nodes(nodes_data)
print(f"Created {len(created_nodes)} nodes in batch")

检索节点

# 按 ID 获取特定节点
alice_node = neptune_store.get_node(node_id="alice")
print(f"Retrieved: {alice_node}")

# 按标签获取节点
people = neptune_store.get_nodes(labels=["Person"], limit=10)
print(f"Found {len(people)} Person nodes:")
for person in people:
    print(f"  - {person.get('properties', {}).get('name')}")

# 按属性获取节点
engineers = neptune_store.get_nodes(
    labels=["Person"],
    properties={"role": "Engineer"},
    limit=5
)
print(f"Found {len(engineers)} engineers")

更新节点

# 更新节点属性(合并模式 - 默认)
updated_alice = neptune_store.update_node(
    node_id="alice",
    properties={"age": 31, "department": "AI Research"},
    merge=True
)
print(f"Updated Alice: {updated_alice}")

# 替换所有属性(merge=False)
# 警告:这会移除不在更新中的属性
replaced = neptune_store.update_node(
    node_id="charlie",
    properties={"name": "Charlie", "age": 36},
    merge=False
)

删除节点

# 删除节点(使用 detach=True 同时删除关系)
deleted = neptune_store.delete_node(node_id="diana", detach=True)
print(f"Deleted diana: {deleted}")

# 不使用 detach(如果节点有关系则会失败)
# neptune_store.delete_node(node_id="alice", detach=False)

6步骤 3:关系操作

创建关系

关系连接节点,表示实体之间的连接。

# 在 Alice 和 Acme 之间创建关系
works_at = neptune_store.create_relationship(
    start_node_id="alice",
    end_node_id=acme["id"],
    rel_type="WORKS_AT",
    properties={"since": 2020, "position": "Senior Engineer"}
)
print(f"Created relationship: {works_at}")

# 在人与人之间创建 KNOWS 关系
knows_rel = neptune_store.create_relationship(
    start_node_id="alice",
    end_node_id=bob["id"],
    rel_type="KNOWS",
    properties={"since": 2019}
)

检索关系

# 获取某个节点的所有关系
alice_rels = neptune_store.get_relationships(node_id="alice", direction="both")
print(f"Alice has {len(alice_rels)} relationships")

# 仅获取出向关系
outgoing = neptune_store.get_relationships(node_id="alice", direction="out")

# 按关系类型过滤
works_rels = neptune_store.get_relationships(
    node_id="alice",
    rel_type="WORKS_AT",
    direction="out"
)
print(f"Alice's work relationships: {len(works_rels)}")

删除关系

# 按 ID 删除特定关系
if works_at.get("id"):
    deleted = neptune_store.delete_relationship(rel_id=works_at["id"])
    print(f"Deleted relationship: {deleted}")

7步骤 4:OpenCypher 查询

Amazon Neptune 通过 Bolt 协议支持 OpenCypher 查询。使用标准 Cypher 语法执行复杂的图模式。

# 简单查询
results = neptune_store.execute_query(
    "MATCH (p:Person) RETURN p.name, p.age ORDER BY p.age"
)
print("People in the graph:")
for record in results.get("records", []):
    print(f"  - {record.get('p.name')}: {record.get('p.age')} years old")
# 使用参数(更安全、更高效)
results = neptune_store.execute_query(
    "MATCH (p:Person) WHERE p.age > $min_age RETURN p.name, p.age",
    parameters={"min_age": 25}
)
print(f"People over 25: {len(results.get('records', []))}")
# 查找节点之间的关系
results = neptune_store.execute_query("""
    MATCH (p:Person)-[r:WORKS_AT]->(c:Company)
    RETURN p.name as employee, c.name as company, r.since as start_year
""")
for record in results.get("records", []):
    print(f"{record['employee']} works at {record['company']} since {record['start_year']}")
# 计数和聚合
results = neptune_store.execute_query("""
    MATCH (p:Person)
    RETURN count(p) as total, avg(p.age) as avg_age, max(p.age) as max_age
""")
stats = results.get("records", [{}])[0]
print(f"Total: {stats.get('total')}, Avg Age: {stats.get('avg_age'):.1f}")

8步骤 5:图分析

获取邻居

遍历图以查找相连的节点。

# 获取直接邻居(depth=1)
neighbors = neptune_store.get_neighbors(
    node_id="alice",
    direction="both",
    depth=1
)
print(f"Alice's direct neighbors: {len(neighbors)}")

# 获取最多 2 跳之外的邻居
extended = neptune_store.get_neighbors(
    node_id="alice",
    direction="out",
    depth=2
)
print(f"Nodes within 2 hops: {len(extended)}")

最短路径

查找两个节点之间的最短路径。

# 查找最短路径
path = neptune_store.shortest_path(
    start_node_id="alice",
    end_node_id="charlie",
    max_depth=5
)

if path:
    print("Path found!")
    print(f"  Length: {path.get('length')}")
    print(f"  Nodes: {len(path.get('nodes', []))}")
    print(f"  Relationships: {len(path.get('relationships', []))}")
else:
    print("No path found between nodes")

9步骤 6:图统计

获取关于你的图的全面统计信息。

# 获取图统计信息
stats = neptune_store.get_stats()

print("Graph Statistics:")
print(f"  Total nodes: {stats.get('node_count', 'N/A')}")
print(f"  Total relationships: {stats.get('relationship_count', 'N/A')}")

print("\nNode labels:")
for label, count in stats.get('label_counts', {}).items():
    print(f"  - {label}: {count}")

print("\nRelationship types:")
for rel_type, count in stats.get('relationship_type_counts', {}).items():
    print(f"  - {rel_type}: {count}")

10步骤 7:连接管理

完成后始终关闭连接以释放资源。

# 检查连接状态
status = neptune_store.get_status()
print(f"Connection status: {status}")

# 关闭连接
neptune_store.close()
print("Connection closed")

11Neptune 特有注意事项

原生元素 ID

Neptune 使用原生 ~id 进行元素标识。在属性中包含 id 以设置自定义 ID:

# 使用自定义 ID 创建节点(在属性中包含 'id')
node = neptune_store.create_node(
    labels=["Person"],
    properties={"id": "my-custom-id", "name": "Test"}
)

# 使用自动生成的 UUID 创建节点(从属性中省略 'id')
node = neptune_store.create_node(
    labels=["Person"],
    properties={"name": "Test"}
)

# 该 ID 在内部用于 id() 函数调用:
# MATCH (n) WHERE id(n) = 'my-custom-id' RETURN n

OpenCypher 注意事项

Amazon Neptune 数据库的 OpenCypher 实现与 Neo4j 有一些差异:

  1. 没有 shortestPath() 函数:使用变长路径模式或 allShortestPaths()
  2. 标签语法:使用 labels(n) 函数检索节点标签
  3. 属性更新:使用 SET n += {props} 实现合并行为

有关 Amazon Neptune 数据库支持的完整 OpenCypher 规范,请参阅 AWS 文档

Amazon Neptune Analytics

对于分析型(OLAP)工作负载,如图算法、聚合和大规模遍历,请考虑使用 Amazon Neptune Analytics。Neptune Analytics 通过为分析查询提供优化性能来补充 Neptune 数据库,而 Neptune 数据库则针对事务型(OLTP)工作负载进行了优化。

性能提示

  1. 使用批量操作来创建多个节点/关系
  2. 在查询中使用参数以启用查询缓存
  3. 使用 LIMIT 子句限制结果集

12小结

本 notebook 介绍了 Amazon Neptune 图存储集成:

  • IAM 认证:安全的 AWS SigV4 签名
  • CRUD 操作:完整的节点和关系管理
  • OpenCypher 查询:标准图查询语言
  • 图分析:邻居和最短路径算法
  • 统计与监控:图指标和状态

关键要点

  • Neptune 使用原生 ~id 进行元素标识
  • 生产环境推荐使用 IAM 认证
  • Bolt 协议提供高效的二进制查询接口
  • Semantica 抽象了 Neptune 特有的语法差异

下一步