查询依赖于mongodb中其他文档值的文档

时间:2017-12-19 12:03:31

标签: javascript mongodb mongoose nosql

想象一下跟随猫鼬模型:

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在一个查询中完成该任务?

2 个答案:

答案 0 :(得分:2)

针对您的用例,尝试使用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”属性中的模型或模式名称。

答案 1 :(得分:1)

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

不要在用户上添加blockedIds数组,而是拥有blockedBy

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

所以现在你反转问题并且查询变得微不足道了:

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

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