问题
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 username
s. 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