我刚刚进入图形数据库,我似乎在使用“索引节点”或“索引属性”来跟踪诸如“节点类型”之类的事情之间继续遇到问题。由于到目前为止我没有真正的经验,我没有任何信息可以作出决定,这两种方法似乎同样有效。
所以,问题是:两种方法之间的权衡是什么,以及规模(即节点数)如何影响决策?
对于示例场景,我们假设有两种类型的“事物”:User
和Product
,而用户节点和Product节点之间的边缘并不重要,但是我们关心的是,如果我们在每个节点上都需要type: User
和type: Product
属性,或者我们希望每个节点都有一个指向User
节点的边和一个Product
} node,分别。
在哪种情况下哪种方法更好?
注意:我特别关注Neo4j和Titan,但我认为这也会更普遍地应用。
答案 0 :(得分:18)
首先,您需要问自己:是否需要对顶点/节点的类型编制索引? I.e。您是否需要按类型检索顶点/节点,比如说,从图中检索所有“用户”顶点,或者您是否需要通过检索给定类型的所有顶点然后过滤/处理这些顶点来回答查询?
如果此问题的答案是是,那么我建议您将类型存储为索引的字符串属性。或者,如果您使用基于jvm的语言进行开发,则可以定义类型枚举,并将其用作属性类型,以进行更多类型安全和自动错误检查。 Titan支持任意用户定义的类/枚举作为属性类型,并将为低内存占用而压缩它们。
然而,这种方法的缺点是,由于您正在构建低选择性指数,因此无法扩展。这意味着可能会有很多类型为'user'或'product'的顶点,并且所有顶点都需要分别与'user'或'product'的索引条目相关联。这使得维护和查询这个索引非常昂贵且难以扩展(想象一下facebook有一个'类型'索引:'photo'条目下面会有数十亿个顶点)。如果你还没有(还)关注缩放,那么这可以工作。
如果问题的答案是否,那么我建议将类型建模为图中的顶点/节点。即有一个'用户'顶点和一个'产品'顶点和一个标记为'type'的边从每个用户到'user'顶点等。
这种方法的优点是您可以使用图形来建模数据,而不是在数据库之外使用字符串值来表示关键的类型信息。在构建应用程序时,图形数据库将成为其核心组件并持续很长时间。随着编程语言和开发人员的不断变化,您不希望数据建模和类型信息与他们一起使用,并且面临一个问题:“SPECIAL_USER是什么意思?”相反,有一个SPECIAL_USER顶点并向其添加起源信息,即创建此类型的人,它代表什么以及简短描述 - 所有这些都在数据库中。
这种方法的一个问题是,随着应用程序的扩展,“用户”和“产品”顶点会有很多边缘事件。换句话说,您正在创建创建缩放问题的超级节点。这就是Titan引入单向边缘概念的原因。单向边缘就像网络上的链接:起始顶点指向另一个顶点,但该顶点不知道边缘。由于您不希望从“用户”顶点遍历到所有用户顶点,因此除了获得可伸缩性和性能之外,您不会失去任何东西。
答案 1 :(得分:4)
您想问什么样的查询?在Neo4j中,您可以创建一个User
和一个Product
索引,甚至可以将它们组合在一起,然后能够提出类似
start bob = node:User(name='Bob') match ....
甚至全文搜索。为了便于检查节点是用户还是产品,您可以将属性保留在节点上,只是为了方便快捷的遍历。如果您没有从用户/产品遍历到实例节点(您对其进行索引查找),您甚至可以通过将PRODUCT
或USER
关系返回到该类型来进行检查(超级)节点,给你一个像
start s = node:User(name='Bob') match s-[r]-(product)-[typeRel:PRODUCT]->() return product
HTH
答案 2 :(得分:0)
这里错过了索引的一个非常重要的原因imo。假设您有一个包含许多不同属性和许多不同节点类型的复杂图形,并且您希望将模式与具有一堆属性的“人”匹配。
如果没有索引,则除了遍历图形之外别无选择,图形中可能只有0.01%的节点属于person类型。并且遍历可能无法到达图表中未连接的区域。
相反,如果我有索引的人,我只是遍历每个人,并在每个人周围搜索,看看他们的模式是否匹配。
您应该能够立即看到这些方法中的第一种方法与图表的总大小一致,但第二种方法只会根据图表中的总人数进行缩放。
道德:如果您设想一个用例,其中将有许多搜索图表,其中特定类型的节点作为您的模式中的绑定节点(例如,这里搜索“具有模式X的人”的大量搜索),那么您应该索引这些节点以提高搜索性能。
如果您要搜索诸如“Peter Peter的两个链接中的所有节点”之类的内容,那么按名称索引人员将是至关重要的,并且无论图表大小如何都可以保持恒定的时间性能 - 因为您实际上在查找彼得在哈希表中的位置。