Q:

Query to exclude an array column in MongoDB

Below json represents a document structure of my collection.
{
    "_id" : ObjectId("591a653c366df19100ed0fbc"),
    "sno" : 1,
    "chapterName" : "chapter 1: Express JS",
    "overView" : "Overview of Express JS",
    "sections" : [
            {
                    "title" : "The Node.js philosophy",
                    "subSections" : [
                            {
                                    "sno" : 1,
                                    "title" : "Small core 1",
                                    "content" : "The Node.js core itself has its foundations 1"
                            },
                            {
                                    "sno" : 2,
                                    "title" : "Small core 2",
                                    "content" : "The Node.js core itself has its foundations 2"
                            }
                    ]
            },
            {
                    "title" : "The Node.js philosophy 2",
                    "subSections" : [
                            {
                                    "sno" : 1,
                                    "title" : "Small core 1",
                                    "content" : "The Node.js core itself has its foundations 1"
                            },
                            {
                                    "sno" : 2,
                                    "title" : "Small core 2",
                                    "content" : "The Node.js core itself has its foundations 2"
                            }
                    ]
            }
    ]
}

I would like to write a query which will return data for all records in below mentioned way (excluding "content" )
{
    "_id" : ObjectId("591a653c366df19100ed0fbc"),
    "sno" : 1,
    "chapterName" : "chapter 1: Express JS",
    "overView" : "Overview of Express JS",
    "sections" : [
            {
                    "title" : "The Node.js philosophy",
                    "subSections" : [
                            {
                                    "sno" : 1,
                                    "title" : "Small core 1"

                            },
                            {
                                    "sno" : 2,
                                    "title" : "Small core 2"

                            }
                    ]
            },
            {
                    "title" : "The Node.js philosophy 2",
                    "subSections" : [
                            {
                                    "sno" : 1,
                                    "title" : "Small core 1"

                            },
                            {
                                    "sno" : 2,
                                    "title" : "Small core 2"

                            }
                    ]
            }
    ]
}

Any idea how to achive that ?

A:

So do-able but "not trivial". This requires heavy use of $map with .aggregate()
db.collection.aggregate([
    { "$project": {
      "sno": 1,
      "chapterName": 1,
      "overView": 1,
      "sections": {
        "$map": {
          "input": "$sections",
          "as": "section",
          "in": {
            "title": "$$section.title",
            "subSections": {
              "$map": {
                "input": "$$section.subSections",
                "as": "subSection",
                "in": {
                  "sno": "$$subSection.sno",
                  "title": "$$subSection.title"
                }
              }
            }
          }
        }
      }
    }}
])

I just want to make a mention about "flattening" though, and outside of all the other considerations of why not to have nested arrays, simply consider this data:
{ 
  "_id" : ObjectId("5921745d13fbc408ee20a60a"),
  "a" : 1, 
  "b" : [
    { "a" : 1, "b" : 2, "c" : 3 }, 
    { "a" : 4, "b" : 5, "c" : 6 }
  ]
}

Since that's only one array level deep, if I want to exclude the "c" property then all I need do is:
db.collection.find({},{ "b.c": 0 })

And I get of course:
{ 
  "_id" : ObjectId("5921745d13fbc408ee20a60a"),
  "a" : 1, 
  "b" : [
    { "a" : 1, "b" : 2 }, 
    { "a" : 4, "b" : 5 }
  ]
}

And that's clearly a lot simpler than nesting $map statements in an aggregate projection.
Food for thought.

