正确的数据模型取决于您需要进行的查询类型。您应该弄清楚您的查询是什么,然后确定满足这些条件的数据模型:
- 它可以回答您所有的疑问,
- 它允许您的查询足够快地完成,
- 它最大限度地减少了所需的数据库存储。
对于讨论评论,您可能想要查询按时间顺序排列的讨论线程。因此,您不仅需要存储发表评论的时间,还需要存储评论之间的关系(因为讨论可能会产生不相交的线程,这些线程不共享相同的先前评论)。
让我们尝试一个简单的测试用例。假设有 2 个不相交的线程由同一个初始注释(我们将其称为c1
): [c1, c3] 和 [c1, c2, c4]。假设在这个简单的测试用例中,我们只对查询与某个主题相关的所有评论线程感兴趣。
如果评论属性存储在节点中,则数据可能如下所示:
(u1:User {name: "A"})-[:MADE]->(c1:Comment {time:0, text: "Fee"})-[:ABOUT]->(s1:Subject {title: "Jack"})
(u2:User {name: "B"})-[:MADE]->(c2:Comment {time:1, text: "Fie"})-[:ABOUT]->(c1)
(u3:User {name: "C"})-[:MADE]->(c3:Comment {time:3, text: "Foe"})-[:ABOUT]->(c1)
(u4:User {name: "D"})-[:MADE]->(c4:Comment {time:9, text: "Fum"})-[:ABOUT]->(c2)
如果您将评论属性存储在关系中,您可能会尝试类似以下的操作,但存在一个很大的缺陷。一个关系无法直接指向另一个关系(正如我们在第 2 行到第 4 行中尝试做的那样)。由于该模型在 Neo4j 中不合法,因此它无法满足上述任何标准。
(u1:User {name: "A"})-[c1:COMMENTED_ABOUT {time:0, text: "Fee"}]->(s1:Subject {title: "Jack"})
(u2:User {name: "B"})-[c2:COMMENTED_ABOUT {time:1, text: "Fie"}]->(c1)
(u3:User {name: "C"})-[c3:COMMENTED_ABOUT {time:3, text: "Foe"}]->(c1)
(u4:User {name: "D"})-[c4:COMMENTED_ABOUT {time:9, text: "Fum"}]->(c2)
因此,在我们的简单测试用例中,将属性存储在节点中似乎是唯一的选择。
这是一个获取不相交线程路径的查询,包括发表每个评论的用户(WHERE
子句过滤掉部分线程):
MATCH p=(s:Subject)<-[:ABOUT*]-(c:Comment)<-[m:MADE]-(u:User)
WHERE NOT (c)<-[:ABOUT]-()
RETURN p