原生投影

此功能在 Aura 图分析无服务器中不可用。

原生投影是从 Neo4j 数据库创建 GDS 图 的最简单方法。原生投影完全由配置参数描述。

节点投影关系投影描述了节点和关系从数据库加载(投影)到内存图中的方式。节点投影基于节点标签,而关系投影基于关系类型。两者都可以包含属性。

注意事项

生命周期

投影图存储在内存中(在图目录中),直到发生以下任何情况:

  • 使用 gds.graph.drop 过程删除图。

  • 投影图的 Neo4j 数据库停止或删除。

  • Neo4j DBMS 停止。

节点属性支持

原生投影只能从 Neo4j 数据库投影有限的节点属性类型集。节点属性页面详细说明了支持哪些节点属性类型。其他类型的节点属性必须转换为或编码为其中一种受支持的类型,才能使用原生投影进行投影。

语法

CALL gds.graph.project(
  graphName: String,
  nodeProjection: String or List or Map,
  relationshipProjection: String or List or Map,
  configuration: Map
) YIELD
  graphName: String,
  nodeProjection: Map,
  nodeCount: Integer,
  relationshipProjection: Map,
  relationshipCount: Integer,
  projectMillis: Integer
表 1. 参数
名称 类型 可选 描述

graphName

字符串

图在目录中存储的名称。

nodeProjection

字符串、列表或映射

一个或多个节点投影

relationshipProjection

字符串、列表或映射

一个或多个关系投影

configuration

映射

配置原生投影的附加参数

表 2. 配置
名称 类型 默认 可选 描述

readConcurrency

整数

4

用于创建图的并发线程数。

nodeProperties

字符串、列表或映射

{}

从与 nodeProjection 中指定的任何标签匹配的节点加载的节点属性。

relationshipProperties

字符串、列表或映射

{}

从与 relationshipProjection 中指定的任何类型匹配的关系加载的关系属性。

validateRelationships

布尔值

如果 relationshipProjection 包含节点投影中不包含的节点之间的关系,是否抛出错误。

jobId

字符串

内部生成

可用于更轻松地跟踪投影进度的 ID。

表 3. 结果
名称 类型 描述

graphName

字符串

图在目录中存储的名称。

nodeProjection

映射

用于投影图的节点投影

nodeCount

整数

投影图中存储的节点数量。

relationshipProjection

映射

用于投影图的关系投影

relationshipCount

整数

投影图中存储的关系数量。

projectMillis

整数

投影图所需的毫秒数。

节点投影

