简体   繁体   中英

Category hierarchy aggregation using mongodb and nodejs

My document structure is as follows:

{
    "_id" : ObjectId("54d81827e4a4449d023b4e34"),
    "cat_id" : 1,
    "description" : "Refridgerator",
    "image" : "refridgerator",
    "parent" : null,
    "slug" : "refridgerator"
}
{
    "_id" : ObjectId("54dc38bce4a4449d023b4e58"),
    "name" : "Ice Cream",
    "description" : "Ice Cream",
    "image" : "ice-cream.jpg",
    "slug" : "ice-cream",
    "parent" : "54d81827e4a4449d023b4e34"
}
{
    "_id" : ObjectId("54dc3705e4a4449d023b4e56"),
    "name" : "Chocolate",
    "description" : "Chocolate",
    "image" : "chocolate.jpg",
    "slug" : "chocolate",
    "parent" : "54d81827e4a4449d023b4e34"
}

I'm making a category hierarchy using mongodb and nodejs.

Now I wish to query for _id = '54d81827e4a4449d023b4e34' (Refridgerator) and should get back all the child categories

How to achieve the above in nodejs?

Also, nodejs uses async call to the database, I'm unable to get the json structured with parent – child relations.

How would I do the async call for this?

This has nothing to do with NodeJS, it's your data structure that matters.

refer to my answer to this question , the first part is about how to implement it efficiently.

You want the refridgerator and all the subcategories?

And async is also a problem?

I think you can use aggregation here.

Say you're looking for a category with _id variable which is an ObjectId of what you want, and it's subcategories.

db.yourCollection.aggregate({
    // get stuff where you have the parent or subcats.
    $match: {
         $or: [
               {_id: ObjectId("54de8b9f022ff38bbf5e0530")},
               {parent: ObjectId("54de8b9f022ff38bbf5e0530")}
              ]
         }
},
// reshape the data you'll need further on from each mached doc
{
    $project: {
        _id: false,
        data: {
            id: '$_id',
            name: '$name'
            // I guess you'll also want the `slug` and `image` here.
            // but that's homework :)
        },
        parent: '$parent'
    }
},
// now put a common _id so you can group them, and also put stuff into arrays
{
    $project: {
        id: {$literal: 'id'},
        mainCategory: {
            // if our parent is null, put our data.
            // otherwise put null here.
            $cond: [{$eq: [null, '$parent']}, {_id: '$data.id', name: '$data.name'}, undefined]
        },
        subcat: {
            // here is the other way around.
            $cond: [{$ne: [null, '$parent']}, {_id: '$data.id', name: '$data.name'}, null]

        }
    }
    // that stage produces for each doc either a mainCat or subcat
    // (and the other prop equals to null)
},
// finally, group the things so you can have them together
{
    $group: {
        _id: '$id',
        // a bit hacky, but mongo will yield to it
        mainCategory: {$max: '$mainCategory'},
        subCategories: {
            // this will, unfortunately, also add the `null` we have
            // assigned to main category up there
            $addToSet: '$subcat'
        }
    }
},
// so we get rid of the unwanted _id = 'id' and the null from subcats.
{
    $project: {
        _id: false,
        mainCategory: 1,
        subCategories: {
            $setDifference: ['$subCategories', [null]]
        }
    }
})

Given this data set:

[{
    "_id" : ObjectId("54de8b9f022ff38bbf5e0530"),
    "name" : "Fridge",
    "parent" : null
},
{
    "_id" : ObjectId("54de8bba022ff38bbf5e0531"),
    "name" : "choco",
    "parent" : ObjectId("54de8b9f022ff38bbf5e0530")
},
{
    "_id" : ObjectId("54de8bc8022ff38bbf5e0532"),
    "name" : "apple",
    "parent" : ObjectId("54de8b9f022ff38bbf5e0530")
}

I get this result:

{
    "result" : [ 
        {
            "mainCategory" : {
                "_id" : ObjectId("54de8b9f022ff38bbf5e0530"),
                "name" : "Fridge"
            },
            "subCategories" : [ 
                {
                    "_id" : ObjectId("54de8bc8022ff38bbf5e0532"),
                    "name" : "apple"
                }, 
                {
                    "_id" : ObjectId("54de8bba022ff38bbf5e0531"),
                    "name" : "choco"
                }
            ]
        }
    ],
    "ok" : 1
}

As for async, typically you'd do something like this:

db.collection.aggregate(thePipeLineAbove, function(err, results) {

    // handle err
    if (err) {
       // deal with it
    } else {
        console.log(results);
    }
});

But that depends a bit on your MongoDB driver.

You could expand this even if you have deeper hierarchy structure.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM