Как использовать Cypher с Merge для создания уникального пути к графу
В Neo4j 2.0 M06 я понимаю, что CREATE UNIQUE обесценивается и заменяется на MERGE и MATCH вместо этого, но мне трудно понять, как это можно использовать для создания уникального пути.
В качестве примера я хочу создать
MERGE root-[:HAS_CALENDER]->(cal:Calender{name:'Booking'})-[:HAS_YEAR]->(year:Year{value:2013})-[:HAS_MONTH]-(month:Month{value:'January'})-[:HAS_DAY]->(day:Day{value:1})
ON CREATE cal
SET cal.created = timestamp()
ON CREATE year
SET year.created = timestamp()
ON CREATE month
SET month.created = timestamp()
ON CREATE day
SET day.created = timestamp()
Намерение состоит в том, что когда я пытаюсь добавить новые дни в свой календарь, он должен создавать только год, а месяц, когда он не существует, просто добавляется к существующему пути. Теперь, когда я запускаю запрос, я получаю STATEMENT_EXECUTION_ERROR
MERGE only supports single node patterns
Должен ли я выполнять несколько заявлений здесь, чтобы добиться этого. Итак, вопрос в том, как лучше всего справиться с подобными случаями в Neo4j?
редактировать
Я немного изменил свой подход, и теперь, даже после нескольких вызовов, я думаю, что мое слияние происходит на уровне метки, и я не пытаюсь ограничиться начальным узлом, который я предоставляю, в результате я получаю узлы, которые совместно используются годами. и месяц, который не то, что я ожидал
Буду очень признателен, если кто-нибудь подскажет мне, как получить правильный график, как показано ниже
мой код на C# выглядит примерно так:
var qry = GraphClient.Cypher
.Merge("(cal:CalendarType{ Name: {calName}})")
.OnCreate("cal").Set("cal = {newCal}")
.With("cal")
.Start(new { root = GraphClient.RootNode})
.CreateUnique("(root)-[:HAS_CALENDAR]->(cal)")
.WithParams(new { calName = newCalender.Name, newCal = newCalender })
.Return(cal => cal.Node<CalenderType>());
var calNode = qry.Results.Single();
var newYear = new Year { Name = date.Year.ToString(), Value = date.Year }.RunEntityHousekeeping();
var qryYr = GraphClient.Cypher
.Merge("(year:Year{ Value: {yr}})")
.OnCreate("year").Set("year = {newYear}")
.With("year")
.Start(new { calNode })
.CreateUnique("(calNode)-[:HAS_YEAR]->(year)")
.WithParams(new { yr = newYear.Value, newYear = newYear })
.Return(year => year.Node<Year>());
var yearNode = qryYr.Results.Single();
var newMonth = new Month { Name = date.Month.ToString(), Value = date.Month }.RunEntityHousekeeping();
var qryMonth = GraphClient.Cypher
.Merge("(mon:Month{ Value: {mnVal}})")
.OnCreate("mon").Set("mon = {newMonth}")
.With("mon")
.Start(new { yearNode })
.CreateUnique("(yearNode)-[:HAS_MONTH]->(mon)")
.WithParams(new { mnVal = newMonth.Value, newMonth = newMonth })
.Return(mon => mon.Node<Month>());
var monthNode = qryMonth.Results.Single();
var newDay = new Day { Name = date.Day.ToString(), Value = date.Day, Date = date.Date }.RunEntityHousekeeping();
var qryDay = GraphClient.Cypher
.Merge("(day:Day{ Value: {mnVal}})")
.OnCreate("day").Set("day = {newDay}")
.With("day")
.Start(new { monthNode })
.CreateUnique("(monthNode)-[:HAS_DAY]->(day)")
.WithParams(new { mnVal = newDay.Value, newDay = newDay })
.Return(day => day.Node<Day>());
var dayNode = qryDay.Results.Single();
С уважением Киран
3 ответа
Нигде на странице документации не сказано, что CREATE UNIQUE
был объявлен устаревшим
MERGE
это просто новый подход, который доступен для вас. Это позволяет некоторые новые сценарии (сопоставление на основе меток, и ON CREATE
а также ON MATCH
триггеры), но также не охватывает более сложные сценарии (более одного узла).
Похоже, вы уже знакомы с CREATE UNIQUE
, На данный момент, я думаю, вы все еще должны использовать это.
Мне кажется, что картинка, на которую вы хотите, чтобы ваш график выглядела, имеет порядок, налагаемый отношениями, но ваш код моделирует порядок с узлами. Если вы хотите этот график, вам нужно использовать такие типы отношений, как [2010]
, [2011]
вместо шаблона как [HAS_YEAR]->({value:2010})
,
Другой способ сказать то же самое: вы пытаетесь создать уникальность для узла по своей сути, используя комбинацию метки и свойства, например: (unique:Day {value:4})
, Предполагая, что у вас есть соответствующие ограничения, это будет уникальность для всей базы данных, поэтому только один четвертый день месяца для всех месяцев, чтобы поделиться. То, что вы хотите, это внешняя локальная уникальность, уникальность, установленная и расширенная транзитивно иерархией отношений. Уникальность узла тогда заключается не в его внутренних свойствах, а в его внешней "позиции" или "порядке" по отношению к его родителю. Локально уникальный образец (month)-[:locally_unique_rel]->(day)
становится уникальным для более широкой области, когда месяц становится уникальным, а месяц становится уникальным не по свойству и метке, а по внешнему виду благодаря его "порядку" или "позиции" в соответствии со своим годом. Отсюда и транзитивность. Я думаю, что это сила моделирования с графиками, помимо прочего, она позволяет вам продолжать разделять вашу структуру. Например, если вы хотите разделить несколько дней на AM и PM или на часы, вы можете легко это сделать.
Итак, на вашем графике, [HAS_DAY]
делает все дни в равной степени связанными с их месяцем, и поэтому не может использоваться для разграничения между ними. Вы решили это локально менее чем за месяц, так как значение свойства различается, но с четвертого дня месяца в
(november)-[:HAS_DAY]->(4th)` and `(december)-[:HAS_DAY]->(4th)
не различаются по значению свойства или метке, это один и тот же узел на вашем графике. Локально, скажем, за месяц уникальные узлы могут быть достигнуты в равной степени с
[11]->()-[4]->(unique1), [11]->()-[5]->(unique2)
а также
[HAS_MONTH]->({value:11})-[HAS_DAY]->(unique1 {value:4}),
[HAS_MONTH]->({value:11})-[HAS_DAY]->(unique2 {value:5})
Разница в том, что с прежней внешней локальной уникальностью вы получаете преимущество транзитивности. Поскольку месяцы уникальны в году, как (november)
в [11]->(november)
является уникальным в местном масштабе, поэтому дни ноября также уникальны в этом году - (fourth)
узел отличается между
[11]->(november)-[4]->(fourth)
а также
[12]-(december)->[4]->(fourth)
Это означает передачу большей части вашей семантической модели вашим отношениям, оставляя узлы для хранения данных. Идентификаторы узлов на картинке, которую вы разместили, являются только педагогическими, их замена на x,y,z или пустые скобки, возможно, лучше раскрыла бы структуру или структуру графа.
Если вы хотите сохранить типы отношений без изменений, добавьте свойство упорядочения к каждому отношению, чтобы создать шаблон (november)-[:HAS_DAY {order:4}]->(4th)
тоже будет работать. Это может быть менее эффективным для запросов, но у вас могут быть другие проблемы, которые стоят того.
Этот код позволяет создавать календарные графики по запросу при создании события на определенный день. Вы захотите изменить его, чтобы разрешить события на несколько дней, но похоже, что ваша проблема заключается в создании уникальных путей, верно? И вы, вероятно, захотите изменить это, чтобы использовать параметры на выбранном вами языке.
Сначала я создаю рут:
CREATE (r:Root {id:'root'})
Затем используйте этот повторно используемый запрос MERGE для последовательного сопоставления или создания подграфов для календаря. Я передаю корень, чтобы я мог отобразить график в конце:
MATCH (r:Root)
MERGE r-[:HAS_CAL]->(cal:Calendar {id:'General'})
WITH r,cal MERGE (cal)-[:HAS_YEAR]->(y:Year {id:2011})
WITH r,y MERGE (y)-[:HAS_MONTH]->(m:Month {id:'Jan'})
WITH r,m MERGE (m)-[:HAS_DAY]->(d:Day {id:1})
CREATE d-[:SCHEDULED_EVENT]->(e:Event {id:'ev3', t:timestamp()})
RETURN r-[*1..5]-()
Создает график, подобный этому, когда вызывается несколько раз:
Это помогает?