我有一个Item节点,它是POSTED_BY一个User节点。此外,项目可以由零个或多个用户节点组成,零个或多个注释可以在项目节点上POSTED_IN。
我需要做的是查询特定的Item节点(给定id),然后检索:
如果我尝试仅查询1-3,并且暂时排除4,我的查询看起来像这样:
MATCH (i:Item {id: 'ByvIzUdbZ'})-[:POSTED_BY]->(u:User)
OPTIONAL MATCH (u2:User)-[:LIKES]->(i)
RETURN i, u, COUNT(u2);
这将正确返回Item节点,User和LIKES计数。
现在,为了适应第4点,我修改了查询以添加另一个OPTIONAL MATCH,如下所示:
MATCH (i:Item {id: 'ByvIzUdbZ'})-[:POSTED_BY]->(u:User)
OPTIONAL MATCH (u2:User)-[:LIKES]->(i)
OPTIONAL MATCH (comment:Comment)-[:POSTED_IN]->(i)
RETURN i, u, COUNT(u2), COUNT(comment);
然而,有了这个,用户(u2)和评论(评论)的计数都是不正确的。他们似乎正在“加起来”。所以,如果Likes实际上是3,喜欢和评论计数都返回为6.任何提示我在这里做错了什么?感谢。
答案 0 :(得分:1)
您正在看到cartesian products的效果。
如果MATCH
子句在其自己的上生成的行数为N,但是已存在的行数(例如,来自之前的{{1 }}子句是M,然后你得到M * N行。
为了避免这种乘法效应(也会对性能和内存产生不利影响),您可以在适当的条款后立即执行aggregation。例如,在您的情况下:
MATCH
答案 1 :(得分:0)
Cybersam很好地解释了为什么你会看到你所看到的。请记住,基数(行数)也会影响查询中的操作(例如MATCH和OPTIONAL MATCH)的运行次数。
例如,在您的第一个MATCH中,如果10个用户发布了相同的项目,那么您将有10个行中的每一个与10个用户中的每一个组合。
然后OPTIONAL MATCH执行,虽然你的意思是它只对每个项目执行一次(你试图获得每个项目的类似计数),它将在同一个项目上执行10次,因为有10行那个项目。这是额外的工作,您可以通过在执行匹配之前尝试将项目计数到一行来避免,或者通过聚合用户,或者在匹配用户之前获取每个项目所需的数据。
这是一个可能更好的示例查询,将匹配保存到用户直到结束,为每个可选匹配保持基数为1。
MATCH (i:Item {id: 'ByvIzUdbZ'}) // one row
OPTIONAL MATCH (u2:User)-[:LIKES]->(i)
WITH i, COUNT(u2) AS u2_count // back to 1 row
OPTIONAL MATCH (comment:Comment)-[:POSTED_IN]->(i)
WITH i, u2_count, COUNT(comment) AS comment_count; // back to one row again
MATCH (i)-[:POSTED_BY]->(u:User)
RETURN i, u, u2_count, comment_count