PostgreSQL实战指南:多领域数据库应用与实践


第二部分:PostgreSQL与图数据库

传统的关系型数据库在处理复杂关系网络(如社交关系、推荐系统、供应链)时,往往面临查询复杂、性能瓶颈等挑战。图数据库因其数据模型与关系网络的高度契合,逐渐受到关注。PostgreSQL虽然不是原生图数据库,但通过其高度可扩展的架构和强大的扩展机制(如 AgePG-Rount 等),能够有效地存储和查询图数据,成为构建轻量级或混合型图应用的可行选择。

本部分将探讨如何在PostgreSQL中建模和管理图数据,介绍相关的扩展,并深入讲解如何进行图遍历、路径查找和社交网络分析等,通过实战案例让你理解PostgreSQL在图数据领域的应用潜力。


第9章:图数据模型与Apache Age

本章将介绍图数据库的基本概念,以及如何在PostgreSQL中利用Apache Age扩展来构建和管理图数据。Apache Age 是一个PostgreSQL扩展,它将图数据库功能引入PostgreSQL,允许用户在一个数据库中同时使用关系型和图数据模型,极大地提升了PostgreSQL处理复杂互联数据的能力。

9.1 图数据库基本概念

在深入Apache Age之前,我们首先回顾图数据库的核心概念:

  • 节点(Nodes/Vertices):代表图中的实体,类似于关系型数据库中的行或对象。每个节点可以拥有属性(Properties)。
    • 示例:用户、商品、城市。
  • 关系/边(Relationships/Edges):代表节点之间的连接或交互。关系也有方向性,并可以拥有属性。
    • 示例:用户“关注”用户,用户“购买”商品,城市“连接”城市。
  • 属性(Properties):附加在节点和关系上的键值对,用于存储实体的详细信息或关系的特征。
    • 示例:用户节点的nameage;“购买”关系的quantitypurchase_date
  • 标签(Labels):用于对节点进行分类。一个节点可以有多个标签。
    • 示例UserProductOrder
  • 类型(Types):用于对关系进行分类。一个关系只能有一个类型。
    • 示例FOLLOWSBOUGHTLOCATED_IN

图数据库的优势:

  • 直观的数据模型:与复杂的关系网络自然契合。
  • 高性能的图遍历:针对图结构优化,查询多跳关系非常高效。
  • 灵活的Schema:通常支持灵活的Schema,便于适应不断变化的数据结构。
9.2 Apache Age 概述与安装

Apache Age 是一个PostgreSQL扩展,它实现了 openCypher 查询语言(Neo4j 引入的图查询语言),并支持属性图模型。通过Age,你可以在PostgreSQL中创建图、节点和关系,并使用 openCypher 进行图查询。

安装 Apache Age:

安装Apache Age通常涉及编译和安装扩展,这需要一些系统依赖。以下是一个简化的安装流程(具体步骤可能因操作系统和PostgreSQL版本而异,请务必参考Apache Age 官方文档获取最新和最准确的指引):

  1. 安装PostgreSQL和开发库:确保你的系统安装了PostgreSQL服务器以及PostgreSQL的开发库(如postgresql-devellibpq-dev)。
  2. 安装必要的构建工具:如gccmake等。
  3. 下载Apache Age源码
    1
    2
    
    git clone https://github.com/apache/age.git
    cd age
    
  4. 编译和安装
    1
    
    sudo make PG_CONFIG=/usr/bin/pg_config install # 替换为你的 pg_config 路径
    
    • pg_config 的路径可以通过 which pg_configfind /usr -name pg_config 找到。
  5. 配置PostgreSQL: 编辑postgresql.conf文件,将age添加到shared_preload_libraries中。
    1
    
    shared_preload_libraries = 'age'
    
  6. 重启PostgreSQL服务
    1
    
    sudo systemctl restart postgresql # 或根据你的系统使用相应命令
    
  7. 在数据库中创建扩展: 连接到你的PostgreSQL数据库,然后执行:
    1
    2
    3
    
    CREATE EXTENSION age;
    LOAD 'age';
    SET search_path = ag_catalog, "$user", public;
    
    • LOAD 'age';:加载 Age 模块。
    • SET search_path = ag_catalog, "$user", public;:将 ag_catalog schema 添加到搜索路径,以便可以直接访问 Age 的函数和视图。
