全部文档
当前文档

暂无内容

如果没有找到您期望的内容,请尝试其他搜索词

文档中心

创建和使用 AGE 引擎

最近更新时间:2025-11-11 16:29:40

本文示例将展示如何使用 Apache AGE 建模一个基本的产品目录(Product Catalog)。

假设我们要建立一个服装目录(Catalog),其中包含不同的系列(Collection),例如“春夏系列”和“秋冬系列”。每个系列包含多个商品(Item),商品可以有子商品,每个商品又可关联属性(Attribute)、图片(Image)和附件(Attachment)等信息。

在该模型中:

  • 每个实体(蓝色矩形)对应图中的一个节点(Vertex)

  • 实体之间的关系对应图中的边(Edge)

命名约定如下:

  • 节点标签(Vertex Label:使用 PascalCase 命名,例如 Catalog

  • 边标签(Edge Label):使用全大写蛇形命名,例如 HAS_ATTRIBUTE


设置 search_path

在使用 AGE 图分析引擎前,需要将 ag_catalog 添加至 search_path,以便在查询时简化命名空间。

SET search_path TO public, ag_catalog;

创建图

SELECT create_graph('playground');

该命令将在数据库中创建一个名为 playground 的图。


定义图结构

图创建完成后,可以通过两种方式定义结构:

  1. 动态方式:直接插入节点和边数据,AGE 会自动创建不存在的节点与边标签。

优点:灵活快捷;

缺点:若查询尚未定义的节点或边,会导致查询错误。

  1. 静态方式:在插入数据前预先定义所有节点与边结构。

适用于结构已确定的生产环境。

定义节点与边的语法如下:

-- 创建节点标签
SELECT create_vlabel(GRAPH_NAME,LABEL);

-- 创建边标签
SELECT create_elabel(GRAPH_NAME,LABEL);

示例:

-- 节点标签
SELECT create_vlabel('playground','Catalog');
SELECT create_vlabel('playground','Collection');
SELECT create_vlabel('playground','Item');
SELECT create_vlabel('playground','Attribute');
SELECT create_vlabel('playground','Image');
SELECT create_vlabel('playground','Attachment');

-- 边标签
SELECT create_elabel('playground','BELONG_TO');
SELECT create_elabel('playground','PART_OF');
SELECT create_elabel('playground','CHILD_OF');
SELECT create_elabel('playground','HAS_ATTRIBUTE');
SELECT create_elabel('playground','HAS_IMAGE');
SELECT create_elabel('playground','HAS_ATTACHMENT');

创建边标签时无需显式指定连接的节点类型,实际连接关系由后续数据决定。

底层结构概览

AGE 在底层会为每个节点和边生成对应的数据库表。同时还维护了两个系统表 _ag_label_vertex_ag_label_edge

此外,AGE 在内部的 ag_catalog 模式下维护图的元数据:

  • ag_graph:存储已创建图的信息。

  • ag_label:存储节点与边的标签定义及其与实际表的映射关系。

节点表包含:

  • id:节点内部 ID。

  • properties:节点属性(以 JSON 格式存储)。

边表包含:

  • id:边内部 ID。

  • start_id:起始节点的 ID。

  • end_id:终止节点的 ID。

  • properties:边的属性。


写入数据

创建 Catalog 节点

如下命令创建一个带有 Catalog 标签的新节点和一个名为 code 的属性,值为 C001,然后修改节点添加一个名为 name 的属性,值为 Apparel Catalog,并返回刚刚创建的节点。

SELECT * FROM cypher('playground', $$
    CREATE (c:Catalog { code: "C001" })
    SET c.name = "Apparel Catalog"
    RETURN c
$$) as (catalog agtype);

输出示例:

{"id": 844424930131969, "label": "Catalog", "properties": {"code": "C001", "name": "Apparel Catalog"}}::vertex

如上,AGE 创建了带有 Catalog 标签的节点,为其分配了一个 ID,并在 properties 字段中设置了我们的附加属性。

使用 MERGE 指令避免重复

当我们创建图模式和标签定义时,我们从未定义某种主键,这是因为在 AGE 中没有自定义索引。如果你尝试再次运行相同的创建查询,你将得到两个相同的节点。

因此,在 Cypher 中有第二种创建对象的方式:MERGE 指令。Merge 就像一个 UPSERT 指令,它查找一个匹配模式,如果它不存在,就创建所需的节点。

如下为一个示例:

SELECT * FROM cypher('playground', $$
    MERGE (c:Catalog { code: "C001" })
    RETURN c
$$) AS (catalog agtype);

这条命令检查图中是否存在带有 Catalog 标签且 code 等于 C001 的节点,如果不存在,就创建它。

在这种方式下,不能使用类似于 UPDATESET 指令,需将其拆分为两个单独的命令:

  1. 一个用于创建节点,code 属性设置为我们的“主键”

  2. 一个用于设置附加属性

SELECT * FROM cypher('playground', $$
    MERGE (c:Catalog { code: "C001" })
    RETURN c
$$) as (catalog agtype);

SELECT * FROM cypher('playground', $$
    MATCH (c:Catalog { code: "C001" })
    SET c.name = "Apparel Catalog"
    RETURN c
$$) as (catalog agtype);

这样我们可以确保节点只创建一次。

不能将所有自定义属性放在 MERGE 指令中一起创建。因为如前所述,MERGE 指令通过模式匹配工作,如果使用了超过关键属性的内容,可能会因为已存在的节点具有不同值的属性而最终导致重复。

所以使用 MERGE 指令时只使用关键属性,然后使用 MATCH ... SET 指令更新节点或边。

关联 Collection 与 Catalog

在图中,我们已创建了一个 Catalog 节点。接下来,将创建一个 Collection 节点,并通过 BELONGS_TO 关系将其关联到父级 Catalog。

SELECT * FROM cypher('playground', $$
    MATCH (c:Catalog { code: "C001"} )
    MERGE (co:Collection { code: "SS22" })
    MERGE (co)-[:BELONGS_TO]->(c)
    RETURN co
$$) AS (collection agtype);

在上述示例中:

  • 查询代码为 C001Catalog 节点,并将其引用存储在变量 c 中。

  • 创建一个代码为 SS22Collection 节点。

  • 使用 MERGE (co)-[:BELONGS_TO]->(c) 指令在两者之间建立有向关系。

Cypher 查询语言具有较强的图形化特性,仅通过阅读语句结构,即可直观理解 CollectionCatalog 节点之间的定向关系。

向 Collection 添加多个 Item

接下来,向 SS22 集合中添加若干 Item 节点:

SELECT * FROM cypher('playground', $$
    MATCH (co:Collection { code: "SS22" })
    MERGE (i1:Item { code: 'ISS001'})
    MERGE (i1)-[:PART_OF]->(co)
    MERGE (i2:Item { code: 'ISS002'})
    MERGE (i2)-[:PART_OF]->(co)
    MERGE (i3:Item { code: 'ISS003'})
    MERGE (i3)-[:PART_OF]->(co)
    RETURN [i1, i2, i3]
$$) AS (items agtype);

在该示例中,使用单个 Cypher 查询即可:

  • 创建三个 Item 节点;

  • 并通过 PART_OF 关系将它们全部关联到 SS22 集合。

这体现了 Cypher 语言的另一项优势 —— 在单条语句中执行多个指令,从而大幅提高建模与数据写入的效率。

检索 Catalog 下的所有 Item

假设我们需要查询目录 C001 下的所有 Item 节点,可以通过显式的路径匹配实现:

SELECT * FROM cypher('playground', $$
    MATCH (c:Catalog { code: "C001" })<-[:BELONGS_TO]-(co:Collection)<-[:PART_OF]-(i:Item)
    RETURN i
$$) AS (item agtype);

上例明确匹配了 Catalog → Collection → Item 的路径关系。

如果希望查询更加灵活,也可以使用可变长度路径(Variable-Length Path)表示法:

SELECT * FROM cypher('playground', $$
    MATCH (c:Catalog { code: "C001" })-[*]-(i:Item)
    RETURN i
$$) AS (item agtype);

该语句表示“查找与目录 C001 存在任意长度关系的所有 Item 节点”。

可变路径匹配功能极其强大,但在大型图数据中应谨慎使用。

无约束的路径遍历可能导致查询复杂度指数增长,从而显著增加资源消耗与响应时间。

更新 Item

Collection 中创建完多个 Item 节点后,我们可以进一步为它们添加或更新属性(Attribute)。

在本例中,Attribute 是独立的节点,通过边与 Item 相连接。

更新方式与前文创建 CollectionItem 的方法类似,依然采用 MATCHMERGE 的组合。

SELECT * FROM cypher('playground', $$
    MATCH (i:Item)
    WHERE i.code = "ISS001"
    SET i.name = "T-Shirt"
    MERGE (i)-[:HAS_ATTRIBUTE { value: "S"}]->(:Attribute { name: "size" })
    RETURN i
$$) AS (item agtype);

在该示例中,单条查询语句完成了多项操作:

  1. 匹配节点:通过 MATCH 子句查找代码为 ISS001 的 Item 节点,并使用 WHERE 条件限定结果。

  2. 更新属性:使用 SET 子句为该 Item 节点添加或更新 name = "T-Shirt" 属性。

  3. 建立关系:创建一个名称为 "size" 的 Attribute 节点,并通过带属性 value = "S"HAS_ATTRIBUTE 边与 Item 节点相连。

这种“多指令合并执行”的能力正是 Cypher 语言的优势之一,它能在一条语句中完成节点匹配、属性修改与关系建立,语义清晰且高效。

接下来,我们再为该 Item 添加一个颜色(color)属性。这次可以直接使用单条 MERGE 语句:

SELECT * FROM cypher('playground', $$
    MERGE (i:Item { code: "ISS001"})-[:HAS_ATTRIBUTE { value: "Blue"}]->(:Attribute { name: "color" })
    RETURN i
$$) AS (item agtype);

在图数据模型中,边(Edge) 与 节点(Vertex) 具有相同的重要性。

边不仅定义了节点间的关系,还可以携带属性,如上述示例中的 value = "Blue"

这使得边成为图数据库中的“一等公民”,能够表达更丰富的语义和数据关联。


查询图

当前,我们已经在图中创建了多个节点与边。接下来,演示如何通过 Cypher 查询语言从图中提取所需信息。

前文示例展示了如何获取 Catalog 下的所有 Item 节点。在本节中,我们将查询代码为 ISS001Item 节点的所有 Attribute,但仅返回每个 Attribute 的 name 与 value 属性。

SELECT * FROM cypher('playground', $$
    MATCH (:Item { code: 'ISS001'})-[v:HAS_ATTRIBUTE]->(a:Attribute)
    RETURN a.name, v.value
$$) AS (name agtype, value agtype);

查询结果如下:

| name | value |
| --- | --- |
| "size" | "S" |
| "color" | "Blue" |

查询结果中的值带有双引号,这是因为图分析引擎使用了内部数据类型 agtypeagtype 是 JSON 的超集,返回值本质上是一个 JSON 结构。若希望获得纯字符串结果,可对字段值进行解码以去除双引号。

Cypher 语言允许构建自定义返回结构,使结果更贴合实际应用需求。例如,我们可以将属性名称与属性值封装为单个 JSON 对象返回:

SELECT * FROM cypher('playground', $$
    MATCH (:Item { code: 'ISS001'})-[v:HAS_ATTRIBUTE]->(a:Attribute)
    RETURN {
        name: a.name,
        value: v.value
    }
$$) AS (attr agtype);

输出结果如下:

attr
------
{"name": "size", "value": "S"}
{"name": "color", "value": "Blue"}

删除数据

在使用图分析引擎的过程中,随着数据模型的演进或测试过程的需要,可能需要删除部分节点、边或标签。本节将介绍几种常用的删除操作方式。

删除标签

当节点或边上的某个标签(属性)不再需要时,可使用 REMOVE 关键字进行删除。

-- 为节点添加新标签
SELECT * FROM cypher('playground', $$
  MATCH (i1:Item { code: 'ISS001'})
  SET i1.newlabel = 'to deleted'
  RETURN i1
$$) AS (result_a agtype);

-- 删除该标签
SELECT * FROM cypher('playground', $$
  MATCH (i1:Item { code: 'ISS001'})
  REMOVE i1.newlabel
  RETURN i1
$$) AS (result_a agtype);

删除边

可使用 DELETE 关键字删除指定的边(关系)。

例如,删除名称为 T-Shirt 的 Item 节点与其 Attribute 节点 size 之间的 HAS_ATTRIBUTE 关系:

SELECT * FROM cypher('playground', $$
  MATCH (i1:Item)-[r:HAS_ATTRIBUTE]->(a1:Attribute)
  WHERE i1.name = 'T-Shirt' AND a1.name='size'
  DELETE r
$$) AS (result_a agtype);

此操作仅会删除匹配到的关系(Edge),不会影响相关节点。

删除节点

同样可以使用 DELETE 关键字删除节点。例如,删除所有名称为 size 的 Attribute 节点:

SELECT * FROM cypher('playground', $$
  MATCH(a1:Attribute { name: 'size'})
  DELETE a1
$$) AS (result_a agtype)

若被删除的节点仍与其他节点存在关系(边),此操作将会失败并返回错误。

同时删除节点及其相关边

若节点存在连接的边,直接执行 DELETE 会导致错误,例如:

-- 删除color属性
SELECT * FROM cypher('playground', $$
  MATCH(a1:Attribute { name: 'color'})
  DELETE a1
$$) AS (result_a agtype);

错误信息如下:

ERROR:  Cannot delete a vertex that has edge(s). Delete the edge(s) first, or try DETACH DELETE.

如需同时删除节点及其所有关联的边,可使用 DETACH DELETE 语句:

-- 删除color属性和它对应的边
SELECT * FROM cypher('playground', $$
  MATCH(a1:Attribute { name: 'color'})
  DETACH DELETE a1
$$) AS (result_a agtype);

DETACH DELETE 在执行时会自动删除目标节点与其所有关联的边,适用于需要彻底清理节点的场景。

在大规模图结构中使用时应谨慎操作,以避免误删过多关联数据。


删除图

当不再需要某个图时,可以使用 drop_graph 语句将其从数据库中删除。

SELECT drop_graph('playground');

如果该图下仍存在数据(包括节点、边或定义表),系统将返回错误提示,示例如下:

ERROR:  cannot drop schema playground because other objects depend on it
DETAIL:  table playground._ag_label_vertex depends on schema playground
table playground._ag_label_edge depends on schema playground
table playground."Catalog" depends on schema playground
table playground."Collection" depends on schema playground
table playground."Item" depends on schema playground
table playground."Attribute" depends on schema playground
table playground."Image" depends on schema playground
table playground."Attachment" depends on schema playground
table playground."BELONG_TO" depends on schema playground
table playground."PART_OF" depends on schema playground
table playground."CHILD_OF" depends on schema playground
table playground."HAS_ATTRIBUTE" depends on schema playground
table playground."HAS_IMAGE" depends on schema playground
table playground."HAS_ATTACHMENT" depends on schema playground
table playground."BELONGS_TO" depends on schema playground
HINT:  Use DROP ... CASCADE to drop the dependent objects too.

强制删除图

若希望递归删除图及其下的所有对象(包括节点、边、表结构等),可在命令中添加第二个参数 true,表示启用 级联删除(CASCADE)

SELECT drop_graph('playground', true);

执行后,系统将输出被递归删除的对象列表。例如:

NOTICE:  drop cascades to 15 other objects
DETAIL:  drop cascades to table playground._ag_label_vertex
drop cascades to table playground._ag_label_edge
drop cascades to table playground."Catalog"
drop cascades to table playground."Collection"
drop cascades to table playground."Item"
drop cascades to table playground."Attribute"
drop cascades to table playground."Image"
drop cascades to table playground."Attachment"
drop cascades to table playground."BELONG_TO"
drop cascades to table playground."PART_OF"
drop cascades to table playground."CHILD_OF"
drop cascades to table playground."HAS_ATTRIBUTE"
drop cascades to table playground."HAS_IMAGE"
drop cascades to table playground."HAS_ATTACHMENT"
drop cascades to table playground."BELONGS_TO"
NOTICE:  graph "playground" has been dropped
 drop_graph
------------

(1 row)

文档导读
纯净模式常规模式

纯净模式

点击可全屏预览文档内容
文档反馈