架构优化

此页面中的所有示例都假设 SparkSession 已使用适当的身份验证选项初始化。有关更多详细信息,请参见 快速入门示例

虽然 Neo4j 不强制使用架构,但在写入数据之前添加索引和约束可以提高写入效率。在更新节点或关系时,存在约束也是避免重复的最佳方法。

架构优化选项总结如下。

此处描述的架构优化选项不能与 query 选项一起使用。如果您使用的是 自定义 Cypher® 查询,则需要使用 script 选项 手动创建索引和约束。

表 1. 架构优化选项
选项 描述 默认值

schema.optimization.type 已在 5.3 版中弃用

使用 node.keys 选项定义的属性创建节点上的 索引属性唯一性 约束。

(已弃用,推荐使用 schema.optimization.node.keysschema.optimization.relationship.keysschema.optimization)

NONEINDEXNODE_CONSTRAINTS 之一

NONE

schema.optimization.node.keys 5.3 版新增

使用 node.keys 选项定义的属性创建节点上的 属性唯一性 约束。

UNIQUEKEYNONE 之一

NONE

schema.optimization.relationship.keys 5.3 版新增

使用 relationship.keys 选项定义的属性创建关系上的 属性唯一性 约束。

UNIQUEKEYNONE 之一

NONE

schema.optimization 5.3 版新增

创建节点和关系上的 属性类型和属性存在 约束,强制执行 DataFrame 架构中的类型和非空性。

用逗号分隔的 TYPEEXISTSNONE 列表

NONE

节点属性上的索引

Neo4j 中的 索引 通常用于提高搜索性能。

您可以通过将 schema.optimization.type 选项设置为 INDEX 来创建索引。

示例
val df = List(
  "Product 1",
  "Product 2",
).toDF("name")

df.write
  .format("org.neo4j.spark.DataSource")
  .mode(SaveMode.Overwrite)
  .option("labels", ":Product")
  .option("node.keys", "name")
  .option("schema.optimization.type", "INDEX")
  .save()
架构查询

在写入过程开始之前,连接器会运行以下架构查询

CREATE INDEX spark_INDEX_Product_name FOR (n:Product) ON (n.name)

索引名称的格式为 spark_INDEX_<LABEL>_<NODE_KEYS>,其中 <LABEL>labels 选项中的第一个标签,<NODE_KEYS>node.keys 选项中指定的单个或多个属性的用连字符分隔的序列。

备注

  • 如果索引已存在,则不会重新创建它。

  • 对于多个标签,仅使用第一个标签来创建索引。

节点属性唯一性约束

节点属性唯一性约束 确保对所有具有特定标签的节点,属性值是唯一的。对于多个属性上的属性唯一性约束,属性值的组合是唯一的。

您可以通过将 schema.optimization.node.keys 选项设置为 UNIQUE 来创建约束。

示例
val df = List(
  "Product 1",
  "Product 2",
).toDF("name")

df.write
  .format("org.neo4j.spark.DataSource")
  .mode(SaveMode.Overwrite)
  .option("labels", ":Product")
  .option("node.keys", "name")
  .option("schema.optimization.node.keys", "UNIQUE")
  .save()
架构查询

在写入过程开始之前,连接器会运行以下架构查询

CREATE CONSTRAINT `spark_NODE_UNIQUE-CONSTRAINT_Product_name` IF NOT EXISTS FOR (e:Product) REQUIRE (e.name) IS UNIQUE

备注

  • 如果约束已存在,则不会重新创建它。

  • 对于多个标签,仅使用第一个标签来创建索引。

  • 如果对同一属性已存在键约束,则无法对节点属性创建唯一性约束。

  • 此架构优化仅适用于 Overwrite 保存模式。

5.3.0 版之前,可以使用 schema.optimization.type 选项设置为 NODE_CONSTRAINTS 来添加节点属性唯一性约束。

节点键约束

节点键约束 确保,对于给定的节点标签和属性集

  • 所有属性都存在于具有该标签的所有节点上。

  • 属性值的组合是唯一的。

您可以通过将 schema.optimization.node.keys 选项设置为 KEY 来创建约束。

示例
val df = List(
  "Product 1",
  "Product 2",
).toDF("name")

df.write
  .format("org.neo4j.spark.DataSource")
  .mode(SaveMode.Overwrite)
  .option("labels", ":Product")
  .option("node.keys", "name")
  .option("schema.optimization.node.keys", "KEY")
  .save()
架构查询

在写入过程开始之前,连接器会运行以下架构查询

CREATE CONSTRAINT `spark_NODE_KEY-CONSTRAINT_Product_name` IF NOT EXISTS FOR (e:Product) REQUIRE (e.name) IS NODE KEY

