我有一个约会应用程序,我将所有潜在的Match对象存储在MongoDB中(当用户向左或向右滑动时,会发生Match对象):
{
uid1: <userid1>,
uid2: <userid2>,
uid1action: <L|R|E> (left/right/empty, based what the user1 has done),
uid2action: <L|R|E> (left/right/empty, based what the user2 has done),
}
现在是我的问题。当我向user1显示潜在用户的个人资料时,我考虑了所有已经喜欢user1的人(因为我将这些个人资料的优先级确定):
var likedQuery = Parse.Query.or(new Parse.Query("Match")
.equalTo("uid1", userId)
.equalTo("u2action", "L")
.equalTo("u1action", "E") // user1 has not done anything
.select("uid2")
.limit(paginationLimit);
现在这很好,一切正常。我现在还希望按照每个用户的喜欢程度(受欢迎程度)来对LikedQuery进行排序。
说这些是喜欢user1的以下用户:
保罗(保罗本人和他一样有50个人)
Logan(20个人喜欢该logan)
Michael(迈克尔被80人喜欢)
我们希望对所有这些人进行排序,以使Michael成为用户1看到的第一个个人资料。
现在我的问题是,我将如何使用mongoDB做到这一点?在SQL中,这很简单,只需执行一个表JOIN,使用SUM()和COUNT()对该表进行排序,并确保您具有必要的索引。
在mongoDB中,我看到的唯一方法是在每个Match对象上有一个uid2likes
(将被排序)字段,该字段将由cron作业递增,但这很荒谬,并且没有没规模。
我的问题更多是关于如何以可扩展的方式执行此操作。
答案 0 :(得分:3)
您可以在3.4中使用以下汇总查询。
这里的想法是$match
所有喜欢user1的用户,然后是$lookup
,以获得所有喜欢user1的用户。
$group
和$sort
以按计数desc对匹配进行排序。
$limit
以限制匹配的用户。
db.colname.aggregate([
{"$match":{"uid1":userID,"uid2action":"L","uid1action":"E"}},
{"$lookup":{
"from":colname,
"localField":"uid2",
"foreignField":"uid1",
"as":"uid2likes"
}},
{"$unwind":"$uid2likes"},
{"$match":{"uid2likes.uid2action":"L"}},
{"$group":{
"_id":{"uid1":"$uid1","uid2":"$uid2"},
"uid2likecount":{"$sum":1}
}},
{"$sort":{"uid2likecount":-1}},
{"$limit":paginationLimit}
])
笔记夫妇
使用{3.4}中优化的$lookup + $unwind + $match
通过在$match
内移动查询谓词$lookup
来运行很重要。更多here
对于初始匹配和查找匹配,您都可以使用现有索引(假设您在uid1上有一个索引)。
还要尝试在uid2action上添加索引,并查看它是否在$lookup
+ $match
阶段被获取。更多here
和here
添加索引:
db.colname.createIndex( { uid1: 1 } )
db.colname.createIndex( { uid2action: 1 } )
衡量索引的使用:
db.colname.aggregate([{$indexStats: {}}, {$project: {key: 0, host: 0}}]).pretty();
说明查询:
db.colname.explain("executionStats").aggregate(above pipeline);
您可以在索引之间切换并检查执行状态,以了解如何挑选索引。也可以尝试复合索引。
使用3.6可以稍微清理一下查询。
db.colname.aggregate([
{"$match":{"uid1":userID,"uid2action":"L","uid1action":"E"}},
{"$lookup":{
"from":colname,
"let":{"uid2":"$uid2"},
"pipeline":[
{"$match":{"$expr":{"$eq":["$uid1","$$uid2"]},"uid2action":"L"}},
{"$count":"count"}
],
"as":"uid2likes"
}},
{"$unwind":"$uid2likes"},
{"$sort":{"uid2likes.count":-1}},
{"$limit":paginationLimit}
])
答案 1 :(得分:2)
我将使用聚合管道。
您没有提供有关架构的很多信息,因此这些是我的假设:
match
users
users
集合中的每个文档都有一个likes
字段,其中包含一个Number
下面的查询将返回对L
投userID
的用户(即当前用户)的排序列表,以其总likes
递增。
db.match.aggregate([
{ $match: { "uid1": userID, "uid1action": "E", "uid2action": "L" } },
{ $project: { _id: 0, uid2: 1 } },
{ $lookup: {
from: "users",
let: { uid: "$uid2" },
pipeline: [
{ $match: { $expr: { $eq: [ "$_id", "$$uid" ] } } },
{ $project: { _id: 0, likes: 1 } },
],
as: "likes" }
},
{ $unwind: "$likes" },
{ $project: { _id: "$uid2", likes: "$likes.likes" } },
{ $sort: { likes: -1 } },
{ $limit: paginationLimit }
])
从match
集合中获取所有符合条件的文档(uid1为当前用户,uid1action为“ E”,uid2action为“ L”)。
仅使用每个Match对象中的uid2
字段。
在users
集合中进行内部搜索。
匹配所有_id
等于uid2
的用户。
从那里的每个文档中仅获取likes
字段。
以likes
字段的形式返回结果。
现在,最后一个操作将返回一个列表,以便展开该列表。
在此之后,仅将uid2
字段设为_id
,将likes.likes
字段设为likes
。
根据likes
字段对结果进行排序。
根据paginationLimit
限制结果。