У меня есть задача для приложения Node.Js, которое преобразует текст в сеть (слова - это узлы, а их совместные вхождения - нескольких весов - это соединения в сетевом графе), где мне нужно сохранить все данные в базу данных Neo4J.
Как вы можете себе представить, хотя это работает и для коротких операторов, как только они становятся длиннее, например, 5000 слов, где словарь составляет около 500 уникальных слов, мне нужно сохранить много узлов и много соединения в базу данных.
На данный момент у меня есть запрос такого типа:
MATCH (u:User {uid: "15229100-b20e-11e3-80d3-6150cb20a1b9"})
MERGE (c_0:Context {name:"st",by:u.uid,uid:"d0342000-1c4c-11e9-b9f6-e1addb3b0fa7"})
ON CREATE SET c_0.timestamp="15479451847980000" MERGE (c_0)-[:BY{timestamp:"15479451847980000"}]->(u)
CREATE (s:Statement {name:"#apple #orange #fruit", text:"apples and oranges are fruits", uid:"d0390200-1c4c-11e9-b9f6-e1addb3b0fa7", timestamp:"15479451847980000"})
CREATE (s)-[:BY {context:c_0.uid,timestamp:s.timestamp}]->(u)
CREATE (s)-[:IN {user:u.id,timestamp:s.timestamp}]->(c_0)
MERGE (cc_0:Concept {name:"apple"})
ON CREATE SET cc_0.uid="d0390201-1c4c-11e9-b9f6-e1addb3b0fa7"
MERGE (cc_1:Concept {name:"orange"})
ON CREATE SET cc_1.uid="d0390204-1c4c-11e9-b9f6-e1addb3b0fa7"
MERGE (cc_2:Concept {name:"fruit"})
ON CREATE SET cc_2.uid="d0390207-1c4c-11e9-b9f6-e1addb3b0fa7"
CREATE (cc_0)-[:BY {context:c_0.uid,timestamp:s.timestamp,statement:s.uid}]->(u)
CREATE (cc_0)-[:OF {context:c_0.uid,user:u.uid,timestamp:s.timestamp}]->(s)
CREATE (cc_0)-[:AT {user:u.uid,timestamp:s.timestamp,context:c_0.uid,statement:s.uid}]->(c_0)
CREATE (cc_0)-[:TO {context:c_0.uid,statement:s.uid,user:u.uid,timestamp:"15479451847980000",uid:"d0390205-1c4c-11e9-b9f6-e1addb3b0fa7",gapscan:"2",weight:"3"}]->(cc_1)
CREATE (cc_1)-[:BY {context:c_0.uid,timestamp:"15479451847980000",statement:s.uid}]->(u) CREATE (cc_1)-[:OF {context:c_0.uid,user:u.uid,timestamp:"15479451847980000"}]->(s)
CREATE (cc_1)-[:AT {user:u.uid,timestamp:"15479451847980000",context:c_0.uid,statement:s.uid}]->(c_0)
CREATE (cc_1)-[:TO {context:c_0.uid,statement:s.uid,user:u.uid,timestamp:"15479451847980002",uid:"d0390208-1c4c-11e9-b9f6-e1addb3b0fa7",gapscan:"2",weight:"3"}]->(cc_2)
CREATE (cc_0)-[:TO {context:c_0.uid,statement:s.uid,user:u.uid,timestamp:"15479451847980002",uid:"d039020a-1c4c-11e9-b9f6-e1addb3b0fa7",gapscan:"4",weight:"2"}]->(cc_2)
CREATE (cc_2)-[:BY {context:c_0.uid,timestamp:"15479451847980002",statement:s.uid}]->(u)
CREATE (cc_2)-[:OF {context:c_0.uid,user:u.uid,timestamp:"15479451847980002"}]->(s)
CREATE (cc_2)-[:AT {user:u.uid,timestamp:"15479451847980002",context:c_0.uid,statement:s.uid}]->(c_0)
RETURN s.uid;
Для этого абзаца текста:
apples and oranges are fruits
Как видите, это оказывается намного дольше, чем необходимо.
В настоящее время для более длинных текстов я просто делю запрос Cypher на более мелкие куски и передаю их в БД в транзакции.
Однако, это все еще довольно медленно - около секунды на каждый КБ текста.
Мой запрос строится в Javascript / Node.Js здесь: https://github.com/noduslabs/infranodus/blob/master/lib/db/neo4j.js#L116
Основная логика:
Если есть предложение типа apples and oranges are fruits
Мне нужно создать узел для каждого, связать их с user
, который их создал, а также с самим statement
и graph
(график может иметь несколько операторов).
Тогда мне нужно создать связи между этими словами с разными весами:
[яблоки - апельсины]
[апельсины - фрукты]
[яблоки - фрукты]
...
Какой эффективный способ сделать это для последовательности, скажем, 300 слов?
Я знаю, что мог бы использовать FOREACH
циклов и params
, а также apoc
для генерации UID, но действительно ли это сделает запрос более эффективным по сравнению с его построением с использованием JavaScript? Я не совсем понимаю, как это сделать на моем множестве и поможет ли это решить проблему слишком большого количества словосочетаний для 300+.
Могут ли эти практики помочь и как я могу внедрить их в этот запрос Cypher?