Express.js blogging application bug: filtering posts by category throws “Cast to ObjectId failed” failed error

六眼飞鱼酱① 提交于 2020-05-30 10:23:06

问题


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

I have Posts that are grouped into Categories, each in its own collection.

I run into a problem trying to filter posts by category. To obtain the post by category url, I turn the category name into a slug and use it this way:

Posted in <a href="/<%= post.category.cat_name.replace(/\s+/g, '-').toLowerCase(); %>"><%= post.category.cat_name %></a>

In the public routes file I have:

const express = require('express');
const postsController = require('../../controllers/front-end/posts');

// Express router
const router = express.Router();

// Get Posts
router.get('/', postsController.getPosts);

// Get Single Post
router.get('/:id', postsController.getSinglePost);

// Get Posts by Category
router.get('/:catname', postsController.getPostsByCategory);

module.exports = router;

The Post 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);

The Category model:

const mongoose = require('mongoose');

const categorySchema = new mongoose.Schema({
    cat_name: {
        type: String,
        required: true
    },
    updated_at: {
        type: Date,
        default: Date.now()
    },
    created_at: {
        type: Date,
        default: Date.now()
    }
});

module.exports = mongoose.model('Category', categorySchema);

In the Posts controller I turn the slug back into category name to filter posts by category name:

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

    function titleize(slug) {
            var words = slug.split("-");
            return words.map(function(word) {
                //return word;
                return word.charAt(0).toUpperCase() + word.substring(1).toLowerCase();
            }).join(' ');
        }

    const postCategory = titleize(req.params.catname);

   const posts = Post.find({ cat_name: postCategory }, (err, posts) => {
        console.log('Category: ', postCategory);
        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',
                posts: posts.reverse(),
            });
        }
    }).populate('category');
};

The line console.log('Category: ', postCategory) outputs Category: Favicon.ico instead of the category name.

What am I doing wrong?


回答1:


first of all - look at the moment where you ask DB - you need to await the answer, so you should use Promise.than() or async/await in your routes ... Another one from request you get STRING as parameter - but in mongo schema you have Object...

So you should receive smth like "CastError: Cast to ObjectId failed...", it depends on your vision you can: first select category from category.db => so you receive category Object after that you can search the posts using this object ..., or you can first populate results of posts by category (obtain plain category fields) and make further search...




回答2:


category in your post-schema is a $ref to the category-schema, which is why it holds an objectId. In order to reference and actually query your category-schema while using .find(), you need to populate it first:

Post.
  find({}).
  populate({
    path: 'category',
    match: { cat_name: postCategory}
  }).
  exec((err, posts) => {
    // ...
  });

The mongoose documentation for $ref/populate() is a bit hidden here, in case you want know more about it.




回答3:


if I read this correctly, res is expect json pairs.

I am guessing your post.reverse() does not output in json format.




回答4:


Thanks to Lilian Baxan, here is the right getPostsByCategory method in controllers\front-end\posts.js:

const Category = require('../../models/categories');
//more code here

exports.getPostsByCategory = async (req, res, next) => {

    function titleize(slug) {
        var words = slug.split("-");
        return words.map(function(word) {
            //return word;
            return word.charAt(0).toUpperCase() + word.substring(1).toLowerCase();
        }).join(' ');
    }

    const postCategory = titleize(req.params.catname);

    const singleCategory = await Category.findOne({cat_name:postCategory})

    const posts = await Post.find({ category : singleCategory }, (err, posts) => {
        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',
                posts: posts.reverse(),
            });
        }
    }).populate('category');
};


来源:https://stackoverflow.com/questions/61527367/express-js-blogging-application-bug-filtering-posts-by-category-throws-cast-to

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