2014-09-20 2 views
1

Я хочу реализовать уникальное свойство ID на всех узлах в моей базе данных, но вам необходимо применить его к существующим данным. Я использую Ruby для выполнения генерации IDS, а затем запускаю запрос Cypher. Я хочу избежать одного запроса, чтобы найти узлы, у которых отсутствует свойство, другое - установить свойство на каждом узле отдельно, поскольку для этого потребуется total_nodes + 1 запросов.Neo4j: присваивать уникальные значения всем узлам, соответствующим запросу

Первоначально я думал, что я мог бы сделать что-то вроде этого:

MATCH (n:`#{label}`) WHERE NOT HAS(n.my_id) SET n.my_id = '#{gen_method}' RETURN DISTINCT(true) 

Конечно, это не будет работать, потому что он назвал бы gen_method один раз в Руби, а затем Neo4j бы попытаться установить все узлы идентификаторы это одно значение.

Теперь я думаю, что было бы лучше всего создать большое количество идентификаторов в Ruby, а затем включить это в запрос 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 

Возможно ли это? Есть ли другой способ справиться с этим, что будет работать?

ответ

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.

Смежные вопросы