2

Data Structure:

{
  _id: '1',
  'title: 'Blabla',
  comments: [ {id:'3', type:'normal'} , 
              {id: '4', type: 'admin'}, 
              {id: '5', type: 'admin'}, 
              {id: '6', type: 'admin'}
            ]
}

What i am trying to get is an array of last 5 comments of type admin:

{ comments: [{id:4, type:'admin'}, {id: '5', type: 'admin'}, {id: '6', type: 'admin'}] } 

What i have tried:

db.collection('posts').find({_id: '1', 'comments.type':'admin'}
                             , {
                               comments:{$slice: -5}, 
                               comments:1, _id:0
                              })

But it returns the hole comments array if at least 1 item with type admin exists.

7
  • Are the latest defined by incrementing IDs? Or are they just defined by their position in the array? Commented May 8, 2014 at 10:10
  • Then the accepted answer won't actually work, it will get the latest 5 depending upon incrementing ID Commented May 8, 2014 at 10:23
  • Any suggestion on how to achieve my goal? Commented May 8, 2014 at 10:25
  • This is the closest I got: stackoverflow.com/questions/13967569/… at the end of the day you might find it better to do this application side Commented May 8, 2014 at 10:25
  • Ok I have found something: stackoverflow.com/questions/12786686/… the answer by Stennie there shows the use of an undocumented operator $const to add a field containing an array index to each element as he groups it back up, you can sort on that and then proceed with Sebastians answer Commented May 8, 2014 at 10:34

2 Answers 2

4

You can try to use the aggregation framework:

db.collection.aggregate([ 
  { $match: {_id: "1"} }, 
  { $unwind: "$comments" }, 
  { $match: { "comments.type": "admin" } },
  { $sort: { "comments.id": -1 } }, 
  { $limit: 5 } 
])

This will result in a list of the last 5 comments for _id="1":

{ "_id" : "1", "title" : "abc", "comments" : { "id" : "7", "type" : "admin" } }
{ "_id" : "1", "title" : "abc", "comments" : { "id" : "6", "type" : "admin" } }
{ "_id" : "1", "title" : "abc", "comments" : { "id" : "5", "type" : "admin" } }
{ "_id" : "1", "title" : "abc", "comments" : { "id" : "4", "type" : "admin" } }
{ "_id" : "1", "title" : "abc", "comments" : { "id" : "3", "type" : "admin" } }
Sign up to request clarification or add additional context in comments.

3 Comments

I see, while it does work, is it a good option for performance? If i get the hole array, and run it thought 'for' cycle in node to match my needs, would it be slower?
The advantage is that Mongo does not need to send the data to the client to perform the operation. Therefore doing this in the database will be faster.
MongoDB will load everything into memory, the second $match is after a $unwind, as such the entire document will be loaded into memory
0

You can use $elemMatch operator:

db.collection('posts').find(
    {
        _id: '1',
    },
    {
        comments: {
            $elemMatch: {
                'type': 'admin'
            }
        }
    }
)

4 Comments

(edited) Actually it gives only the first element with type='admin' of the array
Is there a way to return more that 1 element of the array?
@dyk not yet, the aggregation framework is for that specific purpose
@dyk yep, I agree that for this purpose you should use aggregation framework

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.