如何使用Cypher with Merge创建一个独特的子图路径

时间:2013-11-01 17:09:58

标签: neo4j cypher neo4jclient

在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处理这类案件的最佳方式是什么?

修改

我确实改变了我的方法,现在即使在进行多次调用之后,我认为我的合并发生在标签级别而不是试图限制到我提供的起始节点,因此我最终得到了节点多年和月共享,这不是我所期待的

enter image description here

如果有人能建议我如何获得如下图所示的正确图表,我将非常感激

enter image description here

我的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 个答案:

答案 0 :(得分:2)

the documentation page无处声称CREATE UNIQUE已被弃用。

MERGE只是一种可供您使用的新方法。它支持一些新的方案(基于标签,ON CREATEON MATCH触发器进行匹配),但也不包括更复杂的方案(超过单个节点)。

听起来你已经熟悉了CREATE UNIQUE。现在,我认为你应该继续使用它。

答案 1 :(得分:0)

在我看来,你希望你的图形看起来像是由关系强加的顺序,但是你的代码用节点模拟顺序。如果您需要该图表,则需要使用[2010][2011]等关系类型,而不是像[HAS_YEAR]->({value:2010})这样的模式。

另一种说同样事物的方式:你试图通过标签和属性的组合来固有地构成节点的唯一性,例如: (unique:Day {value:4})。假设您有相关的约束条件,那么这将是数据库范围内的唯一性,因此所有月份中只有四分之一的月份可供共享。你想要的是外在的本地唯一性,建立的唯一性以及通过关系等级传递的唯一性。然后,节点的唯一性不在其内部属性中,而是在其父节点的外部“位置”或“顺序”中。当月份独特时,本地唯一模式(month)-[:locally_unique_rel]->(day)对于更广泛的范围而言是唯一的,并且月份是唯一的,不是通过属性和标签,而是在其年份的“订单”或“位置”的外部。因此传递性。我认为这是使用图形进行建模的强项,除此之外,它还允许您继续对结构进行分区。例如,如果您想将部分时间分为上午和下午或几小时,则可以轻松完成。

因此,在您的图表中,[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)在本地是唯一的,因此11月的日期在该年份也是唯一的 - (fourth)节点在

[11]->(november)-[4]->(fourth)

[12]-(december)->[4]->(fourth)

这相当于将更多的语义模型转移到您的关系中,留下用于存储数据的节点。您发布的图片中的节点标识符只是教学法,用x,y,z替换它们或空括号可能更好地揭示图形的结构或脚手架。

如果要保持关系类型不变,则为每个关系添加一个排序属性以创建类似(november)-[:HAS_DAY {order:4}]->(4th)的模式也可以。这可能不太适合查询,但您可能还有其他值得关注的问题。

答案 2 :(得分:0)

此代码允许您在特定日期创建活动时按需创建日历图表。您需要修改它以允许多天的事件,但似乎更像是您的问题是创建独特的路径,对吧?并且您可能希望修改它以使用您选择的语言中的参数。

首先我创建根目录:

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]-()

多次调用时创建这样的图形:

enter image description here

这有帮助吗?