0

我有以下列表:

在此处输入图像描述

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

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

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

4

1 回答 1

2

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

为了便于说明,我将在 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 与 RESTful 包装器(如neography)配对,提供了您需要的 Rails 和 Neo4j 之间的集成。如果您想创建自定义 RESTful 端点,Neo4j 还提供创建非托管扩展的能力。

于 2012-12-04T17:50:57.237 回答