如何使用图数据库进行声誉扩散?

时间:2016-09-19 10:35:05

标签: neo4j orientdb graph-databases tinkerpop blazegraph

我有一个似乎非常适合图形数据库的问题,但我不确定应用它的最佳方法。

首先,有一组对象可以有方向链接(几千万个,典型的链接输入/输出数量是每个对象几千)。然后,每个对象可以从潜在的大量用户(也是数千万)中积累声誉(想想upvotes,业力等)。

棘手的部分是,无论何时用户调整对象的声誉,我都希望根据一些相当复杂的规则更新所有链接对象的声誉(可能超出一级)。

在SQL中,这看起来像这样:

CREATE TABLE objects (id INTEGER PRIMARY KEY);
CREATE TABLE object_links (from_object_id INTEGER, to_object_id INTEGER);
CREATE TABLE users (id INTEGER PRIMARY KEY);
CREATE TABLE object_reputations (object_id INTEGER, user_id INTEGER, reputation FLOAT);

UPDATE
    object_reputations
SET
    object_reputations.reputation = object_reputations.reputation + ... # some formula goes here
FROM
    object_reputations
    INNER JOIN object_links
        ON object_reputations.object_id = object_links.to_object_id
WHERE
    object_links.from_object_id = ...;

由于这是处理图形,图形数据库看起来很自然,但是通过快速阅读Neo4j / OrientDB / Blazegraph / Tinkerpop API,我无法弄清楚如何将这个问题映射到他们可以做些什么。

以Tinkerpop为例,对象是Vertexes,对象之间的链接是Edges(到目前为止都很好),声誉是......?可能是VertexPropetries,但我不确定事情是如何扩展的,每个顶点可能有多少属性与用户一样多。或者声誉可能是来自用户顶点的加权边缘......这似乎会产生不同类型的性能问题。

您能否将这类问题简单地翻译成一个流行的图形数据库?

2 个答案:

答案 0 :(得分:2)

我会说这实际上取决于想要查询数据的方式。如果信誉具有有限数量的值并且值在用户之间重复,则信誉也可以是顶点。例如,如果它是1-10的数字,那么我们可以让所有声誉为7的用户链接到此顶点。此模型允许您从顶点开始查询,并轻松找到具有该声誉的所有用户。使用Gremlin,它会是这样的。

Link

这将返回链接到声誉顶点的所有顶点,声誉为“7”。

或者,您也可以将声誉作为属性,并且可以查找具有此类属性的所有顶点。

<Link to={`/stuff/${this.state.foo}`}/>

属性数量应该不是问题。 Titan建议您索引要查询的属性,以便更好地改进查找

答案 1 :(得分:1)

您希望始终尝试在不使用任何大表的情况下可视化图形数据查询(基本上,每个顶点超过2或3个属性的任何内容应该几乎专门用于数据存储,而不是查询)。如果您无法将这些复杂数据更改为由顶点之间的较长路径表示,则它可能属于关系数据库。

对于像您这样的“基于视角”的数据,图形数据库非常适合。但是,不要将所有面向用户的声誉存储为Object上的属性,而是将它们移动到连接到对象的单独节点上,以便它们可以转换为从User到Object的路径的一部分。

因此,你有一个User的顶点,一个Object的顶点,每个顶点都有一个到第三个顶点的边,即ObjectReputation。每个Object都有几个相邻的ObjectReputation顶点(每个User对应一个User),但是从任何User到任何Object的边缘只有一条路径。要查找相关的ObjectReputations,您可以沿着用户到对象的边缘导航,沿对象之间的边缘导航,然后从这些对象导航回UserReputation顶点到原始用户。

neo4j的Cypher查询语言中,它看起来像这样:

MERGE (u:User {id:1})
MERGE (o:Object {id:2})
MERGE (u) - [:KNOWS] -> (ur:ObjectReputation) - [:KNOWS] -> (o)
SET ur.score = 100
MATCH (o) - [:RELATED_TO*] - (:Object) <- [:KNOWS] - (related_ur:ObjectReputation) <- [:KNOWS] - (u)
SET related_ur.score = related_ur.score * 1.2