备注

  • 如果约束已存在,则不会重新创建它。

  • 如果对同一属性已存在唯一性约束,则无法对节点属性创建键约束。

  • 此架构优化仅适用于 Overwrite 保存模式。

关系属性唯一性约束

关系属性唯一性约束 确保对所有具有特定类型的关系,属性值是唯一的。对于多个属性上的属性唯一性约束,属性值的组合是唯一的。

您可以通过将 schema.optimization.relationship.keys 选项设置为 UNIQUE 来创建约束。

示例
val df = Seq(
  ("John", "Doe", 1, "Product 1", 200, "ABC100"),
  ("Jane", "Doe", 2, "Product 2", 100, "ABC200")
).toDF("name", "surname", "customerID", "product", "quantity", "order")

df.write
  .mode(SaveMode.Overwrite)
  .format("org.neo4j.spark.DataSource")
  .option("relationship", "BOUGHT")
  .option("relationship.save.strategy", "keys")
  .option("relationship.source.save.mode", "Overwrite")
  .option("relationship.source.labels", ":Customer")
  .option("relationship.source.node.properties", "name,surname,customerID:id")
  .option("relationship.source.node.keys", "customerID:id")
  .option("relationship.target.save.mode", "Overwrite")
  .option("relationship.target.labels", ":Product")
  .option("relationship.target.node.properties", "product:name")
  .option("relationship.target.node.keys", "product:name")
  .option("relationship.properties", "quantity,order")
  .option("schema.optimization.relationship.keys", "UNIQUE")
  .option("relationship.keys", "order")
  .save()
架构查询

在写入过程开始之前,连接器会运行以下架构查询

CREATE CONSTRAINT `spark_RELATIONSHIP_UNIQUE-CONSTRAINT_BOUGHT_order` IF NOT EXISTS FOR ()-[e:BOUGHT]->() REQUIRE (e.order) IS UNIQUE

备注

  • 如果约束已存在,则不会重新创建它。

  • 如果对同一属性已存在键约束,则无法对关系属性创建唯一性约束。

  • 此架构优化仅适用于 Overwrite 保存模式。

关系键约束

关系键约束 确保,对于给定的关系类型和属性集

  • 所有属性都存在于具有该类型的 所有关系上。

  • 属性值的组合是唯一的。

您可以通过将 schema.optimization.relationship.keys 选项设置为 KEY 来创建约束。

示例
val df = Seq(
  ("John", "Doe", 1, "Product 1", 200, "ABC100"),
  ("Jane", "Doe", 2, "Product 2", 100, "ABC200")
).toDF("name", "surname", "customerID", "product", "quantity", "order")

df.write
  .mode(SaveMode.Overwrite)
  .format("org.neo4j.spark.DataSource")
  .option("relationship", "BOUGHT")
  .option("relationship.save.strategy", "keys")
  .option("relationship.source.save.mode", "Overwrite")
  .option("relationship.source.labels", ":Customer")
  .option("relationship.source.node.properties", "name,surname,customerID:id")
  .option("relationship.source.node.keys", "customerID:id")
  .option("relationship.target.save.mode", "Overwrite")
  .option("relationship.target.labels", ":Product")
  .option("relationship.target.node.properties", "product:name")
  .option("relationship.target.node.keys", "product:name")
  .option("relationship.properties", "quantity,order")
  .option("schema.optimization.relationship.keys", "KEY")
  .option("relationship.keys", "order")
  .save()
架构查询

在写入过程开始之前,连接器会运行以下架构查询

CREATE CONSTRAINT `spark_RELATIONSHIP_KEY-CONSTRAINT_BOUGHT_order` IF NOT EXISTS FOR ()-[e:BOUGHT]->() REQUIRE (e.order) IS RELATIONSHIP KEY

备注

  • 如果约束已存在,则不会重新创建它。

  • 如果对同一属性已存在唯一性约束,则无法对关系属性创建键约束。

  • 此架构优化仅适用于 Overwrite 保存模式。

属性类型和属性存在约束

属性类型约束确保属性具有所有具有特定标签的节点的所需属性类型 (节点属性类型约束) 或所有具有特定类型的关系的所需属性类型 (关系属性类型约束).

属性存在约束确保属性存在 (IS NOT NULL) 于所有具有特定标签的节点 (节点属性存在约束) 或所有具有特定类型的关系 (关系属性存在约束).

连接器使用 DataFrame 架构来强制执行类型(使用 数据类型映射 中描述的映射)以及每一列的 nullable 标志来确定是否强制执行存在。

您可以创建

  • 通过将 schema.optimization 选项设置为 TYPE 来为节点和关系创建属性类型约束。

  • 通过将 schema.optimization 选项设置为 EXISTS 来为节点和关系创建属性存在约束。

  • 通过将 schema.optimization 选项设置为 TYPE,EXISTS 来同时创建两者。

备注

  • 如果约束已存在,则不会重新创建它。

在节点上

示例
df.write
  .format("org.neo4j.spark.DataSource")
  .mode(SaveMode.Overwrite)
  .option("labels", ":Person")
  .option("node.keys", "surname")
  .option("schema.optimization", "TYPE,EXISTS")
  .save()
架构查询

在写入过程开始之前,连接器会运行以下架构查询(对 DataFrame 中的每一列运行一个查询)

CREATE CONSTRAINT `spark_NODE-TYPE-CONSTRAINT-Person-name` IF NOT EXISTS FOR (e:Person) REQUIRE e.name IS :: STRING

CREATE CONSTRAINT `spark_NODE-TYPE-CONSTRAINT-Person-surname` IF NOT EXISTS FOR (e:Person) REQUIRE e.surname IS :: STRING

CREATE CONSTRAINT `spark_NODE-TYPE-CONSTRAINT-Person-age` IF NOT EXISTS FOR (e:Person) REQUIRE e.age IS :: INTEGER

如果 DataFrame 中的某一列不可为空,则连接器会运行额外的架构查询。例如,如果 age 列不可为空,则连接器会运行以下架构查询

CREATE CONSTRAINT `spark_NODE-NOT_NULL-CONSTRAINT-Person-age` IF NOT EXISTS FOR (e:Person) REQUIRE e.age IS NOT NULL

在关系上

示例
val df = Seq(
  ("John", "Doe", 1, "Product 1", 200, "ABC100"),
  ("Jane", "Doe", 2, "Product 2", 100, "ABC200")
).toDF("name", "surname", "customerID", "product", "quantity", "order")

df.write
  .mode(SaveMode.Overwrite)
  .format("org.neo4j.spark.DataSource")
  .option("relationship", "BOUGHT")
  .option("relationship.save.strategy", "keys")
  .option("relationship.source.save.mode", "Overwrite")
  .option("relationship.source.labels", ":Customer")
  .option("relationship.source.node.properties", "name,surname,customerID:id")
  .option("relationship.source.node.keys", "customerID:id")
  .option("relationship.target.save.mode", "Overwrite")
  .option("relationship.target.labels", ":Product")
  .option("relationship.target.node.properties", "product:name")
  .option("relationship.target.node.keys", "product:name")
  .option("relationship.properties", "quantity,order")
  .option("schema.optimization", "TYPE,EXISTS")
  .save()
架构查询

在写入过程开始之前,连接器会运行以下架构查询(源节点和目标节点属性的属性类型约束查询,然后对表示关系属性的 DataFrame 中的每一列运行一个属性类型约束查询)

CREATE CONSTRAINT `spark_NODE-TYPE-CONSTRAINT-Customer-name` IF NOT EXISTS FOR (e:Customer) REQUIRE e.name IS :: STRING

CREATE CONSTRAINT `spark_NODE-TYPE-CONSTRAINT-Customer-surname` IF NOT EXISTS FOR (e:Customer) REQUIRE e.surname IS :: STRING

CREATE CONSTRAINT `spark_NODE-TYPE-CONSTRAINT-Customer-id` IF NOT EXISTS FOR (e:Customer) REQUIRE e.id IS :: INTEGER

CREATE CONSTRAINT `spark_NODE-TYPE-CONSTRAINT-Product-name` IF NOT EXISTS FOR (e:Product) REQUIRE e.name IS :: STRING

CREATE CONSTRAINT `spark_RELATIONSHIP-TYPE-CONSTRAINT-BOUGHT-quantity` IF NOT EXISTS FOR ()-[e:BOUGHT]->() REQUIRE e.quantity IS :: INTEGER

CREATE CONSTRAINT `spark_RELATIONSHIP-TYPE-CONSTRAINT-BOUGHT-order` IF NOT EXISTS FOR ()-[e:BOUGHT]->() REQUIRE e.order IS :: STRING

如果 DataFrame 中的某一列不可为空,则连接器会运行额外的架构查询。例如,如果 experience 列不可为空,则连接器会运行以下架构查询

CREATE CONSTRAINT `spark_NODE-NOT_NULL-CONSTRAINT-Customer-id` IF NOT EXISTS FOR (e:Customer) REQUIRE e.id IS NOT NULL

CREATE CONSTRAINT `spark_RELATIONSHIP-NOT_NULL-CONSTRAINT-BOUGHT-quantity` IF NOT EXISTS FOR ()-[e:BOUGHT]->() REQUIRE e.quantity IS NOT NULL