Эффективный импорт операторов Cypher

Я пытаюсь экспортировать базу данных в файл и импортировать ее снова, не копируя реальные файлы базы данных и не останавливая базу данных. Я понимаю, что существует ряд превосходных (и производительных) инструментов neo4j-shell, однако база данных Neo4j удаленная и export-* а также import-* Команды требуют, чтобы файлы находились на удаленном клиенте, тогда как для моих обстоятельств они находятся локально.

В следующем посте объясняются альтернативные методы экспорта / импорта данных, однако импорт не слишком эффективен.

В следующих примерах для тестирования используется подмножество нашего хранилища данных, состоящее из 10000 узлов с различными метками / свойствами. Сначала база данных была экспортирована через

> time cypher-shell 'CALL apoc.export.cypher.all("/tmp/graph.db.cypher", {batchSize: 1000, format: "cypher-shell", separateFiles: true})'  
real    0m1.703s

а затем вытерли,

neo4j stop
rm -rf /var/log/neo4j/data/databases/graph.db
neo4j start

перед повторным импортом,

time cypher-shell < /tmp/graph.db.nodes.cypher
real    0m39.105s

который не кажется чрезмерно производительным. Я также попробовал маршрут Python, экспортировав Cypher в простом формате:

CALL apoc.export.cypher.all("/tmp/graph.db.cypher", {format: "plain", separateFiles: true})

Следующий фрагмент работал примерно через 30 секунд (при размере партии 1000),

from itertools import izip_longest
from neo4j.v1 import GraphDatabase


with GraphDatabase.driver('bolt://localhost:7687') as driver:
    with driver.session() as session: 
        with open('/tmp/graph.db.nodes.cypher') as file:
            for chunk in izip_longest(*[file] * 1000):
                with session.begin_transaction() as tx:
                for line in chunk:
                    if line:
                        tx.run(line)

Я понимаю, что параметризованные запросы Cypher более оптимальны. Я использовал несколько неуклюжую логику (обратите внимание, что замена строки недостаточна для всех случаев), чтобы попытаться извлечь метки и свойства из кода Cypher (который выполнялся в ~ 8 с):

from itertools import izip_longest
import json
from neo4j.v1 import GraphDatabase
import re


def decode(statement):
    m = re.match('CREATE \((.*?)\s(.*?)\);', statement)
    labels = m.group(1).replace('`', '').split(':')[1:]
    properties = json.loads(m.group(2).replace('`', '"')) # kludgy    
    return labels, properties


with GraphDatabase.driver('bolt://localhost:7687') as driver:
    with driver.session() as session: 
        with open('/tmp/graph.db.nodes.cypher') as file:
            for chunk in izip_longest(*[file] * 1000):
                with session.begin_transaction() as tx:
                    for line in chunk:
                        if line:
                            labels, properties = decode(line)

                        tx.run(
                            'CALL apoc.create.node({labels}, {properties})', 
                            labels=labels, 
                            properties=properties,
                        )

С помощью UNWIND а не транзакции дополнительно повышает производительность до ~ 5 с:

with GraphDatabase.driver('bolt://localhost:7687') as driver:
    with driver.session() as session: 
        with open('/tmp/graph.db.nodes.cypher') as file:
        for chunk in izip_longest(*[file] * 1000):
            rows = []

            for line in chunk:
                if line:
                    labels, properties = decode(line)
                    rows.append({'labels': labels, 'properties': properties})

            session.run(
                """
                UNWIND {rows} AS row
                WITH row.labels AS labels, row.properties AS properties
                CALL apoc.create.node(labels, properties) YIELD node
                RETURN true
                """,
                rows=rows,
            )

Это правильный подход для ускорения импорта Cypher? В идеале я хотел бы, чтобы в Python не выполнялся этот уровень манипулирования, это частично потому, что оно может быть подвержено ошибкам, и мне придется делать нечто подобное для отношений.

Также кто-нибудь знает правильный подход к декодированию Cypher для извлечения свойств? Этот метод завершается ошибкой, если в свойстве есть обратная галочка (`). Примечание. Я не хочу идти по маршруту GraphML, поскольку мне также нужна схема, которая экспортируется в формате Cypher. Хотя так странно распаковывать Сайфер таким образом.

Наконец, для справки import-binary Команды оболочки требуют ~ 3 с для выполнения того же импорта:

> neo4j-shell -c "import-binary -b 1000 -i /tmp/graph.db.bin"
...
finish after 10000 row(s)  10. 100%: nodes = 10000 rels = 0 properties = 106289 time 3 ms total 3221 ms

0 ответов

Другие вопросы по тегам