Search code examples
node.jsarraysexpressmongooseobjectid

Issue Trying to Save an Array of Object Ids Mongoose


I am using mongoose and express to create my database and server respectively.

I have a Schema like below for my data:

const mongoose = require('mongoose')
const {Schema} = mongoose

const quotesSchema = new Schema({
  tags: [
    {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Tags' // I want to save an array of tags coming in from the request body
     }
   ],
  content: {
     type: String,
     required: true
  },
  author: {
     type: String,
     required: true
  },
  authorSlug: {
     type: String,
  },
  dateAdded: {
     type: Date,
     default: Date.now()
  },
   dateModified: {
      type: Date,
      default: Date.now()
   },
})

const Quotes = new mongoose.model('Quotes', quotesSchema)
module.exports = Quotes

I want to save an array of tags coming in from the request body but it only saves one item from the array.

Here's my sample code:

router.post('/', async (req, res, next) => {
try {
    const {tags, author, content} = req.body
    const slug = author.replace(/\s+/g, '-').toLowerCase()

    var tagIds = []
    
    tags.forEach( async (tag) => {
        const foundTag = await Tag.find({name: tag}) // I first of all search my tag collection 
 // to see if the names supplied in the request body tags array exist, 
 // then try to extract their Objectids and store in an array to move to the next step - saving the quote

        // foundTag.forEach(async (item) => {
        //     return await tagIds.push(item._id)
        // })            

        for (var i = 0; i < foundTag.length; i++) {
            tagIds.push[i]
        }

        console.log(tagIds)
        
        const quote = new Quote({
            tags: tagIds,
            content,
            author,
            authorSlug: slug
        })

        const quoteToSave = await quote.save()

        return res.status(201).json({
            success: true,
            msg: 'Quote Created Successfully',
            quote: quoteToSave
        })

     })
  } catch (error) {
     console.error(error)
  }
})

How do I pass the complete array of tags as a parameter to my quote to save. I think the issue here is that it doesn't wait for the second tag to enter the array.

Here's an image of my request-response in Postman:

image description

How do I get the tags which is an array from req.body and save it as part of my quote object? At the moment I am doing everything inside my forEach loop and it doesn't seem decent enough to me. Is there a best approach like wait for the data then the save part will not have any parent control statement, like currently.

Thanks


Solution

  • #1 If you will use async functions inside forEach then use Promise.all() with bluebird.

    #2 MongoDB operators are very useful.

    #3 Knowing the difference between forEach and map is very important.

    #4 express response doesn't need return statement if you use it correctly.


    The final code will look something like:

    router.post('/', async (req, res, next) => {
    
        const { tags, author, content } = req.body
        const slug = author.replace(/\s+/g, '-').toLowerCase()
    
        var foundTags = await Tag.find({ name: { $in: tags } })
    
        if (foundTags) {
    
            var tagsIds = []
            foundTags.map(tag => {
                tagsIds.push(tag._id);
            })
    
            const quote = new Quote({
                tags: tagsIds,
                content,
                author,
                authorSlug: slug
            })
    
            const quoteToSave = await quote.save()
    
            if (quoteToSave) {
                res.status(201).json({
                    success: true,
                    msg: 'Quote Created Successfully',
                    quote: quoteToSave
                })
            } else {
                res.status(500).json("failed saving quote")
            }
        } else {
            res.status(500).json("failed finding tags")
        }
    })