Импорт json в neo4J

[ПРОБЛЕМА - Мое окончательное решение ниже]

Я хочу импортировать json-файл, содержащий мои данные, в Neo4J. Однако это очень медленно.

Файл Json имеет следующую структуру:

{
    "graph": {
        "nodes": [
            { "id": 3510982, "labels": ["XXX"], "properties": { ... } },
            { "id": 3510983, "labels": ["XYY"], "properties": { ... } },
            { "id": 3510984, "labels": ["XZZ"], "properties": { ... } },
     ...
        ],
        "relationships": [
            { "type": "bla", "startNode": 3510983, "endNode": 3510982, "properties": {} },
            { "type": "bla", "startNode": 3510984, "endNode": 3510982, "properties": {} },
    ....
        ]
    }
}

Аналогично предложенному здесь: Как восстановить данные из предыдущего результата в браузере?.

Посмотрев на ответ. Я обнаружил, что могу использовать

CALL apoc.load.json("file:///test.json") YIELD value AS row
WITH row, row.graph.nodes AS nodes
UNWIND nodes AS node
CALL apoc.create.node(node.labels, node.properties) YIELD node AS n
SET n.id = node.id

а потом

CALL apoc.load.json("file:///test.json") YIELD value AS row
with row
UNWIND row.graph.relationships AS rel
MATCH (a) WHERE a.id = rel.endNode
MATCH (b) WHERE b.id = rel.startNode
CALL apoc.create.relationship(a, rel.type, rel.properties, b) YIELD rel AS r
return *

(Мне нужно сделать это два раза, потому что в противном случае их отношения дублируются из-за двух unwind).

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

В то же время я знаю, что "startNode": 3510983 относится к узлу. Итак, вопрос: существует ли способ ускорить процесс импорта с использованием идентификаторов в качестве индекса или чего-то еще?

Обратите внимание, что мои узлы имеют разные типы. Поэтому я не нашел способа создать индекс для всех из них, и, полагаю, это было бы слишком много (память)

[МОЕ РЕШЕНИЕ]

CALL apoc.load.json('file:///test.json') YIELD value
WITH value.graph.nodes AS nodes, value.graph.relationships AS rels
UNWIND nodes AS n
CALL apoc.create.node(n.labels, apoc.map.setKey(n.properties, 'id', n.id)) YIELD node
WITH rels, COLLECT({id: n.id, node: node, labels:labels(node)}) AS nMap
UNWIND rels AS r
MATCH (w{id:r.startNode})
MATCH (y{id:r.endNode})
CALL apoc.create.relationship(w, r.type, r.properties, y) YIELD rel
RETURN rel

person Benoît badetitou Verhaeghe    schedule 27.04.2020    source источник


Ответы (2)


[ИЗМЕНИТЬ]

Этот подход может работать более эффективно:

CALL apoc.load.json("file:///test.json") YIELD value
WITH value.graph.nodes AS nodes, value.graph.relationships AS rels
UNWIND nodes AS n
CALL apoc.create.node(n.labels, apoc.map.setKey(n.properties, 'id', n.id)) YIELD node
WITH rels, apoc.map.mergeList(COLLECT({id: n.id, node: node})) AS nMap
UNWIND rels AS r
CALL apoc.create.relationship(nMap[r.startNode], r.type, r.properties, nMap[r.endNode]) YIELD rel
RETURN rel

Этот запрос вообще не использует MATCH (и не требует индексации), поскольку он просто полагается на отображение в памяти идентификаторов импортированных узлов на созданные узлы. Однако этому запросу может не хватить памяти, если имеется много импортированных узлов.

Он также позволяет избежать вызова SET с помощью apoc.map. setKey, чтобы добавить свойство id в n.properties.

2 UNWIND не приводят к декартовому продукту, поскольку в этом запросе используется функция агрегирования COLLECT (перед второй UNWIND) для уплотнения всех предшествующих строк в одну (поскольку ключ группировки rels является одноэлементным).

person cybersam    schedule 27.04.2020
comment
Я получаю java.lang.NullPointerException. Я считаю, что это потому, что nMap[r.startNode] не работает (или nMap[r.endNode]). Я попытался понять, почему, и кажется, что Collect создает список карт, а не только карту. Итак, я пытаюсь создать карту с apoc.map.mergelist(), но похоже, что я не понимаю, как это сделать, поскольку он все еще не работает ... Обратите внимание, что я уверен, что startNode и endNote существуют в моих идентификаторах (в узлы) - person Benoît badetitou Verhaeghe; 28.04.2020
comment
Также обратите внимание, что startNode не позиция в наборе узлов. Это всего лишь ID. - person Benoît badetitou Verhaeghe; 28.04.2020
comment
Я исправил запрос, используя apoc.map.mergeList для преобразования списка карт в единую карту. Кроме того, синтаксис nMap[r.startNode] - это то, как вы можете получить доступ к значению карты с помощью динамического ключа, поэтому не должно быть проблем. - person cybersam; 28.04.2020
comment
Здравствуйте, у меня еще одно исключение Expected Long(3511200) to be a org.neo4j.values.storable.TextValue, but it was a org.neo4j.values.storable.LongValue - person Benoît badetitou Verhaeghe; 29.04.2020
comment
Похоже, n.id числовой. Попробуйте изменить COLLECT({id: n.id, node: node}) на COLLECT({id: TOSTIRNG(n.id), node: node}). И если вы по-прежнему получаете ту же ошибку, возможно, вам придется использовать TOSTRING на r.startNode и r.endNode. - person cybersam; 29.04.2020

Вы пробовали индексировать узлы перед загрузкой JSON? Это может быть неприемлемо, поскольку у вас есть несколько меток узлов. Но если они ограничены, вы можете создать узел-заполнитель, создать и проиндексировать, а затем удалить заполнитель. После этого запустите LOAD Json

    Create (n:YourLabel{indx:'xxx'})
    create index on: YourLabel(indx)
    match (n:YourLabel) delete n

Индекс ускорит сопоставление или слияние

person David A Stumpf    schedule 28.04.2020
comment
Подумал об индексации. Однако у меня есть несколько ярлыков, я могу не знать их перед загрузкой .. Как создать заполнитель? - person Benoît badetitou Verhaeghe; 28.04.2020
comment
Заполнитель создан с помощью: create (n: NodeName (param: 'xxx'}) создать индекс для: NodeName (param) match (n: NodeName (param: 'xxx'}) delete n - person David A Stumpf; 08.05.2020