Server side pagination with single document string array field

匆匆过客 提交于 2020-12-10 07:04:33

问题


I'm trying to figure out how to do server side pagination for a single document that contains a blacklistGroup: [String] field

  "blacklistGroup": [
    "5e99fd3aa506cf570056898d",
    "5e99fde5a506cf5700568991",
    "5e99fd64a506cf570056898e",
    "5e98c950f4fb3f63b4634e30",
    "5e99fd15a506cf570056898c",
    "5e99fda5a506cf570056898f",
    "5e99fdc7a506cf5700568990",
    "5e99fcf3a506cf570056898b",
    "5e98e90d85e69146f841d23a",
    "5e9867ff5e72550988820dd3",
    "5e98e8e785e69146f841d239"
  ]

I want it to limit 10 at a time. I'm basically looking for the equivalent to

     User.findById({ _id: req.params.id })
              .select("blacklistGroup")
              .populate("blacklistGroup", "username")
              .skip(10 * (req.query.currentPage - 1))
              .limit(10)
              .then(documents => {

but obviously not for multiple documents, but for the single document being queried. I'm currently passing the current page to the backend to retrieve that specific page's list of 10. Also it's important to return the username hence the .populate in example. I appreciate any help with this!


回答1:


You could use slice on query followed by populate.

User.findById({ _id: req.params.id })
           .slice('blacklistGroup', [10 * (req.query.currentPage - 1), 10])
           .populate("blacklistGroup", "username");

You could also use aggregate with $slice and $lookup.

User.aggregate([
 {"$match":{"_id":req.params.id}},
 {"$project":{"blacklistGroup":{"$slice":["$blacklistGroup", 10 * (req.query.currentPage - 1), 10]}}},
 {"$lookup":{
   "from": usercollection,
   "localField": "blacklistGroup",
   "foreignField": "_id",
   "as": "blacklistGroup"
 }},
 {"$project":{"usernames":"$blacklistGroup.username"}}])

You could furthur enhance it using newer lookup version.

User.aggregate([
 {"$match":{"_id":req.params.id}},
 {"$lookup":{
  "from":usercollection,
  "let":{"blacklistGroup":{"$slice":["$blacklistGroup", 10 * (req.query.currentPage - 1), 10]}},
  "pipeline":[
    {"$match":{"$expr":{"$eq":["$_id","$$blacklistGroup"]}}},
    {"$project":{"username":1,"_id":0}}
  ],
  "as":"blacklistGroup"
}}]);

For the sake of completeness added the whole example

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true});
var db = mongoose.connection;
mongoose.set('debug', true);
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  // we're connected!
});
const UserSchema = new mongoose.Schema({
    blacklistGroup: [{
      type: String,
      ref: 'Group'
    }]
  })
const GroupSchema = new mongoose.Schema({
    _id: String,
    content: String,
    username: String
  })
const User  = mongoose.model('User', UserSchema, 'users');
const Group = mongoose.model('Group', GroupSchema, 'groups');
module.exports = {
    User, Group
  }

 User.findOne().slice("blacklistGroup", 3).populate("blacklistGroup", "username").exec((err, blacklistGroup) => {
      console.log("Populated blacklistGroup " + blacklistGroups);
    })        

Also added Mongo Playground likn for example 2

https://mongoplayground.net/p/YP5l5Kz3sp8




回答2:


As far as I know this cannot be done using the populate function, instead you'll have to use aggregation:

User.aggregate([
  {$match: {
    _id: req.params.id
  }},
  {$unwind: '$blacklistGroup'},
  {$addFields:{
    blacklistGroupId: {$toObjectId: "$blacklistGroup"}
  }},
  {$lookup:{
    from: '<name of your blacklist collection>',
    localField: 'blacklistGroupId',
    foreignField: '_id',
    as: 'blacklistGroup'
  }},
  {$skip: 10 * (req.query.currentPage - 1)},
  {$limit: 10},
  {$group:{
    _id: '$_id',
    blacklistGroup: {$push: '$blacklistGroup.username'}
  }}
]).exec().then(documents => {
  //...
})

This will give you a single document that contains the blacklistGroup property that is an array of usernames. You can remove the $group-stage to keep these as 10 separate documents.

Note that this requires version 4.0 mongo.




回答3:


Try using the aggregate function and follow the steps,

$lookup:{
   "from": <usercollection>,
   "localField": "blacklistGroup",
   "foreignField": "_id",
   "as": "blacklistGroup"
 }

then,

$unwind: {
             path : <usercollection>,
         }

then,

$match: {
        ...
        }

your desired condition. after then do your use your limit function following the aggregate pattern.



来源:https://stackoverflow.com/questions/61290693/server-side-pagination-with-single-document-string-array-field

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!