Arangodb AQL 过滤器不在集合中,非常慢
Arangodb AQL Filter NOT IN collection, very slow
我想找到一组没有配置文件的用户。
ArangoDB 2.4.3
LENGTH(users) -> 130k
LENGTH(profiles) -> 110k
users.userId -> unique hash index
profiles.userId -> unique hash index
我做的这个AQL片段比盛夏穿越大峡谷的蜗牛还慢。
LET usersWithProfiles = ( /* This part is ok */
FOR i IN users
FOR j IN profiles
FILTER i.userId == j.userId
RETURN i
)
LET usersWithoutProfiles = ( /* This is not */
FOR i IN usersWithProfiles
FILTER i NOT IN users
RETURN i
)
RETURN LENGTH(usersWithoutProfiles)
我很确定有一种非常明智的方法可以正确地做到这一点,但我错过了。有什么想法吗?
编辑 1(在@dothebart 的回复后):
这是新的查询,但还是很慢
LET userIds_usersWithProfile = (
FOR i IN users
FOR j IN profile
FILTER i.userId == j.userId
RETURN i.userId
)
LET usersWithoutProfiles = (
FOR i IN users
FILTER i.userId NOT IN userIds_usersWithProfile
RETURN i
)
RETURN LENGTH(usersWithoutProfiles)
性能不佳的原因是它无法为您的操作使用索引,因为它需要对集合中的每个文档进行全面比较。
您可以使用 explain https://www.arangodb.com/2015/02/02/arangodb-2-4-2 实用程序让 arangodb 告诉您查询的费用在哪里。
您的查询可能不会达到您的预期。 usersWithoutProfiles 将为空,因为任何具有 Profile 的用户都可以在 users 集合中找到。如果你想拥有用户集合的另一部分,它可能看起来像这样:
LET usersWithProfiles = ( /* This part is ok */
FOR i IN users
FOR j IN profiles
FILTER i.userId == j.userId
RETURN i
)
/* now we pick the IDs, we could have done that in your first query... */
LET userWithProfilesIds = FOR i IN userWithProfiles RETURN i.userId;
/* now filter the user list by that */
LET usersWithoutProfiles = FOR i IN users
FILTER i.userId NOT IN userWithProfileIds
RETURN i;
RETURN LENGTH(usersWithoutProfiles)
应该会给你一个正确的结果。
另请注意,这部分原始查询非常昂贵:
LET usersWithoutProfiles = (
FOR i IN usersWithProfiles
FILTER i NOT IN users
RETURN i
)
原因是 FILTER
使用 users
,此时它是一个将集合中的所有文档构建为数组的表达式。
我建议使用此查询,而不是使用此查询,它将 return 没有关联配置文件记录的用户的 _key
属性:
FOR user IN users
LET profile = (
FOR profile IN profiles
FILTER profile.userId == user.userId
RETURN 1
)
FILTER LENGTH(profile) == 0
RETURN user._key
我想找到一组没有配置文件的用户。
ArangoDB 2.4.3
LENGTH(users) -> 130k
LENGTH(profiles) -> 110k
users.userId -> unique hash index
profiles.userId -> unique hash index
我做的这个AQL片段比盛夏穿越大峡谷的蜗牛还慢。
LET usersWithProfiles = ( /* This part is ok */
FOR i IN users
FOR j IN profiles
FILTER i.userId == j.userId
RETURN i
)
LET usersWithoutProfiles = ( /* This is not */
FOR i IN usersWithProfiles
FILTER i NOT IN users
RETURN i
)
RETURN LENGTH(usersWithoutProfiles)
我很确定有一种非常明智的方法可以正确地做到这一点,但我错过了。有什么想法吗?
编辑 1(在@dothebart 的回复后):
这是新的查询,但还是很慢
LET userIds_usersWithProfile = (
FOR i IN users
FOR j IN profile
FILTER i.userId == j.userId
RETURN i.userId
)
LET usersWithoutProfiles = (
FOR i IN users
FILTER i.userId NOT IN userIds_usersWithProfile
RETURN i
)
RETURN LENGTH(usersWithoutProfiles)
性能不佳的原因是它无法为您的操作使用索引,因为它需要对集合中的每个文档进行全面比较。
您可以使用 explain https://www.arangodb.com/2015/02/02/arangodb-2-4-2 实用程序让 arangodb 告诉您查询的费用在哪里。
您的查询可能不会达到您的预期。 usersWithoutProfiles 将为空,因为任何具有 Profile 的用户都可以在 users 集合中找到。如果你想拥有用户集合的另一部分,它可能看起来像这样:
LET usersWithProfiles = ( /* This part is ok */
FOR i IN users
FOR j IN profiles
FILTER i.userId == j.userId
RETURN i
)
/* now we pick the IDs, we could have done that in your first query... */
LET userWithProfilesIds = FOR i IN userWithProfiles RETURN i.userId;
/* now filter the user list by that */
LET usersWithoutProfiles = FOR i IN users
FILTER i.userId NOT IN userWithProfileIds
RETURN i;
RETURN LENGTH(usersWithoutProfiles)
应该会给你一个正确的结果。
另请注意,这部分原始查询非常昂贵:
LET usersWithoutProfiles = (
FOR i IN usersWithProfiles
FILTER i NOT IN users
RETURN i
)
原因是 FILTER
使用 users
,此时它是一个将集合中的所有文档构建为数组的表达式。
我建议使用此查询,而不是使用此查询,它将 return 没有关联配置文件记录的用户的 _key
属性:
FOR user IN users
LET profile = (
FOR profile IN profiles
FILTER profile.userId == user.userId
RETURN 1
)
FILTER LENGTH(profile) == 0
RETURN user._key