【问题标题】:How use Cypher with Merge to create a unique sub graph path如何使用 Cypher 和 Merge 创建唯一的子图路径
【发布时间】:2014-01-29 01:02:04
【问题描述】:

在 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();

问候 基兰

【问题讨论】:

  • 出于兴趣,但与问题无关,RunEntityHousekeeping 是做什么的?
  • @TathamOddie :嗯,这是我在插入之前需要做的一些常见任务,比如使用你的造雪机设置内部 ID 并暂时定义标签等,直到我想出一个更好的解决方案。尝试解决一个问题,即每次我尝试调用 MERGE 时我都会丢失 snowmaker 上的索引,因为我不确定该对象是否会在查询期间实际创建或与现有对象合并。
  • 澄清点:你真的想把日子和[:NEXT]关系联系起来吗?例如,看起来事件 2 实际上与 2010 有两次关联:(year 2010)--&gt;(month 12)--&gt;(day 31)-[:NEXT]-&gt;(day 1)--&gt;(event 2)(year 2010)--&gt;(month 12)--&gt;(day 31)--&gt;(event 2)。我了解您希望事件在多天发生,但我觉得您的 :NEXT 关系可能会产生奇怪的副作用,具体取决于您的查询。例如,(事件 3)将出现在 2010 年以下的(伪)查询 (month 12)-[*]-(:Event) 中,这可能不是您的意图。

标签: neo4j cypher neo4jclient


【解决方案1】:

the documentation page 上没有任何地方说 CREATE UNIQUE 已被弃用。

MERGE 只是一种可供您使用的新方法。它支持一些新的场景(基于标签的匹配,以及ON CREATEON MATCH 触发器),但也没有涵盖更复杂的场景(超过单个节点)。

听起来您已经熟悉CREATE UNIQUE。目前,我认为你应该仍然使用它。

【讨论】:

  • 这是 Peter Neubauer 在我之前的一篇帖子here 中提出的建议,可能是我弄错了他的答案。同样在linkMichael Hunger 中似乎也有同样的建议。
  • 它没有在docs.neo4j.org/chunked/milestone/deprecations.html 中列出,所以我认为它仍然会在 2.0GA 左右
  • MERGE 将取代 CREATE UNIQUE。它尚未正式弃用,但 2.0 版本将具有 MERGE w/ 关系,以及更强大的 ON MATCH/ON CREATE 触发器。很快!
  • 作为参考,在 从 1.9 更新到 2.0 部分中,Cypher 2.0 Refcard 为:MERGE will take CREATE UNIQUE's role for the unique creation of patterns. Note that they are not the same, though.
  • 这作为一个长期策略很棒,但在它还没有涵盖足够多的场景的情况下,CREATE UNIQUE 仍然有效。
【解决方案2】:

在我看来,您希望图表的外观具有由关系强加的顺序,但您的代码使用节点对顺序进行建模。如果您想要该图,则需要使用 [2010][2011] 之类的关系类型,而不是 [HAS_YEAR]-&gt;({value:2010}) 之类的模式。

说同样的事情的另一种方式:您试图通过标签和属性的组合从本质上构成节点的唯一性,例如(unique:Day {value:4})。假设您有相关的限制,这将是数据库范围内的唯一性,因此所有月份只有一个月的第四天可以共享。您想要的是外在的本地唯一性,即通过关系层次结构可传递地建立和扩展的唯一性。节点的唯一性不在于其内部属性,而在于其相对于其父节点的外部“位置”或“顺序”。本地唯一模式(month)-[:locally_unique_rel]-&gt;(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})  

不同之处在于,通过前一种外在的局部唯一性,您可以享受传递性的好处。由于月份在一年中是唯一的,因为 [11]-&gt;(november) 中的 (november) 在本地是唯一的,因此 11 月的日子在当年也是唯一的 - (fourth) 节点之间是不同的

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

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

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

如果您想保持关系类型不变,向每个关系添加排序属性以创建像(november)-[:HAS_DAY {order:4}]-&gt;(4th) 这样的模式也可以。这对于查询可能性能较差,但您可能还有其他值得考虑的问题。

【讨论】:

    【解决方案3】:

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

    首先我创建根:

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

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

    这有帮助吗?

    【讨论】:

      猜你喜欢
      • 2014-12-01
      • 2016-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多