我有一个图形模型,它包含三种类型的顶点(用户,组,文档)和两种类型的边(member_of,permissions)。关系可以表示为:
User,Group --- member_of ---> Group (depth can be arbitrary)
Group --- permissions ---> Document (depth is 1)
我正在编写一个查询,回答“所有没有任何文档权限的用户是什么?”。这是一个非选择性的查询,因为我没有为User类指定id。
我想出了这个解决方案:
SELECT id, name FROM User
LET $p = (
SELECT expand(outE('permissions')) FROM (
TRAVERSE out('member_of') FROM $parent.$current
)
)
WHERE $p.size() = 0
此解决方案似乎有效,但需要在 12-15秒之间执行。目前在我的图表中,每个都有10,000个用户,组和文档。有大约10,000个权限和~50,000个member_of。
检查路径不存在的最有效方法是什么?有没有办法改善现有查询的性能,还是我采取了错误的方法?
答案 0 :(得分:1)
有几种方法可以改善您的查询。首先,没有必要扩展Permissions边缘,您可以简单地检查查询中存储的边缘量。我们还可以限制此检查,以便它在具有权限边缘的第一组停止,而不是全部检查(归功于Luigi D给我这个想法)。因此查询变为如下。
SELECT * FROM User
LET $p = (
SELECT FROM (
TRAVERSE out('Member_Of') FROM $parent.$current
) WHERE out('Permissions').size() > 0 LIMIT 1
)
WHERE $p.size() = 0
如果没有相当大的数据集,我很难检查任何查询改进,但通过使用更明确的out_Member_Of和out_Permissions属性而不是out(字段)函数可能会有一点改进。
可能还有另一个机会通过从遍历结果中“删除”用户记录来略微改进查询,从而减少WHERE子句检查的记录数量。这可以通过
完成SELECT * FROM User
LET $p = (
SELECT FROM (
TRAVERSE out('Member_Of') FROM (SELECT out('Member_Of') FROM $parent.$parent.$current)
) WHERE out('Permissions').size() > 0 LIMIT 1
)
WHERE $p.size() = 0
之前的查询也可以重新排列,虽然我怀疑这个查询会因为检查所有遍历的结果而变慢,而不是在第一次停止。这只是你尝试的另一种选择。
SELECT * FROM User
LET $p = (TRAVERSE out('Member_Of') FROM (SELECT out('Member_Of') FROM $parent.$current))
WHERE $p.out('Permissions').size() = 0
现在我将偏离该查询。如果一个组有权访问文档,那么预计算会更快,然后使用预先计算的用户组检查每个用户组。这可以节省大量的重复遍历。
我认为最好的方法是让所有群组都没有文档。这样,所有具有docs的组都可以在遍历其他组之前被删除。
SELECT * FROM (SELECT FROM Group WHERE out('Permissions').size() = 0)
LET $p = (
SELECT FROM (
TRAVERSE out('Member_Of') FROM $parent.$current
) WHERE out('Permissions').size() > 0 LIMIT 1
)
WHERE $p.size() = 0
也许创建和使用索引会使之前的查询更加高效,尽管这个过程目前看起来有点笨拙。在为out_Permissions创建索引之前,需要使用create property Group.out_Permissions LINKBAG
创建属性,然后可以使用CREATE INDEX hasDocument ON Groups (out_Permissions, @rid) notunique METADATA {ignoreNullValues: false}
创建索引(以这种方式创建索引似乎很奇怪,但这是唯一的方法我可以让它工作,因此我的评论很棒)。然后,您可以使用select expand(rid) from index:hasDocument where key = null
查询索引,这将返回没有权限边缘的所有组,并且将替换上一个查询中的SELECT FROM Group WHERE out('Permissions').size() = 0
。
所以这里是获取带有docs的组的查询,并检查用户对它的反应。它也正确地返回没有组的用户。
SELECT expand($users)
LET $groups_without_docs = (
SELECT FROM (SELECT FROM Group WHERE out('Permissions').size() = 0)
LET $p = (
SELECT FROM (
TRAVERSE out('Member_Of') FROM $parent.$current
) WHERE out('Permissions').size() > 0 LIMIT 1
)
WHERE $p.size() = 0
),
$users = (
SELECT FROM User
LET $groups = (SELECT expand(out('Member_Of')) FROM $current)
WHERE $groups containsall (@rid in $parent.$groups_without_docs)
)
注意我认为$users = (SELECT FROM User WHERE out('Member_Of') containsall (@rid in $parent.$groups_without_docs))
应该有效,但事实并非如此。我认为这可能与我之前发布的错误有关,请参阅https://github.com/orientechnologies/orientdb/issues/4692。
我很想知道上面的各种查询是否会改善您的查询,所以请回复。
答案 1 :(得分:0)
正如你所说,这是一个非选择性的查询,所以很难优化。
您是否尝试过向内部查询添加LIMIT?
SELECT id, name FROM User
LET $p = (
SELECT expand(outE('permissions')) FROM (
TRAVERSE out('member_of') FROM $parent.$current
) LIMIT 1
)
WHERE $p.size() = 0
甚至
SELECT id, name FROM User
LET $p = (
SELECT sum(outE('permissions').size()) as s FROM (
TRAVERSE out('member_of') FROM $parent.$current
)
)
WHERE $p[0].s = 0