【问题标题】:Validation for a list with multiple parents and multiple children (graph)验证具有多个父母和多个孩子的列表(图表)
【发布时间】:2012-12-03 12:52:51
【问题描述】:

我有以下清单:

是一种多对多递归关联,其中一个类别(计算机、Mac、PC 等)可以有许多其他类别。一个类别(如光驱)也可以属于 PC 和 Mac。但是“光驱”不应允许将类别作为“PC”类别,因为它将有一个无限循环。

我想知道如何在 Rails 中创建上述验证并进行优化?我应该检查所有父类别吗?但是如果有很多嵌套类别(例如 200 个深度的子类别),这将导致许多 sql 查询。

另一个想法是在数据库中创建一个深度列,并在父类别不允许作为子类别的情况下进行验证,该类别的深度小于其自身的深度。但这不会让没有共同父母的类别进入另一个深度。例如,在上面的列表示例中,光驱可以作为 Mac 的一个类别,即使它处于上述深度级别。但是由于无限循环,它无法拥有PC。

【问题讨论】:

    标签: mysql ruby-on-rails database validation graph-databases


    【解决方案1】:

    由于一个节点可以有多个父节点,因此这是一个图,而不是一棵树。虽然有patterns 来处理关系数据库中的图形,但我发现它们很麻烦。这就是图数据库大放异彩的地方。

    为了便于说明,我将在 Gremlin 中向您展示您的问题的潜在解决方案,Gremlin 是一种通过插件在 Neo4j 中运行的图形遍历语言,可通过 Neo4j REST API 获得。

    Neo4j 中包含的 Gremlin 的最新版本是 Gremlin 1.5。你可以从Github下载这个版本来试用这个代码:

    g = new Neo4jGraph('/tmp/so')
    
    // add all vertices
    categories = ['Computers', 'Mac', 'PCs', 'Hard Disks', 'Memory', 'Graphic Cards',
    'Optical Drives', 'DVD-Reader', 'DVD-RW', 'Blue Ray', 'Blue Ray / DVD Combo']
    
    categories.each { x -> g.addVertex(['name':x]) }
    
    // show all of the vertices we just created with their properties
    g.V.map
    ==>{name=Computers}
    ==>{name=Mac}
    ==>{name=PCs}
    ==>{name=Hard Disks}
    ==>{name=Memory}
    ==>{name=Graphic Cards}
    ==>{name=Optical Drives}
    ==>{name=DVD-Reader}
    ==>{name=DVD-RW}
    ==>{name=Blue Ray}
    ==>{name=Blue Ray / DVD Combo}
    
    // for ease of this example, create a lookup table of these vertices
    // in a production system, you would look up vertices in a Lucene index
    i = [:]
    g.V.transform {i[it.name] = it}
    
    // create edges representing one category 'in' another
    g.addEdge(i['Mac'], i['Computers'], 'in')
    g.addEdge(i['PCs'], i['Computers'], 'in')
    
    // PCs subgraph
    g.addEdge(i['Hard Disks'], i['PCs'], 'in')
    g.addEdge(i['Memory'], i['PCs'], 'in')
    g.addEdge(i['Graphic Cards'], i['PCs'], 'in')
    
    // optical drives subgraph
    g.addEdge(i['Optical Drives'], i['PCs'], 'in')
    g.addEdge(i['DVD-Reader'], i['Optical Drives'], 'in')
    g.addEdge(i['DVD-RW'], i['Optical Drives'], 'in')
    g.addEdge(i['Blue Ray'], i['Optical Drives'], 'in')
    g.addEdge(i['Blue Ray / DVD Combo'], i['Optical Drives'], 'in')
    
    // adding the optical drive subgraph to Mac is a one-liner
    g.addEdge(i['Optical Drives'], i['Mac'], 'in')
    
    // show the names of all vertices in the paths from Computers to child nodes three-levels down
    i['Computers'].in.in.in.paths {it.name}
    ==>[Computers, PCs, Optical Drives, Blue Ray / DVD Combo]
    ==>[Computers, PCs, Optical Drives, Blue Ray]
    ==>[Computers, PCs, Optical Drives, DVD-RW]
    ==>[Computers, PCs, Optical Drives, DVD-Reader]
    ==>[Computers, Mac, Optical Drives, Blue Ray / DVD Combo]
    ==>[Computers, Mac, Optical Drives, Blue Ray]
    ==>[Computers, Mac, Optical Drives, DVD-RW]
    ==>[Computers, Mac, Optical Drives, DVD-Reader]
    
    g.shutdown()
    

    Gremlin 可能需要一些时间来适应,因为它是一种函数式编程方法,但是一旦你掌握了它,它就会非常强大。

    但“光驱”不应允许将类别作为“PC”类别,因为它会产生无限循环。

    这种验证可以通过寻路来处理。如果存在从当前顶点到子顶点的路径,则不允许创建边。 Neo4j 包含一个Pathfinder API 以方便进行此类搜索。

    由于您在 Rails 中,您可能会发现 Neo4j 与 neography 之类的 RESTful 包装器配对提供了您需要的 Rails 和 Neo4j 之间的集成。如果您想创建自定义 RESTful 端点,Neo4j 还提供创建 unmanaged extensions 的功能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多