数据类型映射
Neo4j 和 Cypher® 提供了一个类型系统,用于描述值在数据库中的存储方式,但这些类型并不总是与 Spark 提供的类型完全匹配。
在某些情况下,Neo4j 提供的数据类型在 Spark 中没有对应项,反之亦然。
数据类型映射
| Neo4j 类型 | Spark 类型 | 说明 |
|---|---|---|
|
|
示例: |
|
|
示例: |
|
|
示例: |
|
|
5.4.0 新增 示例: |
|
|
示例: |
|
|
有关 Neo4j 空间类型的更多信息,请参阅空间值 (Spatial values) |
|
|
示例: |
|
|
示例: |
|
|
示例: |
|
|
5.4.0 新增 示例: |
|
|
5.4.0 起已弃用 将 |
|
|
5.4.0 新增 示例: |
|
|
5.4.0 起已弃用 将 |
|
|
|
|
|
5.4.0 新增 示例: |
|
|
5.4.0 起已弃用 将 |
|
Spark SQL |
5.4.0 新增 示例: |
|
Spark SQL |
5.4.0 起已弃用 将 |
|
|
Neo4j 中的节点表示为属性容器;也就是说,它们显示为带有属性的结构体,这些属性对应于节点中的任何属性。为了易用性,通常最好从查询中返回各个属性,而不是返回整个节点。 |
|
|
关系以 Map 形式返回,标识关系的源节点和目标节点、其类型以及关系的属性(如果有)。为了易用性,通常最好从查询中返回各个属性,而不是返回整个关系。 |
|
|
示例: |
|
|
5.4.0 新增 二进制写入被专门视为 Neo4j 的 |
|
|
5.4.0 起已弃用 将 |
|
|
在 Neo4j 中,数组必须具有一致的类型(例如,数组只能包含 |
复杂数据类型
| Neo4j 类型 | Spark Struct |
|---|---|
Duration |
Struct(Array(
("type", DataTypes.StringType, false),
("months", DataTypes.LongType, false),
("days", DataTypes.LongType, false),
("seconds", DataTypes.LongType, false),
("nanoseconds", DataTypes.IntegerType, false),
("value", DataTypes.StringType, false)
))
|
Point |
Struct(Array(
("type", DataTypes.StringType, false),
("srid", DataTypes.IntegerType, false),
("x", DataTypes.DoubleType, false),
("y", DataTypes.DoubleType, false),
("z", DataTypes.DoubleType, true),
))
|
Time |
Struct(Array(
("type", DataTypes.StringType, false),
("value", DataTypes.StringType, false)
))
|
Map 类型
当列为 Map 时,连接器会尝试将其展平。例如,考虑以下数据集
| id | 名称 (name) | lives_in |
|---|---|---|
1 |
Andrea Santurbano |
{address: 'Times Square, 1', city: 'NY', state: 'NY'} |
2 |
Davide Fantuzzi |
{address: 'Statue of Liberty, 10', city: 'NY', state: 'NY'} |
连接器将 lives_in 列展平为三列:lives_in.address, lives_in.city 和 lives_in.state
| id | 名称 (name) | lives_in.address | lives_in.city | lives_in.state |
|---|---|---|---|---|
1 |
Andrea Santurbano |
Times Square, 1 |
NY |
NY |
2 |
Davide Fantuzzi |
Statue of Liberty, 10 |
NY |
NY |
当 DataFrame 的一列是 Map 时,我们在内部执行的操作是展平该 Map,因为 Neo4j 不支持这种图实体属性类型;因此,对于像这样的 Spark 作业:
val data = Seq(
("Foo", 1, Map("inner" -> Map("key" -> "innerValue"))),
("Bar", 2, Map("inner" -> Map("key" -> "innerValue1"))),
).toDF("id", "time", "table")
data.write
.mode(SaveMode.Append)
.format(classOf[DataSource].getName)
.option("url", SparkConnectorScalaSuiteIT.server.getBoltUrl)
.option("labels", ":MyNodeWithFlattenedMap")
.save()
在 Neo4j 中,对于标签为 MyNodeWithFlattenedMap 的节点,您将发现此信息被存储为:
MyNodeWithFlattenedMap {
id: 'Foo',
time: 1,
`table.inner.key`: 'innerValue'
}
MyNodeWithFlattenedMap {
id: 'Bar',
time: 1,
`table.inner.key`: 'innerValue1'
}
现在,您可能会遇到如下棘手情况:
val data = Seq(
("Foo", 1, Map("key.inner" -> Map("key" -> "innerValue"), "key" -> Map("inner.key" -> "value"))),
("Bar", 1, Map("key.inner" -> Map("key" -> "innerValue1"), "key" -> Map("inner.key" -> "value1"))),
).toDF("id", "time", "table")
data.write
.mode(SaveMode.Append)
.format(classOf[DataSource].getName)
.option("url", SparkConnectorScalaSuiteIT.server.getBoltUrl)
.option("labels", ":MyNodeWithFlattenedMap")
.save()
由于产生的展平键是重复的,Neo4j Spark 将以非确定性的方式选取关联值中的一个。
因为我们将要存入 Neo4j 的信息将是这样(注意,顺序无法保证):
MyNodeWithFlattenedMap {
id: 'Foo',
time: 1,
`table.key.inner.key`: 'innerValue' // but it could be `value` as the order is not guaranteed
}
MyNodeWithFlattenedMap {
id: 'Bar',
time: 1,
`table.key.inner.key`: 'innerValue1' // but it could be `value1` as the order is not guaranteed
}
将重复键归组为值的数组
您可以使用选项 schema.map.group.duplicate.keys 来避免此问题。连接器会将具有相同键的所有值归组为一个数组。该选项的默认值为 false。在如下场景中:
val data = Seq(
("Foo", 1, Map("key.inner" -> Map("key" -> "innerValue"), "key" -> Map("inner.key" -> "value"))),
("Bar", 1, Map("key.inner" -> Map("key" -> "innerValue1"), "key" -> Map("inner.key" -> "value1"))),
).toDF("id", "time", "table")
data.write
.mode(SaveMode.Append)
.format(classOf[DataSource].getName)
.option("url", SparkConnectorScalaSuiteIT.server.getBoltUrl)
.option("labels", ":MyNodeWithFlattenedMap")
.option("schema.map.group.duplicate.keys", true)
.save()
输出将是:
MyNodeWithFlattenedMap {
id: 'Foo',
time: 1,
`table.key.inner.key`: ['innerValue', 'value'] // the order is not guaranteed
}
MyNodeWithFlattenedMap {
id: 'Bar',
time: 1,
`table.key.inner.key`: ['innerValue1', 'value1'] // the order is not guaranteed
}
约束类型映射
| Spark 类型 | Neo4j 类型 |
|---|---|
BooleanType |
布尔值 (BOOLEAN) |
StringType |
STRING |
IntegerType |
INTEGER(整数) |
LongType |
INTEGER(整数) |
FloatType |
FLOAT |
DoubleType |
FLOAT |
DateType |
DATE |
TimestampType |
LOCAL DATETIME |
自定义 |
POINT |
自定义 |
DURATION(持续时间) |
DataTypes.createArrayType(BooleanType, false) |
LIST<BOOLEAN NOT NULL> |
DataTypes.createArrayType(StringType, false) |
LIST<STRING NOT NULL> |
DataTypes.createArrayType(IntegerType, false) |
LIST<INTEGER NOT NULL> |
DataTypes.createArrayType(LongType, false) |
LIST<INTEGER NOT NULL> |
DataTypes.createArrayType(FloatType, false) |
LIST<FLOAT NOT NULL> |
DataTypes.createArrayType(DoubleType, false) |
LIST<FLOAT NOT NULL> |
DataTypes.createArrayType(DateType, false) |
LIST<DATE NOT NULL> |
DataTypes.createArrayType(TimestampType, false) |
LIST<LOCAL DATETIME NOT NULL> |
DataTypes.createArrayType(pointType, false) |
LIST<POINT NOT NULL> |
DataTypes.createArrayType(durationType, false) |
LIST<DURATION NOT NULL> |
特别是对于数组,我们使用不含 null 元素的版本,因为 Neo4j 不允许数组中存在 null。