MongoDB: Trying to use aggregate $unwind to fetch embedded documents
Asked Answered
A

2

5

I have a MongoDB collection [Users] each with an Array of embedded documents [Photos]. I would like to be able to have a page which lists recent photos, for example - the last 10 photos uploaded by any user.

Users

[
    {
        _id: ObjectID
        name: "Matthew"
        Photos: [
            {
                _id: ObjectID
                url: "http://www.example.com/photo1.jpg"
                created: Date("2013-02-01")
            },
            {
                _id: ObjectID
                url: "http://www.example.com/photo3.jpg"
                created: Date("2013-02-03")
            }
        ]
    },
    {
        _id: ObjectID
        name: "Bob"
        Photos: [
            {
                _id: ObjectID
                url: "http://www.example.com/photo2.jpg"
                created: Date("2013-02-02")
            },
            {
                _id: ObjectID
                url: "http://www.example.com/photo4.jpg"
                created: Date("2013-02-04")
            }
        ]
    }
]

My first attempt at solving this was to find all Users where Photos is not null, and sort it by the created date:

User.find({images:{$ne:[]}}).sort({"images.created":-1})

Unfortunately, this doesn't work as I need it to. It sorts Users by the most recent images, but still returns all images, and there is no way to limit the number of images returned by the function.

I started looking into aggregate, and it seems like it might be the solution I'm looking for, but I'm having trouble finding out how to get it to work.

Ideally, the type of result I would like returned would be like this:

results: [
    {
        _id: ObjectID (from Photo)
        name: "Bob"
        url: "http://www.example.com/photo4.jpg"
        created: Date("2013-02-04")
    }
    {
        _id: ObjectID (from Photo)
        name: "Matthew"
        url: "http://www.example.com/photo3.jpg"
        created: Date("2013-02-03")
    }
]

Each result should be a Photo, and I should be able to limit the results to a specific amount, and skip results for paged viewing.

Please let me know if you need any more information, and thank you for your responses.

Annitaanniversary answered 22/2, 2013 at 6:51 Comment(0)
I
7

You need aggregation framework:

db.users.aggregate(
  { $project: {"Photos" : 1, _id: 0, name:1 }},
  { $unwind: "$Photos" },
  { $sort: {"Photos.created" : -1} },
  { $skip: 1 },
  { $limit: 2 }
)

And result would be like this:

{
    "result" : [ 
        {
            "name" : "Matthew",
            "Photos" : {
                "_id" : 2,
                "url" : "http://www.example.com/photo3.jpg",
                "created" : "2013-02-03"
            }
        }, 
        {
            "name" : "Bob",
            "Photos" : {
                "_id" : 3,
                "url" : "http://www.example.com/photo2.jpg",
                "created" : "2013-02-02"
            }
        }
    ],
    "ok" : 1
}
Isochromatic answered 22/2, 2013 at 7:13 Comment(2)
Thank you, that worked. I was so close to coming to this solution on my own, but what I was doing wrong was that I had all of the pipelines together in one object.Annitaanniversary
You are welcome! Next time just go to the mongodb docs to avoid such mistakes ;) -> docs.mongodb.org/manual/applications/aggregationIsochromatic
S
1

I think you want something like this:

db.Users.aggregate( [
{$unwind:"$Photos"},
{$sort: {"Photos.created":-1},
{$limit: 10}
] );
Sheepshank answered 22/2, 2013 at 6:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.