MongoDB按辅助查找表排序

时间:2018-10-29 20:47:37

标签: mongodb

我有一个约会应用程序,我将所有潜在的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作业递增,但这很荒谬,并且没有没规模。

我的问题更多是关于如何以可扩展的方式执行此操作。

2 个答案:

答案 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阶段被获取。更多herehere

添加索引:

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)

此答案取决于您使用的MongoDB版本。

我将使用聚合管道

您没有提供有关架构的很多信息,因此这些是我的假设:

  1. 您的匹配对象集合的名称为match
  2. 您的用户集合的名称为users
  3. users集合中的每个文档都有一个likes字段,其中包含一个Number

下面的查询将返回对LuserID的用户(即当前用户)的排序列表,以其总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限制结果。