javascript - SailsJS & MongoDB Aggregation framework troubles with custom queries - Stack Overflow

I'm totally new to MongoDB and e from an SQL background.I'm trying to do this : Get the top A

I'm totally new to MongoDB and e from an SQL background.

I'm trying to do this :

Get the top Artists, based on the number of Dubs.

Data Structure :

Artists = [
  {
    "dubs": [{...},{...},{...}],
    "name": "The Doors",
    "createdAt": "2014-12-15T15:24:26.216Z",
    "updatedAt": "2014-12-15T15:24:26.216Z",
    "id": "548efd2a436c850000353f4f"
  },
  {
    "dubs": [],
    "name": "The Beatles",
    "createdAt": "2014-12-15T20:30:33.922Z",
    "updatedAt": "2014-12-15T20:30:33.922Z",
    "id": "548f44e90630d50000e2d61d"
  },
  {...}
]

So the result I seeking for would be something like this :

[{
 _id: "548ef6215755950000a9a0de",
 name:"The Doors",
 total: 3
},{
 _id: "548ef6215715300000a9a1f9",
 name:"The Beatles",
 total: 0
}]

I tried to :

Artist.native(function(err, collection) {

  collection.aggregate([ {
    $group: {
      _id: {
        name: "$name"
      },
      total: {
        $size: "$dubs"
      }
    }
  }, {
    $size: {
      total: -1
    }
  }], function(e, r) {
    if (e) res.serverError(e);
    console.log(r);
  });
});

Which gives me

[]

And :

Artist.native(function(err, collection) {
  if (err) return res.serverError(err);

  collection.aggregate({
    $group: {
      _id: "$name",
      total: {
        $sum: 1
      }
    }
  }, {
    $sort: {
      total: -1
    }
  }, function(e, r) {
    console.log(r);
    if (e) return res.serverError(e);
  });
});

Which gives me

[ { _id: 'The Beatles', total: 1 },
{ _id: 'The Doors', total: 1 } ]

Thanks

I'm totally new to MongoDB and e from an SQL background.

I'm trying to do this :

Get the top Artists, based on the number of Dubs.

Data Structure :

Artists = [
  {
    "dubs": [{...},{...},{...}],
    "name": "The Doors",
    "createdAt": "2014-12-15T15:24:26.216Z",
    "updatedAt": "2014-12-15T15:24:26.216Z",
    "id": "548efd2a436c850000353f4f"
  },
  {
    "dubs": [],
    "name": "The Beatles",
    "createdAt": "2014-12-15T20:30:33.922Z",
    "updatedAt": "2014-12-15T20:30:33.922Z",
    "id": "548f44e90630d50000e2d61d"
  },
  {...}
]

So the result I seeking for would be something like this :

[{
 _id: "548ef6215755950000a9a0de",
 name:"The Doors",
 total: 3
},{
 _id: "548ef6215715300000a9a1f9",
 name:"The Beatles",
 total: 0
}]

I tried to :

Artist.native(function(err, collection) {

  collection.aggregate([ {
    $group: {
      _id: {
        name: "$name"
      },
      total: {
        $size: "$dubs"
      }
    }
  }, {
    $size: {
      total: -1
    }
  }], function(e, r) {
    if (e) res.serverError(e);
    console.log(r);
  });
});

Which gives me

[]

And :

Artist.native(function(err, collection) {
  if (err) return res.serverError(err);

  collection.aggregate({
    $group: {
      _id: "$name",
      total: {
        $sum: 1
      }
    }
  }, {
    $sort: {
      total: -1
    }
  }, function(e, r) {
    console.log(r);
    if (e) return res.serverError(e);
  });
});

Which gives me

[ { _id: 'The Beatles', total: 1 },
{ _id: 'The Doors', total: 1 } ]

Thanks

Share Improve this question edited Jun 29, 2017 at 4:22 Neil Lunn 151k36 gold badges355 silver badges325 bronze badges asked Dec 15, 2014 at 21:26 Philippe DavidPhilippe David 8,9243 gold badges26 silver badges35 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 8

Your first query was on the right track back you were using the the wrong pipeline operator.

Artist.native(function(err,collection) {

    collection.aggregate(
        [
            { "$project": {
                "_id": 1,
                "name": 1,
                "total": { "$size": "$dubs" }
            }}
        ],
        function(err,result) {
          if (err) return res.serverError(err);
          console.log(result);
        }
})

Of course the $size operator there requires that you need a MongoDB 2.6 or greater version, which you probably should do by now, but you can still to the same thing without the operator for measuring the array length:

Artist.native(function(err,collection) {

    collection.aggregate(
        [
            { "$project": {
                "_id": 1,
                "name": 1,
                "dubs": {
                    "$cond": [
                       { "$eq": [ "$dubs", [] ] },
                       [0],
                       "$dubs"
                    ]
                }
            }},
            { "$unwind": "$dubs" },
            { "$group": {
                "_id": "$_id",
                "name": { "$first": "$name" },
                "total": { 
                    "$sum": {
                        "$cond": [
                            { "$eq": [ "$dubs", 0 ] },
                            0,
                            1
                        ]
                    }
                }
            }}
        ],
        function(err,result) {
          if (err) return res.serverError(err);
          console.log(result);
        }
})

That does the same thing by counting the members of the array, but instead you would need to $unwind the array elements in order to count them. So it can still be done but is not as efficient.

Additionally you need to handle the cases where the array is truly blank but present because of how $unwind treats an empty array []. If there was no content then the document that contained such an element would be removed from the results. In a similar way you would need to use $ifNull to set an array where the document did not even contain an element for $unwind to not result in an error.

Really if you intend to do this kind of query on a regular basis, then you should maintain a "total" field in the document rather than seek to calculate it first. Use the $inc operator along with operations such as $push and $pull to keep a tally of the current array length.

That does move away from the general Waterline philosophy a bit, but you have already introduced native aggregation operations and it's not that much more of a stretch to realize you are getting better performance from using native operations in other areas as well.

So with documents like these:

{
  "dubs": [{},{},{}],
  "name": "The Doors",
  "createdAt": "2014-12-15T15:24:26.216Z",
  "updatedAt": "2014-12-15T15:24:26.216Z",
  "id": "548efd2a436c850000353f4f"
},
{
  "dubs": [],
  "name": "The Beatles",
  "createdAt": "2014-12-15T20:30:33.922Z",
  "updatedAt": "2014-12-15T20:30:33.922Z",
  "id": "548f44e90630d50000e2d61d"
}

You get exactly the results you want in each case:

{
    "_id" : ObjectId("5494b79d7e22da84d53c8760"),
    "name" : "The Doors",
    "total" : 3
},
{
    "_id" : ObjectId("5494b79d7e22da84d53c8761"),
    "name" : "The Beatles",
    "total" : 0
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744772901a4592868.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信