Я пытался получить вид Cypher «s функции MERGE и начал с написания небольшого файла для импорта некоторых людей со случайными свойствами , используя Java-Факер библиотеку.
public class Merge { private static Label PERSON = DynamicLabel.label("Person"); public static void main(String[] args) throws IOException { File dbFile = new File("/tmp/test-db"); FileUtils.deleteRecursively(dbFile); Faker faker = new Faker(); Random random = new Random(); GraphDatabaseService db = new GraphDatabaseFactory().newEmbeddedDatabase(dbFile.getPath()); Transaction tx = db.beginTx(); for (int i = 0; i < 100000; i++) { Node person = db.createNode(PERSON); person.setProperty("name", faker.name()); person.setProperty("firstName", faker.firstName()); person.setProperty("lastName", faker.lastName()); person.setProperty("country", faker.country()); person.setProperty("age", random.nextInt(50)); } tx.success(); tx.close(); } }
Мы можем написать следующий запрос, чтобы получить образец импортированных людей:
$ MATCH (p:Person) RETURN p LIMIT 5; ==> +------------------------------------------------------------------------------------------------------------------+ ==> | p | ==> +------------------------------------------------------------------------------------------------------------------+ ==> | Node[1344]{name:"Benton Swaniawski",firstName:"Rossie",lastName:"Ankunding",country:"Guadeloupe",age:30} | ==> | Node[1345]{name:"Dagmar Bartell",firstName:"Ashlynn",lastName:"Watsica",country:"French Guiana",age:35} | ==> | Node[1346]{name:"Ms. Missouri Gaylord",firstName:"Muriel",lastName:"Streich",country:"Chile",age:43} | ==> | Node[1347]{name:"Melvina Heathcote",firstName:"Geovanni",lastName:"Marks",country:"United Arab Emirates",age:33} | ==> | Node[1348]{name:"Brendan Schaefer",firstName:"Dayne",lastName:"Haley",country:"Tokelau",age:24} | ==> +------------------------------------------------------------------------------------------------------------------+
Мы можем использовать функцию MERGE, чтобы убедиться, что узел с конкретными свойствами существует, поэтому мы можем написать что-то вроде следующего:
MERGE (p:Person {name: "Benton Swaniawski", firstName:"Rossie", lastName:"Ankunding", country:"Guadeloupe", age:30}) RETURN p
Если мы посмотрим на вывод PROFILE запроса, мы увидим что-то вроде следующего:
UpdateGraph(commands=[" MergeNodeAction( p, Map(firstName(1) -> Literal(Rossie), country(3) -> Literal(Guadeloupe), name(0) -> Literal(Benton Swaniawski), lastName(2) -> Literal(Ankunding), age(4) -> Literal(30)), List(Person(0)), ArrayBuffer(Property(p,lastName(2)) == Literal(Ankunding), Property(p,name(0)) == Literal(Benton Swaniawski), Property(p,age(4)) == Literal(30), Property(p,country(3)) == Literal(Guadeloupe), Property(p,firstName(1)) == Literal(Rossie)), List(LabelAction(p,LabelSetOp,List(Person(0))), PropertySetAction(Property(p,name),Literal(Benton Swaniawski)), PropertySetAction(Property(p,country),Literal(Guadeloupe)), PropertySetAction(Property(p,age),Literal(30)), PropertySetAction(Property(p,lastName),Literal(Ankunding)), PropertySetAction(Property(p,firstName), Literal(Rossie))), List(), Some(PlainMergeNodeProducer(<function2>)))"], _rows=1, _db_hits=100219)
Особенность в том, что было 100,219 db_hits, что значительно замедляет запрос.
Если мы хотим использовать MERGE, нам нужно убедиться, что у нас есть индекс или ограничение для одного из свойств, например
$ CREATE INDEX ON :Person(name); ==> +-------------------+ ==> | No data returned. | ==> +-------------------+ ==> Indexes added: 1
Если мы посмотрим на профиль этого, то увидим, что число db_hits уменьшилось, поскольку теперь он использует индекс для выполнения части поиска, которая требуется MERGE:
UpdateGraph(commands=[" MergeNodeAction( p, Map(firstName(1) -> Literal(Rossie), country(3) -> Literal(Guadeloupe), name(0) -> Literal(Benton Swaniawski), ... Some(PlainMergeNodeProducer(<function2>)))"], _rows=1, _db_hits=4)
Мы можем пойти еще дальше, включив только свойство, которое действует как наш «ключ» (то есть имя), в первую часть оператора и установив другие свойства только в случае необходимости:
MERGE (p:Person {name: "Benton Swaniawski"}) ON CREATE SET p.firstName="Rossie", p.lastName="Ankunding", p.country="Guadeloupe", p.age=30 RETURN p
Если мы профилируем этот запрос, мы увидим, что ситуация улучшилась:
UpdateGraph(commands=["MergeNodeAction( p, Map(name(0) -> Literal(Benton Swaniawski)), List(Person(0)),ArrayBuffer(), List(LabelAction(p,LabelSetOp,List(Person(0))), PropertySetAction(Property(p,name),Literal(Benton Swaniawski)), PropertySetAction(Property(p,firstName),Literal(Rossie)), PropertySetAction(Property(p,lastName),Literal(Ankunding)), PropertySetAction(Property(p,country),Literal(Guadeloupe)), PropertySetAction(Property(p,age),Literal(30))), List(), Some(PlainMergeNodeProducer(<function2>)))"], _rows=1, _db_hits=0)
В некоторых случаях мы можем захотеть обновить свойство каждый раз, когда «ключ» сопоставляется в операторе MERGE, что мы можем сделать так:
MERGE (p:Person {name: "Benton Swaniawski"}) ON MATCH SET p.times = COALESCE(p.times, 0) + 1 RETURN p
Вы также можете использовать MERGE для создания отношений, но сейчас я просто хотел изучить, как это следует использовать в контекстных узлах, которые, как мне кажется, я сейчас понял.
Всегда рад принять советы о том, как сделать что-то лучше!