Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB: Trying to use aggregate $unwind to fetch embedded documents

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.

like image 231
Matthew Lucas Avatar asked Feb 22 '13 06:02

Matthew Lucas


2 Answers

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
}
like image 105
Andrew Orsich Avatar answered Sep 29 '22 02:09

Andrew Orsich


I think you want something like this:

db.Users.aggregate( [
{$unwind:"$Photos"},
{$sort: {"Photos.created":-1},
{$limit: 10}
] );
like image 26
Asya Kamsky Avatar answered Sep 29 '22 03:09

Asya Kamsky