How do I count all the documents in a collection and use the cont in a controller, with MongoDB and Express.js?

大憨熊 提交于 2020-05-17 07:45:26

问题


I am working on a blogging application (click the link to see the GitHub repo) with Express, EJS and MongoDB.

Trying to paginate the posts I did the following, in the controller:

exports.getPosts = (req, res, next) => {

    const perPage = 5;

    const currPage = req.query.page ? parseInt(req.query.page) : 1;

    let postsCount = 0;

    const posts = Post.find({}, (err, posts) => {

            postsCount = posts.length;

            let pageDecrement = currPage > 1 ? 1 : 0;

            let pageIncrement = postsCount >= perPage ? 1 : 0;

            if (err) {
                console.log('Error: ', err);
            } else {
                res.render('default/index', {
                    moment: moment,
                    layout: 'default/layout',
                    website_name: 'MEAN Blog',
                    page_heading: 'XPress News',
                    page_subheading: 'A MEAN Stack Blogging Application',
                    currPage: currPage,
                    posts: posts,
                    pageDecrement: pageDecrement,
                    pageIncrement: pageIncrement
                });
            }
        })
        .sort({
            created_at: -1
        })
        .populate('category')
        .limit(perPage)
        .skip((currPage - 1) * perPage);
};

And in the view:

<a class="btn btn-primary <%= pageDecrement == 0 ? 'disabled' : '' %>" href="/?page=<%= currPage - pageDecrement %>">&larr; Newer Posts</a>

and

<a class="btn btn-primary <%= pageIncrement == 0 ? 'disabled' : '' %>" href="/?page=<%= currPage + pageIncrement %>">Older Posts &rarr;</a>

That works fine unless there are is a number of posts equal to perPage x N, where N is an integer, in which case the "Older Posts" button becomes disabled one page too late.

That is because postsCount = posts.length counts the posts after they are limited by .skip((currPage - 1) * perPage).

So I need to count the posts from the model/collection and bring that count variable in the controller.

My model:

const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    short_description: {
        type: String,
        required: true
    },
    full_text: {
        type: String,
        required: true
    },
    category: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Category'
    },
    post_image: {
        type: String,
        required: false
    },
    updated_at: {
        type: Date,
        default: Date.now()
    },
    created_at: {
        type: Date,
        default: Date.now()
    }
});

module.exports = mongoose.model('Post', postSchema);

How do I count all the documents in the posts collection and use that number in the posts controller?


回答1:


Read $facet.

New in version 3.4.

Processes multiple aggregation pipelines within a single stage on the same set of input documents. Each sub-pipeline has its own field in the output document where its results are stored as an array of documents.

Example: See here

db.collection.aggregate([
  {
    $facet: {
      "count": [
        { $match: {} },
        { $count: "totalCount" }
      ],
      "data": [
        { $match: {} },
        { $sort: { _id: -1 } },
        { $skip: 1 },
        { $limit: 2 }
      ]
    }
  }
])

Mongoose Version:

Model.aggregate([
  {
    $facet: {
      "count": [
        { $match: {} },
        { $count: "totalCount" }
      ],
      "data": [
        { $match: {} },
        { $sort: { _id: -1 } },
        { $skip: 1 },
        { $limit: 2 }
      ]
    }
  }
]).
then(res => console.log(res)).
catch(error => console.error('error', error));



回答2:


In case of Mongoose you should use this:

https://mongoosejs.com/docs/api.html#aggregate_Aggregate-facet

Official Mongodb docs:

https://docs.mongodb.com/manual/reference/operator/aggregation/facet

General idea is to perform aggregation instead of multiple calls (1 for getting needed info + 1 to get the total count of documents)

You can perform 2 separate calls of course but it will hit your performance (not much for small data volumes but still...) So you can get all needed data with .find() and then get count like this: https://mongoosejs.com/docs/api.html#model_Model.count

PS. btw, use async/await instead of callbacks to avoid callback hell



来源:https://stackoverflow.com/questions/61812361/how-do-i-count-all-the-documents-in-a-collection-and-use-the-cont-in-a-controlle

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