由于一个节点可以有多个父节点,因此这是一个图,而不是树。虽然在关系数据库中有处理图形的模式,但我发现它们很麻烦。这就是图数据库大放异彩的地方。
为了便于说明,我将在 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 还提供创建非托管扩展的能力。