zhm
zhm

Reputation: 3641

How to select flat structure of two level model in MongoDB?

I have a two level structure model in MongoDB, like below:

export class ProductTypeModel {
    _id: ObjectID;
    name: string;
    children: {
        _id: ObjectID,
        name: string,
        icon: string
    }[];
}

It represents the product types in my application. Actually the children has almost the same properties with base model, except it has an extra icon property.

Now I have data like this:

{ 
    "_id" : ObjectId("5b9378d9a842a7557223ebfa"), 
    "name" : "Clothes", 
    "children" : [ { 
        "_id" : ObjectId("5b9378d9a842a7557223ebf6"), 
        "name" : "Men", 
        "icon": "xxx"
    }, { 
        "_id" : ObjectId("5b9378d9a842a7557223ebf7"), 
        "name" : "Women", 
        "icon": "xxx"
    }, { 
        "_id" : ObjectId("5b9378d9a842a7557223ebf8"), 
        "name" : "Shoes", 
        "icon": "xxx"
    }, { 
        "_id" : ObjectId("5b9378d9a842a7557223ebf9"), 
        "name" : "Underwear", 
        "icon": "xxx"
    } ] 
}

I want them to be selected as:

[
    { "_id" : ObjectId("5b9378d9a842a7557223ebfa"), "name" : "Clothes", "parent": null  },
    { "_id" : ObjectId("5b9378d9a842a7557223ebf6"), "name" : "Men", "icon": "xxx", "parent": ObjectId("5b9378d9a842a7557223ebfa") }, 
    { "_id" : ObjectId("5b9378d9a842a7557223ebf7"), "name" : "Women", "icon": "xxx", "parent": ObjectId("5b9378d9a842a7557223ebfa") }, 
    { "_id" : ObjectId("5b9378d9a842a7557223ebf8"), "name" : "Shoes", "icon": "xxx", "parent": ObjectId("5b9378d9a842a7557223ebfa") }, 
    { "_id" : ObjectId("5b9378d9a842a7557223ebf9"), "name" : "Underwear", "icon": "xxx", "parent": ObjectId("5b9378d9a842a7557223ebfa") }
]

Is it possible to do this in one query in MongoDB?

I tried $unwind but it still contains two level structure in results.

Upvotes: 1

Views: 227

Answers (3)

Ashh
Ashh

Reputation: 46461

You can try below aggregation

db.collection.aggregate([
  { "$project": {
    "data": {
      "$map": {
        "input": { "$concatArrays": ["$children", [{ "_id": "$_id", "name": "$name" }]] },
        "in": {
          "_id": "$$this._id",
          "icon": "$$this.icon",
          "name": "$$this.name",
          "parent": { "$cond": [{ "$eq": ["$$this.icon", undefined] }, null, "$_id"] }
        }
      }
    }
  }},
  { "$unwind": "$data" },
  { "$replaceRoot": { "newRoot": "$data" }}
])

Upvotes: 2

dnickless
dnickless

Reputation: 10918

All you need to do is this:

db.collection.aggregate({
    $addFields: {
        "children.parent": "$_id" // push the "_id" field into every array element
    }
}, {
    $addFields: {
        "children": { $concatArrays: [ "$children", [ { "_id": "$_id", "name": "$name", "parent": null } ] ] } // a the parent item into the "children" array
    }
}, {
    $unwind: "$children" // flatten the array
}, {
    $replaceRoot: {
        "newRoot": "$children" // move all content inside the "children" field to the top
    }
})

Upvotes: 0

mdewit
mdewit

Reputation: 2036

This will do the trick:

The parent objects and child objects are separately processed as two facets. At the end, both results are combined into one array, which is then unwinded to give everything as separate documents.

db.collection.aggregate(
{
    "$unwind": {
        "path": "$children"
    },
},
{
    "$facet": {
        "parentObjs": [
            {
                "$group": {
                    "_id": "$_id",
                    "name": { "$first": "$name" }
                }
            },
            {
                "$addFields": { 
                    "parent": null 
                }
            }
        ],
        "childObjs": [
            {
                "$project": {
                    "_id": "$children._id",
                    "name": "$children.name",
                    "icon": "$children.icon",
                    "parent": "$_id"
                }
            }
        ]
    }
},
{
    "$project": { "items": { "$concatArrays": [ "$parentObjs", "$childObjs" ] } }
},
{
    "$unwind": {
        "path": "$items"
    }
}
)

Upvotes: 0

Related Questions