开发版本: 19 / devel
此文档适用于不受支持的 PostgreSQL 版本。
您可能需要查看当前版本的相同页面,或上面列出的其他受支持版本。

5.8. 属性图 #

属性图是一种表示数据库内容的方法,它是传统(在 SQL 中)使用表等关系结构来表示数据库内容的替代方案。 随后便可以使用图模式匹配语法来查询属性图,而不是采用关系数据库中常见的连接查询。 PostgreSQL 实现了 SQL/PGQ[6], 这是 SQL 标准的一部分,其中属性图被定义为关系表上的一种只读视图。因此,实际数据仍然存放在表或类似表的对象中, 但在图查询操作中会作为图暴露出来。(这与原生图数据库形成对比,后者的数据直接存储在图结构中。) 在底层,关系查询和图查询使用的是同一套查询规划与执行基础设施,实际上两者还能在同一个查询中组合并混合使用。

图是由顶点和边构成的集合。每条边都有两个可区分的关联顶点,称为源顶点和目标顶点。 (因此在这个模型中,所有边都是有向的。)顶点和边合在一起称为图的元素。属性图在这个众所周知的数学结构上 增加了一种表示用户数据的方式。在属性图中,每个顶点或边都有一个或多个关联标签,每个标签又有零个或多个属性。 这些标签类似于表行类型,因为它们定义了所含数据的种类及其结构。属性则类似于列,因为它们包含实际数据。 实际上,默认情况下,属性图定义会把底层表和列分别暴露为标签和属性,但也可以定义更复杂的映射。

先看下面这些表定义:

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name varchar,
    price numeric
);

CREATE TABLE customers (
    customer_id integer PRIMARY KEY,
    name varchar,
    address varchar
);

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    ordered_when date
);

CREATE TABLE order_items (
    order_items_id integer PRIMARY KEY,
    order_id integer REFERENCES orders (order_id),
    product_no integer REFERENCES products (product_no),
    quantity integer
);

CREATE TABLE customer_orders (
    customer_orders_id integer PRIMARY KEY,
    customer_id integer REFERENCES customers (customer_id),
    order_id integer REFERENCES orders (order_id)
);

把这些表映射成图后,前三个表会成为顶点,后两个表会成为边。外键定义对应于边连接两个顶点这一事实。 (图定义通常更适合多对多关系,因此这个例子按这种方式组织,尽管在纯关系方式中这里可能会使用一对多关系。)

下面给出一个在这些表之上定义属性图的例子:

CREATE PROPERTY GRAPH myshop
    VERTEX TABLES (
        products,
        customers,
        orders
    )
    EDGE TABLES (
        order_items SOURCE orders DESTINATION products,
        customer_orders SOURCE customers DESTINATION orders
    );

然后就可以像这样查询这个图:

-- get list of customers active today
SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers)-[IS customer_orders]->(o IS orders WHERE o.ordered_when = current_date) COLUMNS (c.name AS customer_name));

其大致对应的关系查询是:

-- get list of customers active today
SELECT customers.name FROM customers JOIN customer_orders USING (customer_id) JOIN orders USING (order_id) WHERE orders.ordered_when = current_date;

上面的定义要求所有表都具有主键,且每条边都有合适的外键。否则,就必须额外指定键列。 例如,下面给出一个完全显式、不依赖主键和外键的定义:

CREATE PROPERTY GRAPH myshop
    VERTEX TABLES (
        products KEY (product_no),
        customers KEY (customer_id),
        orders KEY (order_id)
    )
    EDGE TABLES (
        order_items KEY (order_items_id)
            SOURCE KEY (order_id) REFERENCES orders (order_id)
            DESTINATION KEY (product_no) REFERENCES products (product_no),
        customer_orders KEY (customer_orders_id)
            SOURCE KEY (customer_id) REFERENCES customers (customer_id)
            DESTINATION KEY (order_id) REFERENCES orders (order_id)
    );

如上所述,默认情况下,表名和列名会分别作为标签和属性暴露出来。MATCH 子句中的 IS customerIS order 等子句实际上引用的是标签, 而不是表名。

标签的一个用途是把表以不同的名称暴露到图中。例如,在图里,顶点的标签通常是单数名词, 边的标签通常是动词或从动词派生的短语,例如 hascontains 或者像 approved_by 这样的具体表达。我们可以像下面这样把这类标签引入示例:

CREATE PROPERTY GRAPH myshop
    VERTEX TABLES (
        products LABEL product,
        customers LABEL customer,
        orders LABEL "order"
    )
    EDGE TABLES (
        order_items SOURCE orders DESTINATION products LABEL contains,
        customer_orders SOURCE customers DESTINATION orders LABEL has_placed
    );

使用这个定义,我们就可以写出如下查询:

SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customer)-[IS has_placed]->(o IS "order" WHERE o.ordered_when = current_date) COLUMNS (c.name AS customer_name));

使用这些新标签后,MATCH 子句就更直观了。

请注意,标签 order 是带引号的。如果在上述语句中不为 order 加引号,就会得到语法错误,因为 order 是关键字。

另一个用途是把同一个标签应用到多个元素表。例如,考虑再增加这样一张表:

CREATE TABLE employees (
    employee_id integer PRIMARY KEY,
    employee_name varchar,
    ...
);

以及下面的图定义:

CREATE PROPERTY GRAPH myshop
    VERTEX TABLES (
        products LABEL product,
        customers LABEL customer LABEL person PROPERTIES (name),
        orders LABEL order,
        employees LABEL employee LABEL person PROPERTIES (employee_name AS name)
    )
    EDGE TABLES (
        order_items SOURCE orders DESTINATION products LABEL contains,
        customer_orders SOURCE customers DESTINATION orders LABEL has
    );

(实际上,employees 表最好也连接到某个东西,但像这样写也是允许的。) 然后我们可以运行这样一个查询(不完整):

SELECT ... FROM GRAPH_TABLE (myshop MATCH (IS person WHERE name = '...')-[]->... COLUMNS (...));

这样在查找带有 person 标签的边时,就会自动同时考虑 customersemployees 两张表。

当多个元素表具有相同标签时,它们的属性在数量、名称和类型上必须一致。在这个例子中, 我们通过显式属性列表,并在一个地方重命名列来实现这一点。

通过让一个元素表关联多个标签,并让每个标签暴露不同的一组属性,相同的关系数据及其内部图结构 就可以通过多个并存的逻辑视图暴露出来,并用图模式匹配构造进行查询。

关于创建属性图的语法细节,请参见 CREATE PROPERTY GRAPH。 关于图查询语法的更多细节,请参见 Section 7.9



[6] 这里 PGQ 代表 property graph query。在图数据库术语中,property graph 通常缩写为 PG,这对 PostgreSQL 的使用者显然会造成混淆, 因为 PostgreSQL 也通常缩写为 PG。