9.3 图数据模型创建与管理

在Apache Age中,你需要先创建一个图(graph),然后在这个图内部创建节点和关系。

实战举例:社交网络图建模

我们将创建一个简单的社交网络,包含用户(节点)和他们之间的“关注”关系。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
-- 1. 创建一个图
SELECT create_graph('social_network');

-- 2. 创建节点 (Users)
-- 使用 ag_graph.create_v_cypher 函数创建节点,第一个参数是图的名称,第二个参数是 openCypher 语句
-- 节点会自动分配一个唯一的 ID (ag_id)
SELECT * FROM ag_graph.create_v_cypher('social_network', $$
    CREATE (a:Person {name: 'Alice', age: 30}),
           (b:Person {name: 'Bob', age: 25}),
           (c:Person {name: 'Charlie', age: 35})
    RETURN a, b, c
$$) AS nodes;

-- 3. 创建关系 (FOLLOWS)
-- 使用 ag_graph.create_e_cypher 函数创建关系
-- 关系需要指定起点节点、关系类型、终点节点,以及可选的属性
SELECT * FROM ag_graph.create_e_cypher('social_network', $$
    MATCH (a:Person), (b:Person), (c:Person)
    WHERE a.name = 'Alice' AND b.name = 'Bob' AND c.name = 'Charlie'
    CREATE (a)-[r1:FOLLOWS {since: 2020}]->(b),
           (b)-[r2:FOLLOWS]->(c)
    RETURN r1, r2
$$) AS relationships;

-- 4. 查看图中的所有节点
-- 使用 openCypher 查询语言,通过 ag_graph.cypher 函数执行查询
SELECT * FROM ag_graph.cypher('social_network', $$
    MATCH (n) RETURN n
$$) AS nodes;

-- 5. 查看图中的所有关系
SELECT * FROM ag_graph.cypher('social_network', $$
    MATCH ()-[r]->() RETURN r
$$) AS relationships;

-- 6. 查看特定标签的节点及其属性
SELECT * FROM ag_graph.cypher('social_network', $$
    MATCH (p:Person) WHERE p.age < 30 RETURN p.name, p.age
$$) AS young_people;

-- 7. 节点和关系的更新与删除
-- 更新节点属性
SELECT * FROM ag_graph.cypher('social_network', $$
    MATCH (p:Person {name: 'Alice'})
    SET p.age = 31
    RETURN p
$$) AS updated_alice;

-- 删除关系
SELECT * FROM ag_graph.cypher('social_network', $$
    MATCH (a:Person {name: 'Alice'})-[r:FOLLOWS]->(b:Person {name: 'Bob'})
    DELETE r
$$) AS deleted_relationship;

-- 删除节点及其所有关联关系
SELECT * FROM ag_graph.cypher('social_network', $$
    MATCH (c:Person {name: 'Charlie'})
    DETACH DELETE c -- DETACH DELETE 会先删除节点关联的所有关系,再删除节点本身
$$) AS deleted_charlie;
9.4 属性图模型与关系型数据的结合

Apache Age 的一个强大之处在于它允许你将图数据和传统的关系型数据存储在同一个PostgreSQL数据库中,并进行交互。你可以将关系型表中的数据映射为图中的节点或属性,反之亦然。

实战举例:电商系统中的图数据与关系型数据结合

我们之前有usersproducts表。我们可以将它们映射为图中的节点,并创建“购买”关系。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
-- 1. 将现有关系型数据映射为图节点
-- 从 users 表创建 Person 节点
SELECT * FROM ag_graph.create_v_cypher('social_network', $$
    CREATE (p:Person {user_id: 1, name: 'Alice'})
$$) AS alice_node; -- 实际中,可以编写循环或函数来批量导入

