Michael Seltenreich
Michael Seltenreich

Reputation: 3488

Counting nested arrays with mongoose

I'm using mongoDB through mongoose in a node.js application.

In my db I have this general format:

Books:

BookSchema = {
variousData,
//Books contain pages
pages: [{type: pageSchema}]
}

Pages:

pageSchema = {
variousData,
//Pages contain frames
frames: [frameSchema]
}

Frames:

frameSchema = {
variousData
}

I want to write a count query that will count the total number of frames in all pages in all books.

So if I have this content in my db:

    Book1 = {
        data:data,
        pages: [
            {data:data,
             frames: [frame1,frame2,frame3]
            },
            {data:data,
                frames: [frame1,frame2,frame3]
            },
            {data:data,
                frames: [frame1,frame2,frame3]
            }
        ]
    }

and

    Book2 = {
        data:data,
        pages: [
            {data:data,
            frames: [frame1,frame2]
            },
            {data:data,
            frames: [frame1,frame2]
            },
            {data:data,
            frames: [frame1,frame2]
            }
        ]
    }

I'm expecting to get an end result of 15 (9 frames in book 1 and 6 frames in book 2)

All my attempts ended up with errors.

I can write a loop, but I feel like mongoose would have a better alternative.

Any suggestions?

Thanks all!

Upvotes: 2

Views: 2054

Answers (1)

Blakes Seven
Blakes Seven

Reputation: 50406

The most efficient way would be like this:

  Book.aggregate([
    { "$project": {
      "pages": { 
        "$map": {
          "input": "$pages",
          "as": "page",
          "in": { "$size": "$$page.frames" }
        }}
    }},
    { "$unwind": "$pages" },
    { "$group": {
      "_id": null,
      "pages": { "$sum": "$pages" }
    }}
  ],function(err,results) {

  })

Basically there the $map operator allows you to traverse the elements of the "pages" array, take a look at each "frames" array and return the $size. Now the content of "pages" is just an array of sizes for each "frames", and therefore the count.

Then all you really need to do is "add" those together. So you $unwind the array and $sum all the page counts for each frame together with a $group.

Upvotes: 2

Related Questions