数据建模

将数据从 DataFrames 转换为图

当获取任何复杂的 DataFrames 集并准备将其加载到 Neo4j 中时,您有两种选择

  • 规范化加载

  • Cypher® 解构

本节将对这两种方法进行介绍,并提供从性能和复杂性角度来看的优势和潜在缺点的信息。

在可能的情况下,使用规范化加载方法以获得最佳性能和可维护性。

规范化加载

假设您希望将一个名为 purchases 的 DataFrame 加载到 Neo4j 中,内容如下

product_id,product,customer_id,customer,quantity
1,Socks,10,David,2
2,Pants,11,Andrea,1

此数据表示一个简单的 (:Customer)-[:BOUGHT]→(:Product) 图模型。

规范化加载方法要求您创建多个不同的 DataFrame:您的目标图中的每个节点标签和关系类型对应一个 DataFrame。例如,在这种情况下,您可以创建三个 DataFrame

  • val products = spark.sql("SELECT product_id, product FROM purchases")

  • val customers = spark.sql("SELECT customer_id, customer FROM purchases")

  • val bought = spark.sql("SELECT product_id, customer_id, quantity FROM purchases")

一旦这些简单的 DataFrame 表示了“标签表”的规范化视图(即,每个节点标签或关系类型对应一个 DataFrame/表),那么连接器提供的用于写入节点和关系的现有实用程序就可以使用,而无需额外的 Cypher。此外,如果这些框架通过标识符变得唯一,那么数据就已经为最大并行性做好了准备。(请参阅以下章节中的并行性说明。)

优势

  • 规范化加载方法将大部分数据转换工作转移到 Spark 本身(包括数据拆分、去重、分区)。如果可能,任何数据转换/清理工作都应在 Spark 中完成。

  • 这种方法使得代码易于理解;最终,将每个 DataFrame 写入 Neo4j 的过程相当简单,主要只需要一个标签和一个键。

  • 这允许并行性(将在以下章节中讨论)。

缺点

  • 在将数据加载到 Neo4j 之前,您需要进行更多的 SQL 工作。

  • 这种方法要求在开始之前识别图模式,而不是将数据加载到 Neo4j 中再使用 Cypher 进行操作。

Cypher 解构

Cypher 解构是使用单个 Cypher 语句将复杂记录处理成完整图模式的过程。让我们再次看看数据示例

product_id,product,customer_id,customer,quantity
1,Socks,10,David,2
2,Pants,11,Andrea,1

要将此数据存储在 Neo4j 中,您可以使用如下 Cypher 查询

MERGE (p:Product { id: event.product_id })
  ON CREATE SET p.name = event.product
WITH p
MERGE (c:Customer { id: event.customer_id })
  ON CREATE SET c.name = event.customer
MERGE (c)-[:BOUGHT { quantity: event.quantity }]->(p);

在这种情况下,整个工作可以通过单个 Cypher 语句完成。随着 DataFrames 变得复杂,Cypher 语句也会变得相当复杂。

优势

  • 极其灵活:您可以执行 Cypher 提供的任何操作。

  • 如果您是 Neo4j 专家,则可以轻松上手。

缺点

  • Cypher 解构方法倾向于将转换工作转移到 Neo4j,这不是一个好主意,因为它不具备像 Spark 那样支持此功能的基础设施。

  • 这种方法倾向于产生繁重的锁定行为,这会影响并行性并可能降低性能。

  • 它鼓励您将模式信息嵌入到 Cypher 查询中,而不是使用 Spark 实用程序。

将数据从图转换回 DataFrames

通常,始终使用显式 RETURN 语句并解构您的结果。

一种常见模式是编写复杂的 Cypher 语句(可能遍历许多关系)以将数据集返回到 Spark。由于 Spark 不理解图原语,因此在 Spark 中表示原始节点、关系或路径的方法并不多。因此,强烈建议不要将这些类型从 Cypher 返回到 Spark,而是专注于具体属性值和函数结果,您可以在 Spark 中将其表示为简单类型。

例如,以下 Cypher 查询会产生一个难以操作的笨拙 DataFrame

MATCH path=(p:Person { name: "Andrea" })-[r:KNOWS*]->(o:Person)
RETURN path;

一个生成更清晰 DataFrame 的更好 Cypher 查询如下

MATCH path=(p:Person { name: "Andrea" })-[r:KNOWS*]->(o:Person)
RETURN length(path) as pathLength, p.name as p1Name, o.name as p2Name
© . All rights reserved.