【问题标题】:query documents that depend on values of other documents in mongodb查询依赖于 mongodb 中其他文档值的文档
【发布时间】:2018-06-01 20:47:58
【问题描述】:

想象下面的猫鼬模型:

const UserSchema = Schema({
  //_id: ObjectId,
  //more fields,
  blockedIds: [{
    type: ObjectId,
    ref: 'User'
  }]
})

获取与某个 _id 的用户的阻塞 ID 不匹配的所有用户的最有效方法是什么?

一种天真的方法是执行两个查询:

User.findById(id).then(user => {
  return User.find({_id: {$nin: user.blockedIds}})
})

是否可以使用聚合框架和 $facets 在一个查询中完成此任务?

【问题讨论】:

  • 这应该是微不足道的。你可以在 3.6 中尝试User.find({$expr:{{ $not: [ { $in: [ "$_id", "$blockedIds] } ] }}})$expr 是 3.6 中的新添加,它允许您在常规查询中使用聚合比较运算符。不知道为什么需要$facets 虽然
  • 我认为您的查询返回了某事。和我的不一样。这不是找到所有用户的所有未阻止用户吗?我的查询找到具有给定 _id 的特定用户的所有未阻止用户

标签: javascript mongodb mongoose nosql


【解决方案1】:

为您的用例尝试 3.6 中的 non-correlated 子查询。

有点像

User.aggregate(
 [{$lookup:{
   from: "users",
   pipeline:[
    {$match: {_id:mongoose.Types.ObjectId(id)}},
    {$project: {_id:0,blockedIds:1}}
   ],
   as: "noncr"
 }},
 {$match:{
   $expr:{
     $not:[
      {$in:[
        $_id,
        {$arrayElemAt:["$noncr.blockedIds",0]}
      ]}
    ]
  }
}},
{$project:{noncr:0}}]
)

$lookup 为输入 id 拉入“blockedIds”,然后$match 过滤“_id”不在blockedIds 列表中的文档。

$expr 允许在 $match 阶段使用聚合比较运算符。

$arrayElemAt 从 $lookup 数组中获取第一个元素。

$in 将_id 与blockedIds 进行比较。

$project 排除以从最终响应中删除“noncr”字段。

请注意,当您测试查询时,在查找阶段的“from”属性中使用集合名称而不是模型或模式名称。

【讨论】:

  • 很好的答案!你也有 mongo 3.4 的解决方案吗?
  • 使用$facet,如您所述。可能效率不高,但可以尝试User.aggregate( [ { $facet: { "blockedUser": [ {$match: {_id:mongoose.Types.ObjectId(id)}}, {$project: {_id:0,blockedIds:1}} ], "allUsers": [ { $match: { } } ] } }, {$unwind:"$allUsers"}, { "$redact": { "$cond": [ { $not:[ {$in:[ $allUsers._id, {$arrayElemAt:["$blockedUser.blockedIds",0]} ]} ] }, "$$KEEP", "$$PRUNE" ] } } ])
【解决方案2】:

另一个答案中的聚合框架很有用,但为了完整起见,这里有另一种方法。在 Redis 数据库设计中,您总是必须考虑从“双向”访问数据,因此您会考虑反转结果。这就是我们可以在这里做的。

不要在用户上使用blockedIds 数组,而是使用blockedBy

const UserSchema = Schema({
  blockedBy: [{
    type: ObjectId,
    ref: 'User',
    index: true,
  }]
});

所以现在你倒转问题,查询变得微不足道:

User.find({ userID: { $nin: blockedBy: } });

在某些情况下,它可能是一个可行的选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    • 2018-03-09
    • 1970-01-01
    • 2018-12-01
    • 2017-01-04
    相关资源
    最近更新 更多