Nodejs express populate sub array data

Viewed 140

I have relative field and in relative I have subRelatives and it continues like nested array. Mean in subRelatives I have subRelatives and some time its 10 times continues process.

My code

router.get(`/userTree/:id`, async (req, res) => {
    const userTrees = await Tree.find({createdBy: req.params.id})
        .populate(
            ["createdBy",
                {
                    path: "relatives",
                    populate: {
                        path: "subRelatives",
                        populate: {
                            path: "subRelatives",
                            populate: "subRelatives",
                        },
                    },
                }
            ]);

    if (!userTrees) {
        res.status(500).json({success: false});
    }

    res.send({success: true, data: userTrees});
});

I have added populate but it populate first 2 sub relative and then shows MongooseIds only without populating. So I need to added manually some more populate methods so it will run but its crashing because of lot of data now.

and data look like this.

{
    "success": true,
    "data": {
        "_id": "62dad5c6aff2337dc84d9b40",
        "treeName": "test1",
        "createdBy": {
            "_id": "62d8619cebd6543477c5b7d8",
            "userName": "test1",
            "userEmail": "test1@gmail.com",
            "userFamilyTrees": [
                "62d8c713547ba80854d89d59"
            ]
        },
        "relatives": [
            {
                "_id": "62dad5c7aff2337dc84d9b44",
                "firstName": "tesads",
                "subRelatives": [
                    {
                        "_id": "62db1cf186b7012ed9937517",
                        "firstName": "asdasd",
                        "subRelatives": []
                    },
                    {
                        "_id": "62db1d0d86b7012ed9937522",
                        "firstName": "asd",
                        "subRelatives": []
                    },
                    {
                        "_id": "62dc24c15e6f5ea436cce14b",
                        "firstName": "julia",
                        "subRelatives": [
                            {
                                "_id": "62dc24c15e6f5ea436cce14b",
                                "firstName": "julia",
                                "subRelatives": [
                                    "62dc253bd2119bea52f4f9af"
                                ]
                            }
                        ]
                    },
                    {
                        "_id": "62dc24fcd2119bea52f4f99d",
                        "firstName": "julia",
                        "subRelatives": []
                    }
                ]
            }
        ]
    }
}

This is my Tree Schema

const mongoose = require('mongoose')

const treeSchema = new mongoose.Schema({
    treeName: {
        type: String,
        required: true
    }, image: {
        type: String,
        default: ''
    },
    treePrivacy: {
        type: Boolean,
        required: true
    },
    treeNote: {
        type: String,
        default: ""
    },
    createdBy: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'users',
        required: true,
    },
    createDate: {
        type: Date,
        default: Date.now,
    },
    relatives: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'relatives',
    },],
    usersInTree: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'users',
    },],
    media: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'media',
    },]
});



treeSchema.virtual('treeID').get(function () {
    return this._id.toHexString();
});

treeSchema.set('toJSON', {
    virtuals: true
})

exports.Tree = mongoose.model('trees', treeSchema)

exports.treeSchema = treeSchema;

This is relative Schema

const mongoose = require('mongoose')

const relativeSchema = new mongoose.Schema({
    firstName: {
        type: String,
    },
    lastName: {
        type: String,
    }, image: {
        type: String,
    },
    relativeEmail: {
        type: String,
    },
    relativeType: {
        type: Number,
    },
    // relative grandfather0, father1, mother2, wife3, sister4, brother5, child6
    treeID: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'users',
        required: true,
    },
    subRelatives: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'relatives',
    }],
    parentRelative: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'relatives',
    },
    userID: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'relatives',
        required: false
    }
});

relativeSchema.virtual('relativeId').get(function () {
    return this._id.toHexString();
});

relativeSchema.set('toJSON', {
    virtuals: true
})

exports.Relatives = mongoose.model('relatives', relativeSchema)

exports.relativeSchema = relativeSchema;

This is post api for tree

