模式优化

本页中的所有示例均假设 SparkSession 已使用适当的身份验证选项进行了初始化。有关更多详细信息,请参阅 快速入门示例

虽然 Neo4j 不强制要求使用模式(Schema),但在写入数据之前添加索引和约束可以提高写入过程的效率。在更新节点或关系时,应用约束也是避免重复数据的最佳方式。

下面总结了用于模式优化的连接器选项。

此处描述的模式优化选项不能与 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 模式中的类型和非空性约束。

逗号分隔的 TYPE, EXISTS, NONE 列表

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