我想在我的数据库中的所有节点上实现唯一的ID属性,但需要将其应用于现有数据。我正在使用Ruby来执行生成IDS,然后从那里运行Cypher查询。我想避免一个查询找到缺少该属性的节点,另一个查询是为了单独设置每个节点上的属性,因为这需要total_nodes + 1
个查询。
最初,我以为我可以这样做:
MATCH (n:`#{label}`) WHERE NOT HAS(n.my_id) SET n.my_id = '#{gen_method}' RETURN DISTINCT(true)
当然,这不起作用,因为它会在Ruby中调用gen_method
一次,然后Neo4j会尝试将所有节点ID设置为该值。
我现在想的是,最好先在Ruby中生成大量的ID,然后将其包含在Cypher查询中。我想遍历匹配的节点并将缺少的属性设置为等于其在数组中的相应索引。逻辑应该是这样的
MATCH NODES WHERE GIVEN PROPERTY IS NULL, LIMIT TO 10,000
CREATE A COLLECTION OF THOSE NODES
SET NEW UUIDS ARRAY (provided by Ruby) AS "IDS_ARRAY"
FOR EACH NODE IN COLLECTION
SET GIVEN PROPERTY VALUE = CORRESPONDING INDEX POSITION IN "IDS_ARRAY"
RETURN COUNT OF NODES WHERE GIVEN PROPERTY IS NULL
根据返回值,它会知道执行此操作的次数。 Cypher有一个foreach循环,但我是如何做到的,特别是如果我的unique_ids
数组是从Cypher查询中的字符串开始的话?
unique_ids = ['first', 'second', 'third', 'etc']
i = 0
for node in matched_nodes
node.my_id_property = unique_ids[i]
i += 1
end
甚至可能吗?是否有一种不同的处理方式可行?
答案 0 :(得分:1)
知道了!找到http://java.dzone.com/articles/neo4j-cypher-creating,它提供了执行此操作的方法,http://jexp.de/blog/2014/03/quickly-create-a-100k-neo4j-graph-data-model-with-cypher-only/指出了range
函数。我执行此操作的Ruby代码的第一个草稿如下所示:
def add_ids_to(model)
label = model.mapped_label_name
property = model.primary_key
total = 1
until total == 0
total = Neo4j::Session.query("MATCH (n:`#{label}`) WHERE NOT has(n.#{property}) RETURN COUNT(n) as ids").first.ids
return if total == 0
to_set = total > 900 ? 900 : total
new_ids = [].tap do |ids_array|
to_set.times { ids_array.push "'#{new_id_for(model)}'" }
end
Neo4j::Session.query("MATCH (n:`#{label}`) WHERE NOT has(n.#{property})
with COLLECT(n) as nodes, [#{new_ids.join(',')}] as ids
FOREACH(i in range(0,#{to_set - 1})|
FOREACH(node in [nodes[i]]|
SET node.#{property} = ids[i]))
RETURN distinct(true)
limit #{to_set}")
end
end
我认为这些都非常易读。关于查询本身,我使用Neo4j.rb和neo4j-core,但我在这种情况下跳过了Cypher DSL。我将每个查询限制为最多900个节点,因为这是我可以在没有内存不足的情况下可靠地运行的最高节点。调整您的JVM堆大小。