admin管理员组

文章数量:1315061

I've got a MongoDB document which has records that looks like this:

[
  { _id: id, category_name: 'category1', parent_category: null },
  { _id: id, category_name: 'category2', parent_category: null },
  { _id: id, category_name: 'subcategory1', parent_category: id_parent_1 },
  { _id: id, category_name: 'subcategory2', parent_category: id_parent_1 },
  { _id: id, category_name: 'subcategory3', parent_category: id_parent_2 },
  { _id: id, category_name: 'subcategory4', parent_category: id_parent_2 }
]

As you can see, I'm storing categories with a parent_category of null, and subcategories have the parent category's ID. What I'm looking for is to group these into some kind of format like this:

[
  { category_name: 'category1', categories: [
       { category_name: 'subcategory1', _id: id },
       { category_name: 'subcategory2', _id: id }
    ]
  },
  { category_name: 'category2', categories: [
       { category_name: 'subcategory3', _id: id },
       { category_name: 'subcategory4', _id: id }
    ]
  }
]

So basically group the parent categories with an array with their child categories. I'm using Mongoose. I tried using the aggregation framework MongoDB provides but I can't get the desired result. :(

I have access to modify the schema in any way that could be needed!

Thanks in advance!

I've got a MongoDB document which has records that looks like this:

[
  { _id: id, category_name: 'category1', parent_category: null },
  { _id: id, category_name: 'category2', parent_category: null },
  { _id: id, category_name: 'subcategory1', parent_category: id_parent_1 },
  { _id: id, category_name: 'subcategory2', parent_category: id_parent_1 },
  { _id: id, category_name: 'subcategory3', parent_category: id_parent_2 },
  { _id: id, category_name: 'subcategory4', parent_category: id_parent_2 }
]

As you can see, I'm storing categories with a parent_category of null, and subcategories have the parent category's ID. What I'm looking for is to group these into some kind of format like this:

[
  { category_name: 'category1', categories: [
       { category_name: 'subcategory1', _id: id },
       { category_name: 'subcategory2', _id: id }
    ]
  },
  { category_name: 'category2', categories: [
       { category_name: 'subcategory3', _id: id },
       { category_name: 'subcategory4', _id: id }
    ]
  }
]

So basically group the parent categories with an array with their child categories. I'm using Mongoose. I tried using the aggregation framework MongoDB provides but I can't get the desired result. :(

I have access to modify the schema in any way that could be needed!

Thanks in advance!

Share Improve this question edited Sep 22, 2017 at 18:01 CommunityBot 11 silver badge asked Aug 23, 2015 at 10:11 pmerinopmerino 6,13011 gold badges60 silver badges77 bronze badges 5
  • Why did you designed your document that way at first place? maybe you can redesign you mongoose schemma and make some methods to validate the parent_id to assign the subcategories – Jose Osorio Commented Aug 23, 2015 at 14:22
  • Is it just two levels of categories? can your subcategories have their own subcategories? – Yaron Schwimmer Commented Aug 24, 2015 at 14:35
  • @JoseOsorio I don't really understand what you mean – pmerino Commented Aug 24, 2015 at 15:43
  • @yarons yes, it'll be just two levels, category and subcategory – pmerino Commented Aug 24, 2015 at 15:44
  • 1 Look at this category tree library for mongoose: github./incrediblesound/category-tree . Here a blog post from the author: incrediblesound.github.io/blog/2014/07/27/… – lifeisfoo Commented Aug 25, 2015 at 19:03
Add a ment  | 

4 Answers 4

Reset to default 5 +50

It seems like you're treating Mongo like an relational database (separating all these fields and bringing them together with a query). What you should do is rebuild your Schema. For example:

var CategorySchema = new Schema({
   category_name: String,
   subCategories:[subCategorySchema]
}

var subCategorySchema = new Schema({
   category_name: String
})

This way when you need to query the collection it's a simple

db.find({category_name: "name of the category"}, function(){})

to get everything you need.

Just in case: you can add the sub categories to the array with simple updates. Read this for more info.

Please try this if Your schema is not changed:

var MongoClient = require('mongodb').MongoClient

//connect away
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
  if (err) throw err;
  console.log("Connected to Database");

  //simple json record
    var document = [];
    //insert record
    //db.data.find({"parent_category":null }).forEach(function(data) {print("user: " + db.data.findOne({"parent_category":data._id })) })
    db.collection('data').find({"parent_category":null }, function(err, parentrecords) {
        if (err) throw err;
        var cat ={};
        parentrecords.forEach(function(data){
            cat["category_name"] = data["category_name"];
            db.collection('data').find({"parent_category":data._id },function(err, childrecords) {
                var doc = [];
                childrecords.forEach(function(childdata){
                    doc.push(childdata);
                        },function(){
                        cat["categories"] = doc;
                        document.push(cat);
                        console.log(document);
                    });
            });
        });
});
});

If you want to find out expected results without changing schema then you basically follow some plex mongo aggregation query. For finding output I follow following steps :

  1. First in $project check parent_category equals null if true then add _id else add parent_category.
  2. Now document structure looks like with new key name as parent_id presents and group by parent_id and push remaining data like category_name and parent_category.
  3. After that use $setDifference and $setIntersection to differentiate parent data and child data.
  4. And in finally unwind only single array objects so this single array object and used project for showing only those fields which to display.

Check working aggregation query as below :

db.collectionName.aggregate({
    "$project": {
        "parent_id": {
            "$cond": {
                "if": {
                    "$eq": ["$parent_category", null]
                },
                "then": "$_id",
                "else": "$parent_category"
            }
        },
        "category_name": 1,
        "parent_category": 1
    }
}, {
    "$group": {
        "_id": "$parent_id",
        "categories": {
            "$push": {
                "category_name": "$category_name",
                "parent_category": "$parent_category"
            }
        },
        "parentData": {
            "$push": {
                "$cond": {
                    "if": {
                        "$eq": ["$parent_category", null]
                    },
                    "then": {
                        "category_name": "$category_name",
                        "parent_category": "$parent_category"
                    },
                    "else": null
                }
            }
        }
    }
}, {
    "$project": {
        "findParent": {
            "$setIntersection": ["$categories", "$parentData"]
        },
        "categories": {
            "$setDifference": ["$categories", "$parentData"]
        }
    }
}, {
    "$unwind": "$findParent"
}, {
    "$project": {
        "category_name": "$findParent.category_name",
        "categories": 1
    }
}).pretty() 

In order to group records by a field try using $group in Aggreegation.This worked for me.

Example

db.categories.aggregate(
   [
     { $group : { _id : {category_name:"$category_name"}, categories: { $push: {category_name:"$category_name",_id:"$_id"} } } }
   ]
)

Reference: MongoDB Aggregation

Hope this works.

本文标签: javascriptHow to group records by a field in MongooseStack Overflow