SELECT * FROM ag_graph.create_v_cypher('social_network', $$
    CREATE (p:Person {user_id: 2, name: 'Bob'})
$$) AS bob_node;

-- 从 products 表创建 Product 节点
SELECT * FROM ag_graph.create_v_cypher('social_network', $$
    CREATE (prod:Product {product_id: 1, name: 'Laptop Pro', price: 1200.00})
$$) AS laptop_node;

SELECT * FROM ag_graph.create_v_cypher('social_network', $$
    CREATE (prod:Product {product_id: 2, name: 'Mechanical Keyboard', price: 80.00})
$$) AS keyboard_node;

-- 2. 基于 order_items 表创建 BUY 关系
-- 这需要更复杂的逻辑,可能通过 PL/pgSQL 函数或应用程序代码来批量创建
-- 假设我们已经知道 user_id 和 product_id 对应的 ag_id,或者通过属性匹配
-- 这里我们手动指定 user_id 和 product_id 来创建关系
SELECT * FROM ag_graph.create_e_cypher('social_network', $$
    MATCH (u:Person {user_id: 1}), (p:Product {product_id: 1})
    CREATE (u)-[b:BOUGHT {quantity: 1, purchase_date: '2023-01-15'}]->(p)
    RETURN u, b, p
$$) AS bought_relationship_1;

SELECT * FROM ag_graph.create_e_cypher('social_network', $$
    MATCH (u:Person {user_id: 1}), (p:Product {product_id: 2})
    CREATE (u)-[b:BOUGHT {quantity: 1, purchase_date: '2023-01-15'}]->(p)
    RETURN u, b, p
$$) AS bought_relationship_2;

-- 3. 进行混合查询:结合关系型数据和图数据
-- 查找购买了特定商品的用户名称(通过图查询,然后关联回关系型数据)
SELECT u.username, graph_data.product_name
FROM users u
JOIN (
    SELECT
        (u.properties ->> 'user_id')::INTEGER AS user_id,
        (p.properties ->> 'name')::TEXT AS product_name
    FROM ag_graph.cypher('social_network', $$
        MATCH (u:Person)-[:BOUGHT]->(p:Product)
        WHERE p.name = 'Laptop Pro'
        RETURN u, p
    $$) AS result
    JOIN LATERAL (SELECT * FROM result.u) AS u(id, label, properties) ON true
    JOIN LATERAL (SELECT * FROM result.p) AS p(id, label, properties) ON true
) AS graph_data ON u.user_id = graph_data.user_id;

-- 这个例子展示了如何通过 ag_graph.cypher 函数的输出(这是一个 JSONB 结构)
-- 来提取节点属性,并与关系型表进行 JOIN。
-- 注意:这种直接在 SQL 中解析 JSONB 并 JOIN 的方式会比较复杂和低效,
-- 实际生产中通常会通过应用程序层或更高级的集成策略来处理。
9.5 总结

本章我们初步探索了PostgreSQL在图数据库领域的应用,重点介绍了Apache Age扩展。我们学习了图数据库的基本概念(节点、关系、属性、标签),掌握了如何安装和初始化Age,并通过实战演示了如何在PostgreSQL中创建、管理图数据,并执行基本的 openCypher 查询。此外,我们还看到了将关系型数据与图数据结合的可能性。

通过本章的学习,你现在应该能够:

  • 理解图数据库的基本概念和优势。
  • 掌握Apache Age扩展的安装和基本使用。
  • 在PostgreSQL中创建和管理属性图(节点和关系)。
  • 执行基本的 openCypher 查询来检索和操作图数据。
  • 初步理解如何在PostgreSQL中结合关系型和图数据。

在下一章中,我们将深入图查询与遍历,学习 openCypher 语言的更多高级特性,包括路径查询、模式匹配等,从而更高效地从复杂关系网络中提取洞察。