这个树形结构更适合使用图形数据库还是关系型数据库?

3

我对图数据库很陌生,需要针对这种情况提出建议。我有一些人喜欢某些类别(只有树上的叶子节点),其他节点(父节点)不会被“喜欢”。然后,我计算特定用户与其他用户之间所有连接的分数。这是一个图形示例:

enter image description here

我尝试使用neo4j,对于非常小的数据集没有问题,但是我担心在有大量用户时性能会受到影响。我使用以下查询进行测试:

MATCH (n:Person)-[:LIKES*]->()-[r:SUB*0..4]-()<-[:LIKES*]-(m:Person)
WHERE n.name='Gabriel' and n<>m
WITH n.name as user, m.name as connection, 1.0/(length(r)+1)*count(r) as score
RETURN user, connection, sum(score)

我也考虑过在关系数据库中实现,为类别喜好保存3个字段(cat1,cat2,cat3),然后进行3个自连接,在不同的类别级别上查找匹配项。类似这样的操作(从用户1开始尝试与其他用户匹配):

select l2.user_id, sum(
case 
    when (l1.cat2 = l2.cat2 and l1.cat3 = l2.cat3) then 1
    when (l1.cat2 = l2.cat2) then 0.25 
    else 0.05 
end)
from likes l1
inner join likes l2 on l1.cat1 = l2.cat1 and l2.user_id <> 1
where l1.user_id = 1 
group by l2.user_id

但我也读到了应该避免进行自连接。

我想澄清一下,我关注的是阅读性能,写入并不重要。 我的目标是让它在每个用户有10个喜欢的情况下,能够很好地处理100万个用户。 我听取任何意见,谢谢!


你好@diez-gabriel,这是一个非常好的问题,也在这里稍微探讨了一下:https://stackoverflow.com/questions/29629903/graph-database-performance。也许你可以告诉大家更多关于数据预期大小的信息? - TsTeaTime
@TsTeaTime,感谢您的文章,让我明确了需要从子图开始。我已经编辑了帖子,并提供了期望的大小数据。 - Diez Gabriel
关于您的Cypher代码:1.除非Person节点可能具有连续的出站LIKES关系链,否则应使用[:LIKE]而不是[:LIKE*]。2.由于(根据您的数据模型)查询不应为给定的一对Person节点多次返回相同的r列表,因此聚合值 count(r) 很可能始终为1。 - cybersam
@cybersam 你说得对,必须是[:LIKES]而不是*。另一方面,count(r)可以不等于1。例如,如果(Gabriel)-[:LIKES]->(BocaJuniors),那么Gabriel到Raul或Juana的长度为4的路径有两条。 - Diez Gabriel
你对第二点是正确的 - 我在想 COUNT(DISTINCT r)。但使用 COUNT(*) 可能更清晰。 - cybersam
@cybersam 谢谢!我会记住的。我现在在考虑,更接近 SQL (失去一些灵活性),只需要一个 Join 和用 Case 解决计算问题。 - Diez Gabriel
1个回答

1

简而言之,我认为关系型数据库更好,因为你正在考虑一件事与另一件事的关系,例如一个人有多少个团队的喜欢。你可以轻松地更新用户、团队或运动的元数据,而不用担心会破坏你的分析查询。此外,你可以轻松扩展添加诸如大学、高中等体育类型,而不必担心之前的设置可能会出问题。

但是,我必须承认我以前从未使用过图形数据库。 :)


进行关系型数据库可能会像这样:

我喜欢称之为信息表,因为它们仅提供有关特定项目的信息,有些人也称其为参考表: Sport(Sport_ID,Sport_Name,[等等] ...)运动名称如足球,篮球等 示例视图:

╔══════════╦════════════╦═════╗
║ Sport_ID ║ Sport_Name ║ ... ║
╠══════════╬════════════╬═════╣
║        1 ║ Football   ║ ... ║
║        2 ║ Basketball ║ ... ║
║     ...  ║ ...        ║ ... ║
╚══════════╩════════════╩═════╝

团队(Team_ID,Team_Name,Home_State,[等等] ...) - 团队将是所有团队,无论其所属的运动类型。

示例视图:
╔═════════╦═════════════╦═════╗
║ Team_ID ║  Team_Name  ║ ... ║
╠═════════╬═════════════╬═════╣
║       1 ║ Boca Junior ║ ... ║
║       2 ║ River Plate ║ ... ║
║       3 ║ Spurs       ║ ... ║
║     ... ║ ...         ║ ... ║
╚═════════╩═════════════╩═════╝ 

用户(用户ID,用户名,[等等]...)-- 所有用户特定信息都将在此处列出。示例视图:

╔═════════╦═════════════════╦═════╗
║ User_ID ║ User_First_Name ║ ... ║
╠═════════╬═════════════════╬═════╣
║       1 ║ Mario           ║ ... ║
║       2 ║ Gabriel         ║ ... ║
║       3 ║ Juana           ║ ... ║
║       4 ║ Raul            ║ ... ║
║     ... ║ ...             ║ ... ║
╚═════════╩═════════════════╩═════╝

然后,您需要创建关系表来建立体育运动、团队和用户之间的联系。
Sports_Team(Sport_ID,Team_ID)- 在这里,您需要展示哪个团队参加了哪项运动。 示例视图:
╔══════════╦═════════╦═════╗
║ Sport_ID ║ Team_ID ║ ... ║
╠══════════╬═════════╬═════╣
║        11 ║ ... ║
║        12 ║ ... ║
║        23 ║ ... ║
║      ... ║     ... ║ ... ║
╚══════════╩═════════╩═════╝

团队_用户_喜欢 (团队_ID, 用户_ID) -- 在此处,您需要展示哪些人喜欢哪些参与某项运动的团队。 示例视图:
╔═════════╦═════════╦═════╗
║ Team_ID ║ User_ID ║ ... ║
╠═════════╬═════════╬═════╣
║       11 ║ ... ║
║       22 ║ ... ║
║       23 ║ ... ║
║       33 ║ ... ║
║     ... ║     ... ║ ... ║
╚═════════╩═════════╩═════╝

现在,您只需执行以下操作即可获得用户喜欢的团队数量得分:
SELECT tul.User_ID
     , COUNT(tul.Team_ID) AS Likes
  FROM team_user_likes tul
 GROUP
    BY tul.User_ID

如果您想要用户元数据,比如他们的姓名,您可以将此查询投入到CTE中,然后使用用户表连接到CTE表。
这可能看起来很复杂,但它会使编辑/更新用户/团队/运动信息变得更加容易。您将能够做一些有趣的分析,比如使用喜欢的数据计算有多少用户喜欢/偏爱某个运动,而不必担心影响关系表,或者每种运动的哪个团队是最受欢迎的。
此外,这应该很容易扩展,具体取决于您使用的关系数据库。假设您想开始添加高中、大学等体育项目,您只需添加一个sport_type表,然后创建一个sport_sport_type关系表来建立哪些运动是职业运动或其他运动之一的联系。这样,您就可以按运动类型进行分析,而不必担心它如何影响您以前的设置。
我更喜欢关系数据库,因为它们似乎使事情更整洁。话虽如此,我从未使用过图形数据库。但考虑到您正在查看一件事与另一件事的关系,例如一个人喜欢多少个团队,我的意见是您应该选择关系数据库。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接