router.post('/createTree', uploadOptions.single('image'), async (req, res) => {
    const file = req.file;
    if (!file) return res.status(400).send('No image in the request');

    const fileName = file.filename;
    const basePath = `${req.protocol}://${req.get('host')}/public/uploads/`;
    var userintree = [];


    const userExist = await User.findById(req.body.createdBy);
    if (!userExist) return res.status(400).send({ success: false, message: 'UserID   is not correct' })
    userintree.push(req.body.createdBy);

    let createtree = new Tree({
        treeNote: req.body.treeNote,
        treeName: req.body.treeName,
        treePrivacy: req.body.treePrivacy,
        createdBy: req.body.createdBy,
        image: `${basePath}${fileName}`,
        usersInTree: userintree
    });

    createtree = await createtree.save();
    if (!createtree) return res.status(400).send({ success: false, message: 'Issue to create a tree' })


    userExist.userFamilyTrees.push(createtree._id.toHexString())
    const user = await User.findByIdAndUpdate(
        req.body.createdBy,
        {
            userFamilyTrees: userExist.userFamilyTrees,
            $push: {
                usersInTree: req.body.createdBy
            }

        },

        { new: true }
    )
    if (user) res.status(200).send({ success: true, message: 'Tree Created.!,', data: createtree })
});

and post API for relative

router.post('/addRelative', uploadOptions.single('image'), async (req, res) => {
    const file = req.file;
    if (!file) return res.status(400).send('No image in the request');

    const fileName = file.filename;
    const basePath = `${req.protocol}://${req.get('host')}/public/uploads/`;
    console.log(fileName); console.log(basePath);
    console.log(req.body);
    let createRelative = new Relatives({
        firstName: req.body.firstName,
        lastName: req.body.lastName,
        relativeEmail: req.body.relativeEmail,
        relativeType: req.body.relativeType,
        treeID: req.body.treeID,
        subRelatives: req.body.subRelatives,
        parentRelative: req.body.parentRelative, image: `${basePath}${fileName}`,

    });
    const treeExist = await Tree.findById(req.body.treeID);
    if (!treeExist) return res.status(400).send({ success: false, message: 'TreeID is not correct' })
    createRelative = await createRelative.save();
    if (!createRelative)
        return res.status(400).send({ success: false, message: 'Something Went Wrong.!,' })
    treeExist.relatives.push(createRelative._id.toHexString())

    const tree = await Tree.findByIdAndUpdate(
        req.body.treeID,
        {
            relatives: treeExist.relatives
        },
        { new: true }
    )

    if (req.body.parentRelative) {
        console.log(req.body.parentRelative)

        const parent = await Relatives.findById(
            req.body.parentRelative
        );
        // console.log(parent)
        // console.log(parent)

        parent.subRelatives.push(createRelative)
        const user = await Relatives.findByIdAndUpdate(
            req.body.parentRelative,
            {
                subRelatives: parent.subRelatives,
            },
            { new: true }
        )
        // console.log(user)
        if (!user) return res.status(400).send({ success: false, message: 'Something Went Wrong.!,' })

        // res.send(ser);
    }


    res.status(200).send({ success: true, message: 'Relative Created Created.!,', data: createRelative })
});
2 Answers

Populate data like this

  .populate(
            ["createdBy",
                {
                    path: "relatives",
                    populate: {
                        path: "subRelatives",
                        model: "SubRelative",
                    },
                }

            ]);

I've assumed that the model name refaring to subRelative is SubRelative

mongoose does support nested population, it's just that you need to specify the model field as the treeSchema does not have access to all other schema during run time. It looks like this:

const userTrees = await Tree.find({createdBy: req.params.id})
    .populate(
        [
            "createdBy",
            {
                path: "relatives",
                populate: {
                    path: "subRelatives",
                    model: 'relatives',
                },
            }
        ]);

mongoose does some optimizations to the query, but considering you know the exact structure you can reduce db calls and improve performance if you do this yourself instead of using populate.

Related