neo4j - 在 neo4j 中使用展开和转储数据 - 查询优化
问题描述
我正在批量插入以在neo4j中插入数据,但我的事务需要大量时间,因为我的数据库也在不断增加。
在我的项目中,仅在一种情况下,我有超过18,000 条记录,这些记录将存储在数据库中,并将与目标节点建立关系。每条记录都将存储为朋友节点
关系就像
Target_Node-[r:followed_by]->Friend_Node
Target_Node-[r:Friends_with]-> Friend_Node
Target_Node-[r:Performs_Activity]->Friend_Node
我的查询分别针对所有情况执行,并且很有可能目标和朋友节点之间可能存在所有三种关系。
我为单个插入发送 20 条记录,该插入展开记录数组并检查记录是否已存在于 Friend_Node 或 Target_Node 中,如果不存在,则将其创建为 Friend_Node,然后为其分配关系;如果节点已经有关系并且新的关系被传递给查询,那么两个节点之间也将添加一个新的关系。
此外,如果记录确实具有Location属性,我也会检查我的查询,然后我会创建一个 Location Node 并分配与它的关系。
注意:create_rel 变量可以是 Friends_with、Followed_by 或 Activity_p
我的查询如下
"""UNWIND [{id: "1235" , uid : "0"}] as user
UNWIND """+ l +""" as c
OPTIONAL MATCH (n:Target {id : c.id , uid : "0"})
OPTIONAL MATCH (m:Friend {id : c.id , screen_name:c.screen_name, uid : "0"})
WITH coalesce(n, m) as node,user,c // returns first non-null value
CALL apoc.do.when(node is null, "MERGE (n:Friend {id:c.id, name:c.name, profile: c.profile, location:c.location, uid : user.uid}) RETURN n", '', {c:c,user:user}) YIELD value
with coalesce(node, value.n) as y,user,c
MERGE (u:Target {id: user.id , uid : user.uid})
"""+create_rel+"""
foreach (sc in c.cityn | merge(cn:Location {location:sc.location, loc_lower : sc.loc_lower}) merge (y)-[:`located_at`]-(cn))
"""
Db 有时也会给出 TransientError 错误。
感谢您的反馈,因为我是一名学习者,并将感谢您提出宝贵的建议。
提前致谢
解决方案
您应该避免同时运行多个写入查询(可以触及相同的节点和关系),因为这可能会导致间歇性
TransientError
s,正如您所见。(但是,可以重试导致暂时错误的查询。)您应该将
user
和作为参数l
传递给您的查询,以便 Cypher 计划程序只需要编译一次查询,并使查询不太容易受到 Cypher 注入攻击。(此外,不需要一个总是只有一个地图的列表——您可以通过 直接使用地图。但是,正如我所提到的,您应该只将地图作为参数传递,这样您就可以有效地更改用户无需强制重新编译。)UNWIND
WITH {id: "1235" , uid : "0"} AS user
user
为避免重新编译,您还需要将
create_rel
字符串设为常量字符串(因此,它也可能直接位于主查询字符串中)。同样,您还应该将所需的任何变量作为参数传递。您应该在and上创建索引(或唯一性约束),以加快您的and子句。
:Target(id)
:Friend(id)
MATCH
MERGE
(a)
MERGE (u:Target {id: user.id , uid : user.uid})
只需要执行一次,而不是每个c
值。所以,它应该在.UNWIND
(b) 此外,此查询不一定要创建
u
,因为查询中没有任何内容使用它。MERGE
因此,您应该考虑将其取出并运行一个单独的独立查询,而不是每个线程运行一次相同的子句。
这是一个结合了建议 #2 和 #5a 的查询(但您必须自己处理其他建议),以及使用模式理解进行一些重构以避免不必要的数据库命中:
MERGE (u:Target {id: $user.id, uid: $user.uid})
WITH u
UNWIND $l as c
WITH u, c, [(n:Target {id : c.id})-[*0]-()|n] AS nodeList
WITH u, c, CASE WHEN SIZE(nodeList) = 0 THEN [(n:Friend {id : c.id})-[*0]-()|n] ELSE nodeList END AS nodeList
CALL apoc.do.when(SIZE(nodeList) = 0, 'MERGE (n:Friend {id: c.id, name: c.name, profile: c.profile, location: c.location, uid: user.uid}) RETURN n', 'RETURN nodeList[0] AS n', {c:c,user:$user,nodeList:nodeList}) YIELD value
WITH u, c, value.n AS node
FOREACH (sc IN c.cityn | MERGE (cn:Location {location: sc.location, loc_lower: sc.loc_lower}) MERGE (node)-[:located_at]-(cn))
// Put your parameterized create_rel code here
推荐阅读
- react-native - 如何根据 Tab Navigator 更改 Drawer Navigator 的 headerStyle
- nuxt.js - 如何在 nuxt js 中从strapi 农场/解析降价帖子
- python - 如何在 python2 中递归使用 glob.glob() 和 glob.iglob()
- typescript - Prisma:我怎样才能让所有孩子都处于某种状态?
- javascript - 对象在浏览器上的最大尺寸是多少?(铬合金)
- javascript - Firebase 期望 collection() 的第一个参数是 CollectionReference、DocumentReference 或 FirebaseFirestore
- python - 循环设置和排序函数
- python - 如何在自定义过滤器中从渲染的 Jinja 文件中获取数据?
- openlayers - 在 OL 6.4.3 中从源向量加载特征 - 未显示特征
- ruby-on-rails - Sidekiq 忽略 I18n.locale