加载 JSON
Web API 允许您访问并整合来自不同来源的数据到您的图中。大多数 Web API 提供 JSON 格式的数据。
加载 JSON 过程从 URL 或映射中检索数据,并将其转换为 MAP
值供 Cypher 使用。Cypher 支持使用点语法、切片、UNWIND
等解构嵌套文档,因此可以轻松地将嵌套数据转换为图。
流中包含多个 JSON 对象(JSONL、JSON Lines)的来源,例如 Twitter 流格式 或 Yelp Kaggle 数据集,也受支持。
过程概述
下表描述了可用的过程
限定名称 | 类型 |
---|---|
apoc.load.json |
|
apoc.load.jsonParams |
|
apoc.load.jsonArray |
|
apoc.import.json |
|
apoc.load.json
此过程接受文件或 HTTP URL,并将 JSON 解析为映射数据结构。
签名 |
---|
|
它支持以下配置参数
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
出错时失败 |
BOOLEAN |
true |
如果解析 JSON 时遇到错误,则失败 |
二进制 |
|
|
如果不为 null,则允许将二进制数据而不是文件名/URL 作为第一个参数。类似于 二进制文件示例 |
字符集 |
java.nio.charset.Charset |
|
可选字符集,当 |
apoc.load.jsonParams
该过程的“Params”名称指的是连接到给定 URL 时 HTTP 请求的 headers 和 payload 被参数化,而不是 Neo4j 的 $ 参数。 |
此过程接受文件或 HTTP URL,并将 JSON 解析为映射数据结构。它是 apoc.load.json 的一个更可配置的版本,支持处理需要 HTTP 头部或 JSON 有效负载的端点。
签名 |
---|
|
它支持以下配置参数
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
出错时失败 |
BOOLEAN |
true |
如果解析 JSON 时遇到错误,则失败 |
apoc.load.jsonArray
此过程接受包含 JSON 数组的文件或 HTTP URL,并将其解析为映射流。
签名 |
---|
|
apoc.import.json
此过程可用于导入由 导出 JSON 过程创建的 JSON 文件,这些文件使用配置参数 jsonFormat: 'JSON_LINES'
(默认配置)导出。
签名 |
---|
|
它支持以下配置参数
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
unwindBatchSize |
INTEGER |
|
unwind 的批处理大小 |
txBatchSize |
INTEGER |
|
事务的批处理大小 |
importIdName |
STRING |
|
要用 JSON 中存在的“id”字段填充的属性名称。例如,一行 |
nodePropertyMappings |
MAP |
|
自定义 Neo4j 类型(点日期)的映射标签/属性名称/属性类型。 例如 |
relPropertyMappings |
MAP |
|
自定义 Neo4j 类型(点日期)的映射关系类型/属性名称/属性类型。 例如 |
nodePropertyMappings
和 relPropertyMappings
支持以下 Neo4j 类型
Point
, Localdate
, Localtime
, Localdatetime
, Duration
, offsettime
, 和 Zoneddatetime
。
从文件导入
默认情况下,从文件系统导入是禁用的。我们可以通过在 apoc.conf
中设置以下属性来启用它
apoc.import.file.enabled=true
如果我们尝试在未首先设置此属性的情况下使用任何导入过程,将收到以下错误消息
调用过程失败:原因:java.lang.RuntimeException: 未启用文件导入,请在 apoc.conf 中设置 apoc.import.file.enabled=true |
导入文件从 import
目录读取,该目录由 server.directories.import
属性定义。这意味着我们提供的任何文件路径都是相对于此目录的。如果尝试从绝对路径读取,例如 /tmp/filename
,将收到类似于以下内容的错误消息
调用过程失败:原因:java.lang.RuntimeException: 无法将 url 或 key file:/path/to/neo4j/import/tmp/filename 读取为 json:/path/to/neo4j//import/tmp/filename (没有此文件或目录) |
我们可以通过在 apoc.conf
中设置以下属性来启用从文件系统中任意位置读取文件
apoc.import.file.use_neo4j_config=false
Neo4j 现在将能够从文件系统中的任何位置读取,因此在设置此属性之前请务必确认这是您的意图。 |
JSON-Path
使用 JSON 路径提供了一种简洁的方式来读取和处理嵌套 JSON 结构中的子文档和子值。如果您需要跳过展开高级父对象以访问更深层嵌套的数据,或者需要操作这些子结构中的值,这将特别有用。
您无需传入大型 JSON 文件并使用 Cypher 展开每个对象并访问所需内容,而是可以传入文件并提供所需子结构的 JSON 路径,从而使嵌套 JSON 的语句更短。JSON 路径格式遵循 Jayway 的 Java 实现,该实现基于 Stefan Gössner 的 JSONPath,为路径提供了一致的语法。
许多 apoc.convert.*Json*
过程和函数,以及 apoc.load.json
过程,也接受 JSON 路径作为它们的最后一个参数。请注意,这些函数旨在流式传输数组(值或对象)和映射,而不是单个值。如果路径指定了一个包含单个值的项目,则函数必须尝试将其包装,并且不会返回预期结果。
还有一个 apoc.json.path(json,path)
函数,它接受一个 JSON 字符串(不是映射或列表),并从作为第二个参数提供的 JSON 路径中检索值。注意:如果 JSON 尚未是字符串格式,可以使用 apoc.convert.toJson
函数进行转换。
更多示例请参阅上面提供的链接,但让我们看一个 JSON 路径语法的示例。下面显示的语法从 Neo4j 问题的 StackOverflow API 中提取 items
数组,并从项目列表中的第一个对象中检索 tags
数组。
$.items[0].tags
指定 JSON 路径的所有操作符和选项都包含在下表中。
操作符 | 描述 | 示例 |
---|---|---|
|
要查询的根元素。这启动所有路径表达式。 |
|
|
正在由过滤谓词处理的当前节点。 |
|
|
通配符。在需要名称或数字的任何地方都可用。 |
|
|
深度扫描。在需要名称的任何地方都可用。 |
|
|
点表示法子项 |
|
|
数组索引或多个索引 |
|
|
数组切片操作符 |
|
|
过滤表达式。表达式必须求值为布尔值。 |
|
此外,我们可以自定义 Json 路径选项,添加配置 {pathOptions: 字符串列表
},其中字符串基于 枚举<选项>。默认值为 ["SUPPRESS_EXCEPTIONS", "DEFAULT_PATH_LEAF_TO_NULL"]
。请注意,我们也可以插入 []
,即“无选项”。所以对于以下 json
{ "columns": {
"col2": {
"_id": "772col2"
}
}
}
我们可以执行(使用默认 pathOptions
)
CALL apoc.load.json($url, '$..columns');
值 |
---|
[ {"col2": { "_id": "772col2" }}, null, null ] |
或者,使用自定义路径选项
CALL apoc.load.json($url, '$..columns', ['ALWAYS_RETURN_LIST']);
输出 |
---|
[ {"col2": { "_id": "772col2" }} ] |
示例
以下部分包含从各种 JSON 源导入数据的示例。
从本地文件导入
person.json
包含一个 JSON 文档,表示一个人及其子女。
{
"name":"Michael",
"age": 41,
"children": ["Selina","Rana","Selma"]
}
我们将把这个文件放到 Neo4j 实例的 import
目录中。现在让我们使用 apoc.load.json
过程编写一个查询来探索这个文件。
person.json
并将内容作为 Cypher 数据结构返回CALL apoc.load.json("file:///person.json")
YIELD value
RETURN value;
值 |
---|
{name: "Michael", children: ["Selina", "Rana", "Selma"], age: 41} |
我们得到了一个与 JSON 文档几乎相同的映射。现在我们可以扩展该查询,根据此 JSON 文件创建一个图。我们将为 Michael 和他的每个孩子创建一个 Person
节点,并从每个孩子到 Michael 节点创建一个 CHILD_OF
关系。
person.json
创建图CALL apoc.load.json("file:///person.json")
YIELD value
MERGE (p:Person {name: value.name})
SET p.age = value.age
WITH p, value
UNWIND value.children AS child
MERGE (c:Person {name: child})
MERGE (c)-[:CHILD_OF]->(p);
下面的 Neo4j Browser 可视化显示了导入的图
您可以使用 failOnError
配置来处理不正确的 URL 或 JSON 导致的结果。例如,借助 apoc.when
过程,您可以在 URL 不正确时返回 nothingToDo
作为结果
CALL apoc.load.json("MY_JSON_URL", null, {failOnError:false})
YIELD value
WITH collect(value) as values
call apoc.do.when(values = [], "return 'nothingToDo' as result", "return values as result", {values: values})
YIELD value
UNWIND value["result"] as result
RETURN result
从 StackOverflow API 导入
apoc.load.json
允许从任何文件或 URL 加载 JSON 数据。如果结果是 JSON 对象,则返回单个映射。如果结果是数组,则转换为映射流。
StackOverflow 提供了多个 API,其中一个用于检索最近的问题和答案。用于检索 neo4j 标签 的最新问题和答案的 URL 是
https://api.stackexchange.com/2.2/questions?pagesize=100&order=desc&sort=creation&tagged=neo4j&site=stackoverflow&filter=!5-i6Zw8Y)4W7vpy91PMYsKM-k9yzEsSC1_Uxlf
由于这是一个相当长的 URL 字符串,我们可以通过在 apoc.conf
带有完整 json url 字符串的原始调用
带有别名字符串的新调用,完整字符串在 apoc.conf 中
|
让我们检查从这个端点返回的数据。
neo4j
标签的 5 个最新问题WITH "https://api.stackexchange.com/2.2/questions?pagesize=100&order=desc&sort=creation&tagged=neo4j&site=stackoverflow&filter=!5-i6Zw8Y)4W7vpy91PMYsKM-k9yzEsSC1_Uxlf" AS url
CALL apoc.load.json(url) YIELD value
UNWIND value.items AS item
RETURN item.title, item.owner, item.creation_date, keys(item)
LIMIT 5;
item.title | item.owner | item.creation_date | keys(item) |
---|---|---|---|
"用于获取自相关节点的 Cypher 模式" |
{profile_image: "https://lh3.googleusercontent.com/-1FWbhuaEBiQ/AAAAAAAAAAI/AAAAAAAAAIA/tLM_mEb-8MY/photo.jpg?sz=128", user_type: "registered", user_id: 5730203, link: "https://stackoverflow.com/users/5730203/asif-ali", reputation: 1148, display_name: "Asif Ali", accept_rate: 90} |
1586944991 |
["owner", "comment_count", "link", "last_activity_date", "creation_date", "answer_count", "title", "question_id", "tags", "share_link", "score", "down_vote_count", "body_markdown", "favorite_count", "is_answered", "delete_vote_count", "close_vote_count", "view_count", "up_vote_count"] |
"将 .NET 客户端连接到 Neo4j Desktop 版本 4 时出现问题" |
{profile_image: "https://www.gravatar.com/avatar/a3fac35d600d1d462d8fc12f3926074c?s=128&d=identicon&r=PG&f=1", user_type: "registered", user_id: 2853912, link: "https://stackoverflow.com/users/2853912/user2853912", reputation: 21, display_name: "user2853912"} |
1586938954 |
["owner", "comment_count", "link", "last_activity_date", "creation_date", "answer_count", "title", "question_id", "tags", "share_link", "score", "down_vote_count", "body_markdown", "favorite_count", "is_answered", "delete_vote_count", "close_vote_count", "view_count", "up_vote_count"] |
"Neo4j 使用哪种图算法?" |
{profile_image: "https://www.gravatar.com/avatar/736024b862a229111d4b3119875753b0?s=128&d=identicon&r=PG&f=1", user_type: "registered", user_id: 4402081, link: "https://stackoverflow.com/users/4402081/mariappan", reputation: 7, display_name: "Mariappan"} |
1586901300 |
["owner", "comment_count", "answers", "link", "last_activity_date", "creation_date", "answer_count", "title", "question_id", "tags", "share_link", "score", "down_vote_count", "body_markdown", "favorite_count", "is_answered", "delete_vote_count", "close_vote_count", "view_count", "up_vote_count"] |
"导入 json 文件到 Neo4j" |
{profile_image: "https://lh3.googleusercontent.com/-PWDC85Kp2ig/AAAAAAAAAAI/AAAAAAAAAAA/AB6qoq3nhmVZl-_0VDKESOG5MsyHvXnw_A/mo/photo.jpg?sz=128", user_type: "registered", user_id: 9964138, link: "https://stackoverflow.com/users/9964138/jo%c3%a3o-costa", reputation: 23, display_name: "João Costa"} |
1586897574 |
["owner", "comment_count", "answers", "link", "last_activity_date", "creation_date", "answer_count", "title", "question_id", "tags", "share_link", "score", "down_vote_count", "body_markdown", "favorite_count", "is_answered", "delete_vote_count", "close_vote_count", "view_count", "up_vote_count"] |
"Neo4j 图算法和图数据科学之间的区别" |
{profile_image: "https://i.stack.imgur.com/2rLPZ.jpg?s=128&g=1", user_type: "registered", user_id: 3297954, link: "https://stackoverflow.com/users/3297954/rotten", reputation: 1295, display_name: "rotten", accept_rate: 75} |
1586872077 |
["owner", "comment_count", "answers", "link", "last_activity_date", "creation_date", "answer_count", "title", "question_id", "tags", "share_link", "score", "down_vote_count", "body_markdown", "favorite_count", "is_answered", "delete_vote_count", "close_vote_count", "view_count", "up_vote_count"] |
现在让我们基于这些实体创建一个 Neo4j 图。
WITH "https://api.stackexchange.com/2.2/questions?pagesize=100&order=desc&sort=creation&tagged=neo4j&site=stackoverflow&filter=!5-i6Zw8Y)4W7vpy91PMYsKM-k9yzEsSC1_Uxlf" AS url
CALL apoc.load.json(url) YIELD value
UNWIND value.items AS q
MERGE (question:Question {id:q.question_id})
ON CREATE SET question.title = q.title,
question.share_link = q.share_link,
question.favorite_count = q.favorite_count
FOREACH (tagName IN q.tags | MERGE (tag:Tag {name:tagName}) MERGE (question)-[:TAGGED]->(tag))
FOREACH (a IN q.answers |
MERGE (question)<-[:ANSWERS]-(answer:Answer {id:a.answer_id})
MERGE (answerer:User {id:a.owner.user_id}) ON CREATE SET answerer.display_name = a.owner.display_name
MERGE (answer)<-[:PROVIDED]-(answerer)
)
WITH * WHERE NOT q.owner.user_id IS NULL
MERGE (owner:User {id:q.owner.user_id}) ON CREATE SET owner.display_name = q.owner.display_name
MERGE (owner)-[:ASKED]->(question)
下面的 Neo4j Browser 可视化显示了导入的图
使用 JSON 路径并从 StackOverflow API 导入
我们可以使用 JSON 路径语法缩小筛选和导入的数据范围。这将允许我们指定要导入的子结构并忽略其余数据。对于此示例,我们只想导入答案和发布这些答案的成员。
WITH "https://api.stackexchange.com/2.2/questions?pagesize=100&order=desc&sort=creation&tagged=neo4j&site=stackoverflow&filter=!5-i6Zw8Y)4W7vpy91PMYsKM-k9yzEsSC1_Uxlf" AS url
CALL apoc.load.json(url,'$.items[?(@.answer_count>0)].answers[*]') YIELD value
RETURN value LIMIT 5;
请注意,我们只查看答案计数大于 0 的 StackOverflow 问题。这意味着我们只传递包含答案的问题 JSON 对象,因为其余的与我们的用例无关。考虑到这一点,让我们使用此语句导入它们
WITH "https://api.stackexchange.com/2.2/questions?pagesize=100&order=desc&sort=creation&tagged=neo4j&site=stackoverflow&filter=!5-i6Zw8Y)4W7vpy91PMYsKM-k9yzEsSC1_Uxlf" AS url
CALL apoc.load.json(url,'$.items[?(@.answer_count>0)].answers[*]') YIELD value
MERGE (a:Answer {id: value.answer_id})
ON CREATE SET a.accepted = value.is_accepted,
a.shareLink = value.share_link,
a.lastActivityDate = value.last_activity_date,
a.creationDate = value.creation_date,
a.title = value.title,
a.score = value.score
MERGE (q:Question {id: value.question_id})
MERGE (a)-[rel:POSTED_TO]->(q)
WITH a as answer, value.owner as value
MERGE (u:User {userId: value.user_id})
ON CREATE SET u.displayName = value.display_name,
u.userType = value.user_type,
u.reputation = value.reputation,
u.userLink = value.link
MERGE (u)-[rel2:SUBMITTED]->(answer)
RETURN count(answer)
这将导入大约 78 个答案到我们的图中。然后我们可以探索这个图,找出哪些用户提交的答案最多,拥有最高的评分等等。
导入由导出 JSON 过程创建的 JSON 文件
apoc.import.json
过程可用于导入由 apoc.export.json.*
过程创建的 JSON 文件,这些文件使用配置参数 jsonFormat: 'JSON_LINES'
(默认配置)导出。
此过程支持以下配置参数
名称 | 默认值 | 描述 |
---|---|---|
unwindBatchSize |
|
unwind 的批处理大小 |
txBatchSize |
|
事务的批处理大小 |
importIdName |
|
要用 JSON 中存在的“id”字段填充的属性名称。例如,一行 |
nodePropertyMappings |
|
自定义 Neo4j 类型(点日期)的映射标签/属性名称/属性类型。例如 |
relPropertyMappings |
|
自定义 Neo4j 类型(点日期)的映射关系类型/属性名称/属性类型。例如 |
nodePropertyMappings
和 relPropertyMappings
支持以下 Neo4j 类型
-
Point
-
Localdate
-
Localtime
-
Localdatetime
-
Duration
-
offsettime
-
Zoneddatetime
all.json
包含 Neo4j 电影图的一个子集,由 导出 JSON 过程 生成。
{"type":"node","id":"0","labels":["User"],"properties":{"born":"2015-07-04T19:32:24","name":"Adam","place":{"crs":"wgs-84","latitude":13.1,"longitude":33.46789,"height":null},"age":42,"male":true,"kids":["Sam","Anna","Grace"]}}
{"type":"node","id":"1","labels":["User"],"properties":{"name":"Jim","age":42}}
{"type":"node","id":"2","labels":["User"],"properties":{"age":12}}
{"id":"0","type":"relationship","label":"KNOWS","properties":{"bffSince":"P5M1DT12H","since":1993},"start":{"id":"0","labels":["User"],"properties":{"born":"2015-07-04T19:32:24","name":"Adam","place":{"crs":"wgs-84","latitude":13.1,"longitude":33.46789,"height":null},"age":42,"male":true,"kids":["Sam","Anna","Grace"]}},"end":{"id":"1","labels":["User"],"properties":{"name":"Jim","age":42}}}
我们可以使用 apoc.import.json
导入此文件。
CALL apoc.import.json("file:///all.json")
文件 | 源 | 格式 | 节点 | 关系 | 属性 | 时间 | 行 | 批处理大小 | 批次 | 完成 | 数据 |
---|---|---|---|---|---|---|---|---|---|---|---|
"file:///all.json" |
"文件" |
"json" |
3 |
1 |
15 |
105 |
4 |
-1 |
0 |
TRUE |
NULL |
向 Wikipedia Action API 发送 GET 请求
以下向 Wikipedia Action API 发送 GET 请求
apoc.load.jsonParams
允许显式指定 HTTP 请求参数。要向 JSON 端点执行 GET 请求,可以将配置中的 method
参数设置为 GET
。
CALL apoc.load.jsonParams(
"https://en.wikipedia.org/w/api.php?action=query&titles=Neo4j&format=json&formatversion=2",
{method: "GET"},
"",
".query.pages[0]"
) YIELD value
RETURN value
值 |
---|
{ "title": "Neo4j", "ns": 0, "pageid": 25505874 } |
请注意,URL 需要正确编码。这可以通过使用 apoc.text.urlencode 实现。
WITH apoc.text.urlencode("Auvergne-Rhône-Alpes") AS title
CALL apoc.load.jsonParams(
"https://en.wikipedia.org/w/api.php?action=query&titles="+title+"&format=json&formatversion=2",
{method: "GET"}, "", ".query.pages[0]"
) YIELD value
RETURN value.title AS title, value.pageid AS pageid
标题 | pageid |
---|---|
"奥弗涅-罗讷-阿尔卑斯" |
45093325 |