简体   繁体   中英

query documents that depend on values of other documents in mongodb

Imagine following mongoose model:

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

What is the most efficient way to get all users that don't match the blockedIds of an user with a certain _id?

A naive way would be to to perform two queries:

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

Is it possible to use the aggregation framework and $facets to accomplish that in one query?

Try non-correlated sub query from 3.6 for your use case.

Something like

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 to pull in the "blockedIds" for input id followed by $match to filter the documents where "_id" is not in list of blockedIds.

$expr allows use of aggregation comparison operators in $match stage.

$arrayElemAt to fetch the first element from $lookup array.

$in to compare the _id against blockedIds.

$project with exclusion to remove the "noncr" field from the final response.

Please note when you test query use the collection name not model or schema name in "from" attribute of look up stage.

The aggregation framework in the other answer is useful, but for the sake of completeness, here's an alternate approach. In Redis db designs, you always have to think of accessing the data from "both ways", so you think of inverting results. So that's what we can do here.

Instead of having blockedIds array on user, have a blockedBy :

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

So now you invert the problem and the query becomes trivial:

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

In some situations it could be a viable option.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM