7

Consider the following is my document stored in collection Users

{
  _id : "User1",
  joined : ISODate("2011-03-02"),
  likes : {
           sublikes: [
                      {WebsiteID:'001': WebsiteName: 'ABCD'},
                      {WebsiteID:'002': WebsiteName: '2BC2'},
                      {WebsiteID:'003': WebsiteName: '3BC3'},
                      //........... 
                      //........... 
                      {WebsiteID:'999999': WebsiteName: 'SOME_NAME'}
                     ]
          }
}

Now using mongodb aggregation framework I need to fetch that

collection.aggregate([
                        { $project: {
                            _id: 1,
                            "UserID": "$_id", 
                            "WebsiteName": "$likes.sublikes[0]['WebsiteName']"
                        }
                        },
                         { $match: { _id: 'User1'} }
                      ], function (err, doc) {
 ///Not able to get the WebsiteName: 'ABCD'
});

If I use $unwind the document becomes bulk (specifically in the above case), So I don't want to unwind it for getting only first item in an array (irrespective of others)

Can anyone give me hints on how to access is and rename that field?


Update 1: Even I tried to project with "WebsiteName": "$likes.sublikes.0.WebsiteName". Didn't work :-(

Update 2: Open issue - https://jira.mongodb.org/browse/SERVER-4589

As of now $at does not work. Throws an error:

{ [MongoError: exception: invalid operator '$at']
  name: 'MongoError',
  errmsg: 'exception: invalid operator \'$at\'',
  code: 15999,
  ok: 0 }

Till then using $unwind

// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};
Amol M Kulkarni
  • 21,143
  • 34
  • 120
  • 164
  • Wouldn't you instead use normal querying with the $slice ( http://docs.mongodb.org/manual/reference/projection/slice/ ) operator like so: `db.col.find({_id: 'User1'}, {'likes.sublikes': {$slice: 1}})` ? – Sammaye Apr 10 '13 at 13:14
  • 1
    The open issue in Jira that you've referenced (SERVER-4589) is a feature request so the `$at` operator does not exist yet. You can vote/watch the feature in Jira to track progress. – Stennie Apr 11 '13 at 06:54
  • 1
    FYI, you should also have your `$match` at the beginning of the aggregation pipeline if you want it to take advantage of an index. See the [Optimizing Performance](http://docs.mongodb.org/manual/core/aggregation/#optimizing-performance) details in the MongoDB documentation. – Stennie Apr 11 '13 at 07:11
  • @Stennie you are absolutely right. Thanks for reminding... – Amol M Kulkarni Apr 11 '13 at 09:43

1 Answers1

10

The easiest way to achieve your result is using a normal find query and the $slice operator:

db.collection.find( {_id: "User1"}, {"likes.sublikes": {$slice: 1}} )

The aggregation framework (as at MongoDB 2.4.1) does not support $slice or array indexes (vote/watch feature requests: SERVER-6074 and SERVER-4589).

You could do this in aggregation framework using $unwind, $group and the $first operator, eg:

db.collection.aggregate([
    { $match: {
         _id : "User1"
    }},
    { $unwind: "$likes.sublikes" },
    { $group: {
        _id: "$_id",
        like: { $first: "$likes.sublikes" }
    }},
    { $project: {
        _id: 0,
        "UserID": "$_id",
        "WebsiteName": "$like.WebsiteName"
    }}
])

The normal $slice should be the most performant option.

Linda Qin
  • 1,036
  • 9
  • 9