【问题标题】:Mongoose/MongoDB perform where after populateMongoose/MongoDB 在填充后执行
【发布时间】:2020-02-10 21:30:01
【问题描述】:

我目前面临一个问题,我想使用用户以前不喜欢的 mongoose 从 MongoDB 检索列表。

房源表:

const listingSchema = new Schema({
   _id,
   ...etc
})
listingSchema.virtual('listingLikes',{
    ref:'ListingLikes',
    ...etc
})

列表喜欢表:

const listingLikesSchema = new Schema({
   _id,
   listingId,
   userId
})

我希望能够执行这样的查询:

ListingModel.find().populate('listingLikes').where({'listingLikes.userId':{$ne:userId}}).limit(10).exec()

基本获取用户不喜欢的所有listing。以下查询可实现此目的:

Listing.find().where({_id:{$nin:[ ...All previously liked user listings in the listing likes model ) ]}})

但是我认为这在很大程度上是低效的(必须在找到列表之前将所有用户以前喜欢的列表加载到内存中)。

我怎样才能以更有效的方式做到这一点?理想情况下不修改当前架构。

【问题讨论】:

标签: mongodb mongoose aggregation-framework where-clause populate


【解决方案1】:

写效率cursor (这是一个查询结果集的指针)可以用来将查询结果的数据一一提取到内存和过滤器:

注意:根据listingLikes架构存储Listings的实际引用(_idlistingId),建议调整listingSchema直接定义@ 987654328@ 作为ref 的字段。而不是virtuals

因为下面的解决方案是按照上面的。需要注意的一件事是 match 不能直接用于填充 virtuals

这里的想法是:

  • 准备一个查询以填充listingLikes for ListingsuserId 上的匹配项
  • 迭代此查询的光标并推送 listingLikes [] or null 为空/null 的文档,因为这些将是用户不喜欢的列表。

由于cursors 实现AsyncIterator interface,允许它们在for…await 循环中使用。

let requiredListingDocs = [];
//with cursor limit can be removed if it was there to bring only some documents in memory
const cursor = ListingModel.find({})
  .populate({
    path: "listingLikes",
    match: { userId: userId },
    options: { limit: 10 }
  })
  .cursor();
//will contain listingLikes [] empty/or null for Listing docs that user didn't like

//looping on cursor which returns promise
for await (const doc of cursor) {
    if (doc["listingLikes"] === null || doc["listingLikes"].length === 0) {
        requiredListingDocs.push(doc);
    }
  }
console.info("LISTING DATA::", requiredListingDocs);

【讨论】:

  • 这不会做以下事情吗? 1) 查找所有喜欢的列表 2) 填充喜欢的列表 3) 忽略当前用户列表喜欢的所有列表喜欢?我相信这个查询仍然会给我用户在结果中喜欢的列表。我需要结果不包括以前喜欢的任何列表。
  • @Eladian 如果列表也应该有一个过滤器,可以放入find(),这不会解决它。您能否使用一些示例 listinglistingLikes 文档更新问题,以及您的预期结果是什么样的,以便更好地理解。
  • 我能给出的最好解释是我想要“用户以前不喜欢的所有列表”。当用户喜欢一个列表时,一个文档被插入到 listingLikes 集合中。因此,我需要执行“不在”形式的查询。我会尽快更新问题。
  • 列表过滤器应该是Listing.find().where({_id:{$nin:[ ...All previously liked user listings in the listing likes model ) ]}})。但以更有效的方式
  • @Eladian 这说明了这一点。我不确定这是否可以通过单个查询来完成。已经用另一种方法更新了答案,以有效地记忆。
【解决方案2】:

如果您想引入 userId 作为变量排除,带有 $lookupaggregate() 可能会起作用:

let loggedInUser = 'Eladian'
ListingModel.aggregate([
  {
    // $lookup is instead of .populate() but does roughly the same thing,
    // bringing in the data from the listingLikes table
    '$lookup': {
      'from': 'listingLikesModel', 
      'localField': 'listingLikes', 
      'foreignField': '_id', 
      'as': 'listingLikes'
    }
  }, {
    // $lookup puts its results in an array in your table. 
    // We flatten it using $unwind
    '$unwind': '$listingLikes'
  }, {
    // $match is equivalent to 'where' 
    '$match': {
      'listingLikes.userId': {
        '$ne': loggedInUser
      }
    }
  }, {
    // limit to 10 results
    '$limit': 10
  }
])

或者,您可以在 $lookup 中添加一个 $pipeline,我相信这会更有效,并允许您从 ListingModel 或其他任何可能存储它的地方引入 userId:

ListingModel.aggregate([
  {
    '$lookup': {
      'from': 'listingLikesModel',
      // below assumes there is a 'userId' field in ListingModel,
      // which we put in to a variable (listingUserId) so we can use it in
      // the pipeline
      'let': {'listingUserId': '$userId'}, 
      'pipeline': [
        {
          '$match': {
            '$expr': {
              '$ne': [ '$userId', '$$listingUserId']
            }
          }
        },
        { '$limit': 10 }
      ],
      // It puts the 10 posts in an array called 'notLikedList' in the ListingModel
      'as': 'notLikedList'
    }
  }
])

有关 $lookup 管道的更多信息here

【讨论】:

  • 是否也可以“填充” userId 或在同一个查询中加入用户?顺便感谢您的回复!
  • 在 $lookup 中添加了第二个使用管道的方法,1) 更高效,2) 允许您使用 ListingModel 中的 userId - 我想这就是您的意思?
  • 这仍然不太正确,也不是我想要实现的目标,但感谢迄今为止的所有帮助。作为一个更好的主意,我想执行类似于 Listing.find().where({_id:{$nin:[ ...All previously liked user listings in the listing likes model ) ]}}) 的查询,但不必将所有用户以前喜欢的列表查询到内存中。
猜你喜欢
  • 2019-06-25
  • 2023-04-11
  • 2020-10-14
  • 2021-03-03
  • 2019-04-23
  • 2021-04-19
  • 2019-11-24
  • 2023-03-14
  • 2012-07-03
相关资源
最近更新 更多