javascriptmongoosemernmongoose-schemamongoose-populate

Mongoose: Displaying data with ObjectID (or populate)


I am writing a MERN web application. This particular page is focused on recording demerits that have been accrued by students. I have many different models that interact with each other, but in this case I need the following to interact:

const demeritReportInstanceSchema = new mongoose.Schema({
    user : {
        type: mongoose.Schema.Types.ObjectId,
        required: true,
        ref: 'User'
    },
...
})
const userSchema = new mongoose.Schema({
...
    fName: {
        type: String,
        required: true
    },
    lName: {
        type: String,
        required: true
    },
...
})

whereas when the table is displayed, it currently looks like this:

user offenseDate period infractionCode punishingOfficial
(this is a user ObjectID) 65c5480f6b1bcdbcee2491aa Feb 2, 2024 1 another objectID another user ObjectID

I need it it look like:

user offenseDate period infractionCode punishingOfficial
lName, fName Feb 2, 2024 1 M11012 lName, fName

I have been following this tutorial: https://www.youtube.com/watch?v=CvCiNeLnZ00&t=12778s&ab_channel=DaveGray (ref: chapter 7) in which Dave has a Note and User model (backend) like the following:

const noteSchema = new mongoose.Schema({
        user: {
            type: mongoose.Schema.Types.ObjectId,
            required: true,
            ref: 'User'
        },
...
})
const userSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true
    },
})

that displays the username in a similar table as what I have (frontend) by:

Note.js

        return (
            <tr className="table__row">
                ...
                <td className="table__cell note__username">{note.username}</td>

                ...
            </tr>
        )

NotesList.js

    if (isSuccess) {
        const { ids } = notes

        const tableContent = ids?.length
            ? ids.map(noteId => <Note key={noteId} noteId={noteId} />)
            : null

        content = (
            <table className="table table--notes">
                <thead className="table__thead">
                <tr>
                    ...
                    <th scope="col" className="table__th note__username">Owner</th>
                    ...
                </tr>
                </thead>
                <tbody>
                {tableContent}
                </tbody>
            </table>
        )
    }

Any help would be greatly appreciated.

Using {demerit.user} provides me with the ID of the user as displayed above. Using {demerit.user.fName}, {demerit.user.lName} provides me with " , " in the cell of the table, and does not return its attributes. I have been examining how to use Model.populate from Mongoose: https://mongoosejs.com/docs/populate.html but I do not know where I would place populating queries - in the frontend, or the backend with the model?


Solution

  • To make this easy for you implement, I have had a look at the tutorial and the author seems to be importing the models into a controller and making custom query functions to the database then using them inside each route handler. Therefore following the pattern adopted in the tutorial your code might look something like:

    const Demerits = require('../models/Demerits'); //< or whatever this model is called
    const User = require('../models/User');
    const asyncHandler = require('express-async-handler');
    
    const getAllDemerits = asyncHandler(async (req, res) =>{
       const demerits = await Demerits.find().populate('user').lean();
       if(!demerits){
          return res.status(400).json({message: 'No demerits found'});
       }
       res.json(demerits);
    })
    

    Here populate('user') is what swaps the ObjectId for the full User documents and will allow you to access user.fName and user.lName in your front-end.