SailsJS& MongoDB聚合框架与自定义查询有关

时间:2014-12-15 21:26:37

标签: javascript mongodb mongodb-query sails.js aggregation-framework

我是MongoDB的新手,来自SQL背景。

我试图这样做:

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

数据结构:

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"
  },
  {...}
]

所以我寻求的结果将是这样的:

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

我试图:

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);
  });
});

哪个给了我

[]

并且:

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);
  });
});

哪个给了我

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

由于

1 个答案:

答案 0 :(得分:8)

你的第一个查询是在正确的轨道上,你使用了错误的管道操作符。

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);
        }
})

当然$size运算符需要你需要一个MongoDB 2.6或更高版本,你现在可能应该这样做,但是如果没有运算符来测量数组长度,你仍然可以做同样的事情: / p>

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);
        }
})

通过计算数组的成员来做同样的事情,但是你需要$unwind数组元素来计算它们。所以它仍然可以完成,但效率不高。

此外,由于$unwind处理空数组[]的方式,您需要处理数组真正空白但存在的情况。如果没有内容,那么包含此类元素的文档将从结果中删除。以类似的方式,您需要使用$ifNull来设置一个数组,其中文档甚至不包含$unwind的元素,以免导致错误。

如果您打算定期进行此类查询,那么您应该保持"总计"文档中的字段而不是首先尝试计算它。使用$inc运算符以及$push$pull等操作来保持当前数组长度的计数。

这确实偏离了一般的Waterline哲学,但是你已经引入了原生聚合操作,并且认识到通过在其他领域使用本机操作获得更好的性能并不是一件容易的事情。同样。

所以使用这些文件:

{
  "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"
}

在每种情况下,您都可以获得所需的结果:

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