Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get Tree data from MongoDB

I have a collection in which data is stored in tree structure. Sample:

[
    {
        "_id": "entry1Id",
        "name": "entry1",
        "dependency": "entry2"
    },
    {
        "_id": "entry2Id",
        "name": "entry2",
        "dependency": "entry3"
    },
    {
        "_id": "entry3Id",
        "name": "entry3",
        "dependency": "entry4"
    },
    {
        "_id": "entry4Id",
        "name": "entry4",
        "dependency": "entry5"
    },
    {
        "_id": "entry5Id",
        "name": "entry5",
        "dependency": ""
    }
]

The depth is not defined but wont be circular.

Now i want a query to pass a name and get that item and all the nodes below that.

I tried doing it using multiple DB calls and its working, but i want to improve it.

'use strict';

module.exports = ({ $db }) => {
    return async (req, res, next) => {

        try {

            const name = req.query.name;
            const data = await getEntity($db, name);

            return res.status(200).json({
                success: true,
                data
            });

        } catch (error) {
            return next(error)
        }
    }
}

const getEntity = async ($db, id) => {
    const entity = await $db.models.Entity.findOne({ name }, { name: true, dependency: true });
    if (entity && entity.dependency) {
        entity.dependency = await getEntity($db, entity.dependency);
    }
    return entity;
}

But i expect the output should be more like tree.

{
    "_id": "entry1Id",
    "name": "entry1",
    "dependency": {
        "_id": "entry2Id",
        "name": "entry2",
        "dependency": {
            "_id": "entry3Id",
            "name": "entry3",
            "dependency": {
                "_id": "entry4Id",
                "name": "entry4",
                "dependency": {
                    "_id": "entry5Id",
                    "name": "entry5",
                    "dependency": ""
                }
            }
        }
    }
}

I also tried $graphLookup but the output is not what i expect

const getEntityWithDependencies = async ($db, name) => {
    const aggregationPipeline = [
        {
            $match: { name }
        },
        {
            $graphLookup: {
                from: 'entities', // Use the actual name of your collection
                startWith: '$dependency',
                connectFromField: 'dependency',
                connectToField: 'name',
                as: 'dependencies',
            }
        }
    ];

    const result = await $db.models.Entity.aggregate(aggregationPipeline).exec();
    return result.length > 0 ? result[0] : null;
}

The output i got using above is something like

{
    "_id": "entry1Id",
    "children": [
      {
        "_id": "entry2Id",
        "dependency": "entry3Id",
        "name": "entry2"
      },
      {
        "_id": "entry3Id",
        "dependency": "",
        "name": "entry3"
      }
    ],
    "dependency": "entry2Id",
    "name": "entry1"
  },
like image 794
Kamran Avatar asked Dec 05 '25 13:12

Kamran


1 Answers

Try some loop on top of graphLookup like:

'use strict';

const helper = require('../../../utils/helper');

module.exports = ({ $db }) => {
    return async (req, res, next) => {

        try {

            const entityName = req.query.name;
            let entity = await getEntityWithDependencies($db, entityName);

            const dependencies = {}
            if (entity.dependencies) {
                entity.dependencies.forEach(dependency => {
                    dependencies[dependency.name] = dependency;
                });
                delete entity.dependencies;
            }

            if (entity.dependency) entity = mapDependency(entity, dependencies)


            return res.status(200).json({
                success: true,
                data: entity
            });

        } catch (error) {

            return next(error)

        }
    }
}

const mapDependency = (entity, dependencies) => {
    entity['dependency'] = dependencies[entity['dependency']];
    if (entity['dependency'].dependency)
        entity['dependency'] = mapDependency(entity['dependency'], dependencies)
    return entity
}


const getEntityWithDependencies = async ($db, name) => {
    const aggregationPipeline = [
        {
            $match: { name }
        },
        {
            $graphLookup: {
                from: 'entities',
                startWith: '$dependency',
                connectFromField: 'dependency',
                connectToField: 'name',
                as: 'dependencies',
            }
        }
    ];

    const result = await $db.models.Entity.aggregate(aggregationPipeline).exec();
    return result.length > 0 ? result[0] : null;
}
like image 109
Shivam Singh Avatar answered Dec 08 '25 06:12

Shivam Singh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!