最近更新时间:2025-11-11 16:29:40
在处理已有业务数据或大规模数据场景时,若采用 CREATE 语法逐条插入节点和边,将导致性能极低。为提高效率,建议先将原始数据导入至关系型表中,再通过以下步骤批量写入图分析引擎(AGE)。
以下示例基于前文 创建与使用 AGE 图分析引擎 中的模型,假设包含以下三个节点:
Catalog
Collection
Item
以及两种关系:
BELONGS_TO:表示 Collection 属于 Catalog
PART_OF:表示 Item 属于 Collection
接下来,我们将演示如何从关系表批量导入这些节点与边到 AGE 图中。
在将数据导入 AGE 图分析引擎之前,首先需要在关系型数据库中创建原始数据表,用于存放节点和关系数据。以下示例分别创建 Catalog、Collection、Item 三类节点表,以及 BELONGS_TO 与 PART_OF 两类关系表,并插入示例数据。
创建 Catalog 表(用于存储 Catalog 数据)并写入样本数据:
CREATE TABLE IF NOT EXISTS catalog_raw (
id BIGINT,
code TEXT,
name TEXT,
description TEXT
);
INSERT INTO catalog_raw VALUES
('0', 'C001', 'Apparel Catalog', 'Fashion and clothing catalog'),
('1', 'C002', 'Electronics Catalog', 'Electronic devices and accessories');创建 Collection 表(用于存放 Collection 数据)并写入样本数据:
CREATE TABLE IF NOT EXISTS collection_raw (
id BIGINT,
code TEXT,
name TEXT,
season TEXT
);
INSERT INTO collection_raw VALUES
('10', 'SS22', 'Spring Summer 2022', 'Spring'),
('11', 'FW22', 'Fall Winter 2022', 'Fall'),
('12', 'SS23', 'Spring Summer 2023', 'Spring');创建 Item 表(用于存放 Item 数据)并写入样本数据:
CREATE TABLE IF NOT EXISTS item_raw (
id BIGINT,
code TEXT,
name TEXT,
price DECIMAL
);
INSERT INTO item_raw VALUES
('100', 'ISS001', 'T-Shirt', 29.99),
('101', 'ISS002', 'Jeans', 79.99),
('102', 'ISS003', 'Sneakers', 129.99),
('103', 'IWS001', 'Winter Coat', 199.99),
('104', 'IWS002', 'Boots', 149.99);该表用于存储商品(Item)信息,字段说明如下:
id:唯一标识符
code:商品代码
name:商品名称
price:商品价格
创建 BELONGS_TO 表(用于存放 Collection 到 Catalog 的关系数据)并写入样本数据:
CREATE TABLE IF NOT EXISTS collection_belongsto_catalog_raw (
start_id BIGINT,
end_id BIGINT
);
INSERT INTO collection_belongsto_catalog_raw VALUES ('10','0'), ('11','0'), ('12','0');该表定义了 Collection → Catalog 的从属关系(BELONGS_TO),即每个系列所属的目录。
创建 PART_OF 表(用于存放 Item 到 Collection 的关系数据)并写入样本数据。
CREATE TABLE IF NOT EXISTS item_partof_collection_raw (
start_id BIGINT,
end_id BIGINT
);
INSERT INTO item_partof_collection_raw VALUES ('100','10'), ('101','10'), ('102','10'), ('103','11'), ('104','11');该表定义了 Item → Collection 的归属关系(PART_OF),表示每个商品所属的系列。
在批量将关系表数据写入 AGE 图结构前,需要创建两个辅助函数,用于生成节点和边的唯一标识符(ID)及序列号。这些函数将帮助 AGE 在导入过程中保持 ID 的唯一性和一致性。
创建辅助函数 age_name_to_idx_start。该函数根据图名称、对象类型(节点或边)及标签名称生成唯一的 ID 起始值。
CREATE OR REPLACE FUNCTION age_name_to_idx_start(graph_name text, kind_name text, label_name text)
RETURNS bigint
AS 'SELECT id::bigint<<48 FROM ag_catalog.ag_label WHERE kind = kind_name AND name = label_name AND graph = (SELECT graphid FROM ag_catalog.ag_graph WHERE name = graph_name)'
LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;创建函数 age_name_to_seq。该函数用于返回节点或边对应的内部自增序列名称,用于生成后续插入时的唯一 ID。
CREATE OR REPLACE FUNCTION age_name_to_seq(graph_name text, kind_name text, label_name text)
RETURNS text
AS $$SELECT graph_name || '."' || seq_name || '"' FROM ag_catalog.ag_label WHERE kind = kind_name and name = label_name and graph = (SELECT graphid FROM ag_catalog.ag_graph WHERE name = graph_name)$$
LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;参数说明
graph_name:图名称。
kind_name:类型(v 表示节点,e 表示边)。
label_name:标签名称。
这两个函数在后续写入阶段会被用来自动生成节点和边的唯一标识。
首先,创建一个新的图对象:
SELECT ag_catalog.create_graph('playground');然后,为该图定义所需的节点标签(Vertex Label)和边标签(Edge Label):
-- 创建节点标签
SELECT ag_catalog.create_vlabel('playground', 'Catalog');
SELECT ag_catalog.create_vlabel('playground', 'Collection');
SELECT ag_catalog.create_vlabel('playground', 'Item');
-- 创建边标签
SELECT ag_catalog.create_elabel('playground', 'BELONGS_TO');
SELECT ag_catalog.create_elabel('playground', 'PART_OF');节点标签对应数据模型中的实体类型。
边标签定义节点间的关联关系。
标签创建完成后,AGE 会在图对应的 schema 下自动生成相应的存储表结构。
在完成图结构及辅助函数的创建后,可以将关系表中的原始数据批量导入为图中的节点数据。
以下 SQL 语句分别导入 Catalog、Collection 和 Item 三类节点数据,并同步更新对应的序列值。
-- 导入 Catalog 数据
INSERT INTO playground."Catalog"
SELECT
(age_name_to_idx_start('playground', 'v', 'Catalog') + id)::text::ag_catalog.graphid,
row_to_json((SELECT x FROM (SELECT code, name, description) x))::text::ag_catalog.agtype
FROM catalog_raw;
-- 设置 Catalog 的序列值
SELECT setval(age_name_to_seq('playground', 'v', 'Catalog'), (SELECT max(id) + 1 FROM catalog_raw));
-- 导入 Collection 数据
INSERT INTO playground."Collection"
SELECT
(age_name_to_idx_start('playground', 'v', 'Collection') + id)::text::ag_catalog.graphid,
row_to_json((SELECT x FROM (SELECT code, name, season) x))::text::ag_catalog.agtype
FROM collection_raw;
-- 设置 Collection 的序列值
SELECT setval(age_name_to_seq('playground', 'v', 'Collection'), (SELECT max(id) + 1 FROM collection_raw));
-- 导入 Item 数据
INSERT INTO playground."Item"
SELECT
(age_name_to_idx_start('playground', 'v', 'Item') + id)::text::ag_catalog.graphid,
row_to_json((SELECT x FROM (SELECT code, name, price) x))::text::ag_catalog.agtype
FROM item_raw;
-- 设置 Item 的序列值
SELECT setval(age_name_to_seq('playground', 'v', 'Item'), (SELECT max(id) + 1 FROM item_raw));说明:
每个节点表都通过 age_name_to_idx_start 计算唯一 ID,确保在同一图中不冲突。
row_to_json() 用于将节点属性转换为 JSON 结构,以符合 AGE 图的存储格式。
通过 setval() 同步更新内部序列,保证后续插入数据时 ID 连续。
节点导入完成后,可将关系表中的关联数据导入为图中的边(Edge)。以下 SQL 语句批量创建 BELONGS_TO 和 PART_OF 两种关系。
-- 导入BELONGS_TO边的数据
INSERT INTO playground."BELONGS_TO"(start_id,end_id,properties)
SELECT
(age_name_to_idx_start('playground', 'v', 'Collection') + start_id)::text::ag_catalog.graphid,
(age_name_to_idx_start('playground', 'v', 'Catalog') + end_id)::text::ag_catalog.graphid,
'{}'::text::ag_catalog.agtype
FROM collection_belongsto_catalog_raw;
-- 导入PART_OF边的数据
INSERT INTO playground."PART_OF"(start_id,end_id,properties)
SELECT
(age_name_to_idx_start('playground', 'v', 'Item') + start_id)::text::ag_catalog.graphid,
(age_name_to_idx_start('playground', 'v', 'Collection') + end_id)::text::ag_catalog.graphid,
'{}'::text::ag_catalog.agtype
FROM item_partof_collection_raw;完成节点与边的导入后,可通过 Cypher 查询验证数据是否正确写入图中:
SELECT * FROM ag_catalog.cypher('playground', $$
MATCH (c:Catalog)<-[:BELONGS_TO]-(co:Collection)<-[:PART_OF]-(i:Item)
RETURN c, co, i
LIMIT 5
$$) AS (catalog ag_catalog.agtype, collection ag_catalog.agtype, item ag_catalog.agtype);
纯净模式