节点投影可以使用以下任何形式指定:

  • 单个字符串(Neo4j 节点标签 <label> 或通配符 *

  • Neo4j 节点标签列表 ([<label_1>, <label_2>, <label_3>])

  • 一个投影映射,其中每个键是投影图中的节点标签,每个值本身是一个映射

投影映射的指定方式如下:

{
    <projected_label_1>: {                       (1)
        label: <label_1>,                        (2)
        properties: <prop_1>                     (3)
    },
    <projected_label_2>: {
        label: <label_2>,
        properties: [<prop_1>, <prop_2>, ...]    (3)
    },
    <projected_label_3>: {
        label: <label_3>,
        properties: {                            (3)
            <projected_prop_1>: {                (4)
                property: <prop_1>,              (5)
                defaultValue: <default_1>        (6)
            },
            <projected_prop_2>: {
                property: <prop_2>,
                defaultValue: <default_2>
            },
            ...
        }
    },
    ...
}
1 要在投影图中创建的节点标签。它可以与对应的 Neo4j 节点标签相同。
2 作为字符串的源 Neo4j 节点标签。默认值:与投影标签相同(此处为 <projected_label_1>)。
3 节点属性投影,指定为单个 Neo4j 节点属性、Neo4j 节点属性列表或投影映射。默认值:空映射。
4 要在投影图中创建的节点属性。它可以与对应的 Neo4j 节点属性相同。
5 作为字符串的源 Neo4j 节点属性。默认值:与投影属性名称相同(此处为 <projected_prop_1>)。
6 如果属性未为节点定义,则为默认值。默认值:取决于属性类型的回退值

注意

  • 当指定为字符串或列表时,投影不包含任何节点属性。

  • 通配符形式不保留投影节点上的标签。节点标签对于完全支持异构节点的算法很有用。包含所有节点标签的图示例展示了如何为这些情况保留所有标签。

  • 所有具有指定节点标签中任何一个的节点都将投影到 GDS 图中。

  • 所有指定的节点标签和属性都必须存在于数据库中。您可以使用 db.createProperty() 过程创建新的节点属性,而无需修改数据库。

关系投影

关系投影可以使用以下任何形式指定:

  • 单个字符串(Neo4j 关系类型 <type> 或通配符 *

  • Neo4j 关系类型列表 ([<type_1>, <type_2>, <type_3>])

  • 一个投影映射,其中每个键是投影图中的关系类型,每个值本身是一个映射

投影映射的指定方式如下:

{
    <projected_type_1>: {                        (1)
        type: <type_1>,                          (2)
        orientation: <orientation_1>,            (3)
        aggregation: <aggregation_1>,            (4)
        properties: <prop_1>                     (5)
    },
    <projected_type_2>: {
        type: <type_2>,
        orientation: <orientation_2>,
        aggregation: <aggregation_2>,
        properties: [<prop_1>, <prop_2>, ...]    (5)
    },
    <projected_type_3>: {
        type: <type_3>,
        orientation: <orientation_3>,
        aggregation: <aggregation_3>,
        properties: {                            (5)
            <projected_prop_1>: {                (6)
                property: <prop_1>,              (7)
                defaultValue: <default_1>,       (8)
                aggregation: <aggregation_1>     (9)
            },
            <projected_prop_2>: {
                property: <prop_2>,
                defaultValue: <default_2>,
                aggregation: <aggregation_2>
            },
            ...
        }
    },
    ...
}
1 要在投影图中创建的关系类型。它可以与对应的 Neo4j 关系类型相同。
2 作为字符串的源 Neo4j 关系类型。默认值:与投影类型相同(此处为 <projected_type_1>)。
3 投影图中的关系方向。允许值:NATURAL(默认,与 Neo4j 图中的方向相同),UNDIRECTED(使所有关系无向),REVERSE(反转所有关系的方向)。
4 处理与关系关联的所有关系属性的多个实例。允许值:NONE(默认)、SINGLECOUNTMINMAXSUM
5 关系属性投影,指定为单个 Neo4j 关系属性、Neo4j 关系属性列表或投影映射。默认值:空映射。
6 要在投影图中创建的关系属性。它可以与对应的 Neo4j 关系属性相同。
7 作为字符串的源 Neo4j 关系属性。默认值:与投影属性相同(此处为 <projected_prop_1>)。
8 如果属性未为关系定义,则为默认值。默认值:Double.NaN
9 处理与关系关联的特定关系属性的多个实例。允许值:NONE(默认)、SINGLECOUNTMINMAXSUM

注意

  • 当指定为字符串或列表时,投影不包含任何关系属性。

  • 通配符形式不保留投影关系上的类型。关系类型对于完全支持异构关系的算法很有用。包含所有节点标签的图示例展示了如何为这些情况保留所有关系。

  • 所有具有指定关系类型中任何一种且其端点节点包含在节点投影中的关系都将投影到 GDS 图中。validateRelationships 配置参数控制是失败还是静默丢弃其端点节点未包含在节点投影中的关系。

  • 所有指定的关系类型和属性都必须存在于数据库中。您可以使用 db.createProperty() 过程创建新的关系属性,而无需修改数据库。

示例

以下所有示例都应在空数据库中运行。

为了演示 GDS 图投影功能,我们将在 Neo4j 中创建一个小型社交网络图。示例图如下所示:

Visualization of the example graph
以下 Cypher 语句将在 Neo4j 数据库中创建示例图:
CREATE
  (florentin:Person { name: 'Florentin', age: 16 }),
  (adam:Person { name: 'Adam', age: 18 }),
  (veselin:Person { name: 'Veselin', age: 20, ratings: [5.0] }),
  (hobbit:Book { name: 'The Hobbit', isbn: 1234, numberOfPages: 310, ratings: [1.0, 2.0, 3.0, 4.5] }),
  (frankenstein:Book { name: 'Frankenstein', isbn: 4242, price: 19.99 }),

  (florentin)-[:KNOWS { since: 2010 }]->(adam),
  (florentin)-[:KNOWS { since: 2018 }]->(veselin),
  (florentin)-[:READ { numberOfPages: 4 }]->(hobbit),
  (florentin)-[:READ { numberOfPages: 42 }]->(hobbit),
  (adam)-[:READ { numberOfPages: 30 }]->(hobbit),
  (veselin)-[:READ]->(frankenstein)

简单图

简单图是只有一种节点标签和关系类型(即单向图)的图。我们将从演示如何通过仅投影 Person 节点标签和 KNOWS 关系类型来加载简单图开始。

投影 Person 节点和 KNOWS 关系
CALL gds.graph.project(
  'persons',            (1)
  'Person',             (2)
  'KNOWS'               (3)
)
YIELD
  graphName AS graph, nodeProjection, nodeCount AS nodes, relationshipProjection, relationshipCount AS rels
1 图的名称。之后,persons 可用于运行算法或管理图。
2 要投影的节点。在此示例中,是带有 Person 标签的节点。
3 要投影的关系。在此示例中,是类型为 KNOWS 的关系。
表 4. 结果
nodeProjection 节点 relationshipProjection 关系

"persons"

{Person={label="Person", properties={}}}

3

{KNOWS={aggregation="DEFAULT", indexInverse=false, orientation="NATURAL", properties={}, type="KNOWS"}}

2

在上面的示例中,我们使用了节点和关系投影的简写语法。所使用的投影在内部扩展为完整的 Map 语法,如 Results 表中所示。此外,我们可以看到投影的内存图包含三个 Person 节点和两个 KNOWS 关系。

多图

多图是具有多个节点标签和关系类型的图。

要投影多个节点标签和关系类型,我们可以按如下方式调整投影:

投影 PersonBook 节点以及 KNOWSREAD 关系
CALL gds.graph.project(
  'personsAndBooks',    (1)
  ['Person', 'Book'],   (2)
  ['KNOWS', 'READ']     (3)
)
YIELD
  graphName AS graph, nodeProjection, nodeCount AS nodes, relationshipCount AS rels
1 personsAndBooks 为名称投影图。
2 要投影的节点。在此示例中,是带有 PersonBook 标签的节点。
3 要投影的关系。在此示例中,是类型为 KNOWSREAD 的关系。
表 5. 结果
nodeProjection 节点 关系

"personsAndBooks"

{Book={label="Book", properties={}}, Person={label="Person", properties={}}}

5

6

在上面的示例中,我们使用了节点和关系投影的简写语法。所使用的投影在内部扩展为完整的 Map 语法,如 Results 表中的 nodeProjection 所示。此外,我们可以看到投影的内存图包含五个节点和两个关系。

关系方向

默认情况下,关系以与 Neo4j 数据库中存储的相同方向加载。在 GDS 中,我们称之为 NATURAL 方向。此外,我们提供了以 REVERSE 甚至 UNDIRECTED 方向加载关系的功能。

投影 Person 节点和无向 KNOWS 关系
CALL gds.graph.project(
  'undirectedKnows',                    (1)
  'Person',                             (2)
  {KNOWS: {orientation: 'UNDIRECTED'}}  (3)
)
YIELD
  graphName AS graph,
  relationshipProjection AS knowsProjection,
  nodeCount AS nodes,
  relationshipCount AS rels
1 undirectedKnows 为名称投影图。
2 要投影的节点。在此示例中,是带有 Person 标签的节点。
3 投影类型为 KNOWS 的关系,并使用 orientation 参数指定它们应为 UNDIRECTED
表 6. 结果
knowsProjection 节点 关系

"undirectedKnows"

{KNOWS={aggregation="DEFAULT", indexInverse=false, orientation="UNDIRECTED", properties={}, type="KNOWS"}}

3

4

要指定方向,我们需要使用扩展的 Map 语法编写 relationshipProjection。投影 UNDIRECTEDKNOWS 关系会以两个方向加载每个关系。因此,undirectedKnows 图包含四个关系,是简单图persons 图的两倍。

节点属性

要投影节点属性,我们可以使用 nodeProperties 配置参数来共享属性,或为特定标签扩展单个 nodeProjection

投影 PersonBook 节点以及 KNOWSREAD 关系
CALL gds.graph.project(
  'graphWithProperties',                                (1)
  {                                                     (2)
    Person: {properties: 'age'},                        (3)
    Book: {properties: {price: {defaultValue: 5.0}}}    (4)
  },
  ['KNOWS', 'READ'],                                    (5)
  {nodeProperties: 'ratings'}                           (6)
)
YIELD
  graphName, nodeProjection, nodeCount AS nodes, relationshipCount AS rels
RETURN graphName, nodeProjection.Book AS bookProjection, nodes, rels
1 graphWithProperties 为名称投影图。
2 使用扩展的节点投影语法。
3 投影带有 Person 标签的节点及其 age 属性。
4 投影带有 Book 标签的节点及其 price 属性。每个没有 price 属性的 Book 都将获得 5.0defaultValue
5 要投影的关系。在此示例中,是类型为 KNOWSREAD 的关系。
6 全局配置,在每个指定标签上投影节点属性 rating
表 7. 结果
graphName bookProjection 节点 关系

"graphWithProperties"

{label="Book", properties={price={defaultValue=5.0, property="price"}, ratings={defaultValue=null, property="ratings"}}}

5

6

投影的 graphWithProperties 图包含五个节点和六个关系。在返回的 bookProjection 中,我们可以观察到 Book 的节点属性 priceratings 已加载。

GDS 目前仅支持加载数值属性。

此外,price 属性的默认值为 5.0。示例图中并非所有书籍都指定了价格。下面我们将检查价格是否正确投影:

验证投影图中 Adam 的 ratings 属性
MATCH (n:Book)
RETURN n.name AS name, gds.util.nodeProperty('graphWithProperties', n, 'price') as price
ORDER BY price
表 8. 结果
名称 价格

"The Hobbit"

5.0

"Frankenstein"

19.99

我们可以看到,价格已投影,Hobbit 的默认价格为 5.0。

关系属性

与节点属性类似,我们可以使用 relationshipProperties 配置参数或为特定类型扩展单个 relationshipProjection

投影 PersonBook 节点以及带有 numberOfPages 属性的 READ 关系
CALL gds.graph.project(
  'readWithProperties',                     (1)
  ['Person', 'Book'],                       (2)
  {                                         (3)
    READ: { properties: "numberOfPages" }   (4)
  }
)
YIELD
  graphName AS graph,
  relationshipProjection AS readProjection,
  nodeCount AS nodes,
  relationshipCount AS rels
1 readWithProperties 为名称投影图。
2 要投影的节点。在此示例中,是带有 PersonBook 标签的节点。
3 使用扩展的关系投影语法。
4 投影类型为 READ 的关系及其 numberOfPages 属性。
表 9. 结果
readProjection 节点 关系

"readWithProperties"

{READ={aggregation="DEFAULT", indexInverse=false, orientation="NATURAL", properties={numberOfPages={aggregation="DEFAULT", defaultValue=null, property="numberOfPages"}}, type="READ"}}

5

4

接下来,我们将验证关系属性 numberOfPages 是否已正确加载。

流式传输投影图的关系属性 numberOfPages
CALL gds.graph.relationshipProperty.stream('readWithProperties', 'numberOfPages')
YIELD sourceNodeId, targetNodeId, propertyValue AS numberOfPages
RETURN
  gds.util.asNode(sourceNodeId).name AS person,
  gds.util.asNode(targetNodeId).name AS book,
  numberOfPages
ORDER BY person ASC, numberOfPages DESC
表 10. 结果
人物 书籍 页数

"Adam"

"The Hobbit"

30.0

"Florentin"

"The Hobbit"

42.0

"Florentin"

"The Hobbit"

4.0

"Veselin"

"Frankenstein"

NaN

我们可以看到,numberOfPages 属性已加载。默认属性值为 Double.NaN,可以使用 Map 语法进行更改,与节点属性中的方式相同。

并行关系

Neo4j 支持并行关系,即两个节点之间存在多个关系。默认情况下,GDS 保留并行关系。对于某些算法,我们希望投影图中两个节点之间最多只包含一个关系。

我们可以通过关系投影中的 aggregation 参数指定如何将并行关系聚合成单个关系。

对于没有关系属性的图,我们可以使用 COUNT 聚合。如果不需要计数,可以使用 SINGLE 聚合。

投影 PersonBook 节点以及 COUNT 聚合的 READ 关系
CALL gds.graph.project(
  'readCount',                      (1)
  ['Person', 'Book'],               (2)
  {
    READ: {                         (3)
      properties: {
        numberOfReads: {            (4)
          property: '*',            (5)
          aggregation: 'COUNT'      (6)
        }
      }
    }
  }
)
YIELD
  graphName AS graph,
  relationshipProjection AS readProjection,
  nodeCount AS nodes,
  relationshipCount AS rels
1 readCount 为名称投影图。
2 要投影的节点。在此示例中,是带有 PersonBook 标签的节点。
3 投影类型为 READ 的关系。
4 投影关系属性 numberOfReads
5 一个占位符,表示关系属性的值是派生的,而不是基于 Neo4j 属性。
6 聚合类型。在此示例中,COUNT 导致属性值是并行关系的数目。
表 11. 结果
readProjection 节点 关系

"readCount"

{READ={aggregation="DEFAULT", indexInverse=false, orientation="NATURAL", properties={numberOfReads={aggregation="COUNT", defaultValue=null, property="*"}}, type="READ"}}

5

3

接下来,我们将验证 READ 关系是否已正确聚合。

流式传输投影图的关系属性 numberOfReads
CALL gds.graph.relationshipProperty.stream('readCount', 'numberOfReads')
YIELD sourceNodeId, targetNodeId, propertyValue AS numberOfReads
RETURN
  gds.util.asNode(sourceNodeId).name AS person,
  gds.util.asNode(targetNodeId).name AS book,
  numberOfReads
ORDER BY numberOfReads DESC, person
表 12. 结果
人物 书籍 读取次数

"Florentin"

"The Hobbit"

2.0

"Adam"

"The Hobbit"

1.0

"Veselin"

"Frankenstein"

1.0

我们可以看到,Florentin 和 Hobbit 之间的两个 READ 关系导致 numberOfReads2

带属性的并行关系

对于带关系属性的图,我们还可以使用其他聚合。

投影 PersonBook 节点以及通过求和 numberOfPages 聚合的 READ 关系
CALL gds.graph.project(
  'readSums',                                                   (1)
  ['Person', 'Book'],                                           (2)
  {READ: {properties: {numberOfPages: {aggregation: 'SUM'}}}}   (3)
)
YIELD
  graphName AS graph,
  relationshipProjection AS readProjection,
  nodeCount AS nodes,
  relationshipCount AS rels
1 readSums 为名称投影图。
2 要投影的节点。在此示例中,是带有 PersonBook 标签的节点。
3 投影类型为 READ 的关系。聚合类型 SUM 会导致投影的 numberOfPages 属性的值为并行关系的 numberOfPages 属性之和。
表 13. 结果
readProjection 节点 关系

"readSums"

{READ={aggregation="DEFAULT", indexInverse=false, orientation="NATURAL", properties={numberOfPages={aggregation="SUM", defaultValue=null, property="numberOfPages"}}, type="READ"}}

5

3

接下来,我们将验证关系属性 numberOfPages 是否已正确聚合。

流式传输投影图的关系属性 numberOfPages
CALL gds.graph.relationshipProperty.stream('readSums', 'numberOfPages')
YIELD
  sourceNodeId, targetNodeId, propertyValue AS numberOfPages
RETURN
  gds.util.asNode(sourceNodeId).name AS person,
  gds.util.asNode(targetNodeId).name AS book,
  numberOfPages
ORDER BY numberOfPages DESC, person
表 14. 结果
人物 书籍 页数

"Florentin"

"The Hobbit"

46.0

"Adam"

"The Hobbit"

30.0

"Veselin"

"Frankenstein"

0.0

我们可以看到,Florentin 和 Hobbit 之间的两个 READ 关系总共有 46 页。

验证关系标志

语法部分所述,validateRelationships 标志控制是否在尝试投影源或目标节点未存在于节点投影中的关系时抛出错误。请注意,即使该标志设置为 false,此类关系仍不会被投影,但加载过程不会中止。

我们可以通过 Neo4j 数据库中存在的图 来模拟这种情况:

投影 READKNOWS 关系,但只投影 Person 节点,并将 validateRelationships 设置为 true
CALL gds.graph.project(
  'danglingRelationships',
  'Person',
  ['READ', 'KNOWS'],
  {
    validateRelationships: true
  }
)
YIELD
  graphName AS graph,
  relationshipProjection AS readProjection,
  nodeCount AS nodes,
  relationshipCount AS rels
结果
org.neo4j.graphdb.QueryExecutionException: Failed to invoke procedure `gds.graph.project`: Caused by: java.lang.IllegalArgumentException: Failed to load a relationship because its target-node with id 3 is not part of the node query or projection. To ignore the relationship, set the configuration parameter `validateRelationships` to false.

我们可以看到上面的查询导致抛出异常。异常消息将提供有关缺失特定节点 ID 的信息,这将有助于调试底层问题。

包含所有节点标签的图

您可以使用通配符运算符 * 选择要投影的所有节点标签。但是,这不会保留投影节点上的标签信息。

请改用 db.labels() 检索所有标签列表,并将其用作 nodeProjection 参数的值,如下例所示:

投影所有标签
CALL db.labels() YIELD label
WITH collect(label) AS allLabels
CALL gds.graph.project(
  'allLabelsGraph',
  allLabels,
  ['KNOWS', 'READ']
)
YIELD graphName, nodeProjection, nodeCount AS nodes, relationshipCount AS rels
RETURN *
表 15. 结果
所有标签 graphName nodeProjection 节点 关系

["Person", "Book"]

"allLabelsGraph"

{Book={label="Book", properties={}}, Person={label="Person", properties={}}}

5

6

以类似的方式,您可以通过使用 db.relationshipTypes() 代替 ['KNOWS', 'READ'] 作为 relationshipProjection 参数的值来选择要投影的所有关系类型。

© . All rights reserved.