javascript - 返回零计数,其中不存在数据

标签 javascript node.js mongodb mongodb-query aggregation-framework

我的mongodb数据收集中的文档采用这种格式,

[ 
  {
    "_id": xxxxxxxxx,
    "crime_type": "illegal_trade",
    "crime_year": "2013",
    "location": "Kurunegala"
  },
  {
    "_id": xxxxxxxxx,
    "crime_type": "illegal_trade",
    "crime_year": "2013",
    "location": "Colombo"
  },
  {
    "_id": xxxxxxxxx,
    "crime_type": "illegal_trade",
    "crime_year": "2014",
    "location": "Kandy"
  },
  {
    "_id": xxxxxxxxx,
    "crime_type": "murder",
    "crime_year": "2013",
    "location": "Kadawatha"
  }
]


当我运行此聚合操作时,

db.collection.aggregate(
   [
     { $group : { _id : {type: "$crime_type", year: "$crime_year"}, count: { $sum: 1 } } }
   ]
)


结果仅包含具有count > 0的项目

例如,_id : {type: "murder", year: "2014"}的结果中不包含count = 0的结果。

我的问题是
我应该如何更改查询,以使这些count = 0项目也出现在结果中?

换句话说,如何使用mongodb ...做类似this的事情?

最佳答案

基本上,您要的是数据中不存在的结果,因此,在不存在该键组合的情况下,您将返回计数0。实际上,没有数据库系统“真正”做到这一点,但是有一些方法可以使看起来正在发生。但这也意味着了解真正发生的事情。

确实,SQL的解决方法是从不同的值对期望的键进行子查询,然后将其“联接”到现有数据集,以创建用于分组累积的“误报”。那是那里的通用方法,但是当然有一个基本的“连接”概念,由于可伸缩性,MongoDB不支持该概念。那里有完全不同的论点,只是接受MongoDB不会在其自己的服务器体系结构上进行联接,而且可能永远不会这样做。

这样,在与MongoDB一起工作时创建“假集”的任务将移交给客​​户端(并且仅从“客户端”的角度考虑这是数据库服务器的独立过程)操作。因此,您基本上可以同时获得“结果集”和“空白集”,并“合并”结果。

不同的语言方法各不相同,但以下是node.js的有效列表:

var async = require('async'),
    mongo = require('mongodb'),
    MongoClient = mongo.MongoClient,
    DataStore = require('nedb'),
    combined = new DataStore();

var data = [
  {
    "crime_type": "illegal_trade",
    "crime_year": "2013",
    "location": "Kurunegala"
  },
  {
    "crime_type": "illegal_trade",
    "crime_year": "2013",
    "location": "Colombo"
  },
  {
    "crime_type": "illegal_trade",
    "crime_year": "2014",
    "location": "Kandy"
  },
  {
    "crime_type": "murder",
    "crime_year": "2013",
    "location": "Kadawatha"
  }
];


MongoClient.connect('mongodb://localhost/test',function(err,db) {

  if (err) throw err;

  db.collection('mytest',function(err,collection) {
    if (err) throw err;

    async.series(
      [
        // Clear collection
        function(callback) {
          console.log("Dropping..\n");
          collection.remove({},callback);
        },

        // Insert data
        function(callback) {
          console.log("Inserting..\n");
          collection.insert(data,callback);
        },

        // Run parallel merge
        function(callback) {
          console.log("Merging..\n");
          async.parallel(
            [
              // Blank Distincts
              function(callback) {
                collection.distinct("crime_year",function(err,years) {
                  if (err) callback(err);
                  async.each( years, function(year,callback) {
                    collection.distinct("crime_type",function(err,types) {
                      if (err) callback(err);
                      async.each( types, function(type,callback) {
                        combined.update(
                          { "type": type, "year": year },
                          { "$inc": { "count": 0 } },
                          { "upsert": true },
                          callback
                        );
                      },callback);
                    });
                  },callback);
                });
              },

              // Result distincts
              function(callback) {
                collection.aggregate(
                  [
                    { "$group": {
                      "_id": {
                        "type": "$crime_type",
                        "year": "$crime_year"
                      },
                      "count": { "$sum": 1 }
                    }}
                  ],
                  function(err,results) {
                    async.each( results, function(result, callback) {
                      combined.update(
                        { "type": result._id.type, "year": result._id.year },
                        { "$inc": { "count": result.count } },
                        { "upsert": true },
                        callback
                      );
                    },callback);

                  }
                );
              }
            ],
            function(err) {
              callback(err);
            }
          )

        },

        // Retrieve result
        function(callback) {
          console.log("Fetching:\n");
          combined.find({},{ "_id": 0 }).sort(
            { "year": 1, "type": 1 }).exec(function(err,results) {
            if (err) callback(err);
            console.log( JSON.stringify( results, undefined, 4 ) );
            callback();
          });
        }
      ],
      function(err) {
        if (err) throw err;
        db.close();
      }
    )

  });

});


这将返回一个结果,该结果不仅“组合”了分组密钥的结果,而且还包含了“ 2014”年中“谋杀”的0条目:

[
    {
        "type": "illegal_trade",
        "year": "2013",
        "count": 2
    },
    {
        "type": "murder",
        "year": "2013",
        "count": 1
    },
    {
        "type": "illegal_trade",
        "year": "2014",
        "count": 1
    },
    {
        "type": "murder",
        "year": "2014",
        "count": 0
    }
]


因此,请考虑一下此处的操作内容,主要是在“合并”下的代码的“并行”部分中,因为这是节点在节点上发出所有查询(可能相当多)的一种有效方法。同时。

为了获得无计数的“空白”结果的第一部分本质上是一个双循环操作,其中重点是为“年”和“类型”中的每一个获取不同的值。是否使用此处显示的.distinct()方法或使用带“光标”的.aggregate()方法进行输出和迭代取决于您拥有多少数据或个人喜好。对于较小的集合,则.distinct()可以将结果存储在内存中。但是我们想为每个可能的配对创建“空白”或0计数条目,或更重要的是,将“不存在”的那些作为配对在数据集中创建。

其次,在可能的情况下,并行运行汇总结果和标准结果。当然,这些结果不会返回“ 2014”中的“谋杀”计数,因为没有结果。但这基本上归结为合并结果。

“合并”基本上是与“年份”和“类型”组合键的“哈希/映射/字典”(无论您使用什么术语)一起使用的。因此,您只需使用该结构,在不存在的键上添加键,或在不存在的键上增加“计数”值。这是一个古老的操作,基本上是所有聚合技术的基础。

这里要做的一件整洁的小事情(不是您需要使用它)是使用nedb,这是一个很好的小模块,允许对内存中或其他自包含数据使用MongoDB的“ like”操作文件。可以将其视为从SQLite到SQL RDBMS的操作。完整功能仅需轻一点。

这里的部分意思是“哈希合并”函数现在看起来像对代码的常规MongoDB "upsert"操作。实际上,如果您需要在服务器上以“结果集合”结尾的较大结果,则基本上使用相同的代码。

总的说来,这实际上是一个“加入”操作,否则就是“填补空白”操作,具体取决于操作中“键”的总体大小和期望值。 MongoDB服务器将不会执行此操作,但是没有什么阻止您编写实际上是您自己的“数据层”作为最终应用程序和数据库之间的中间层的内容。可以扩展这种分布式服务器模型,以便此服务级别执行此类“连接”操作。

所有用于合并数据的查询都可以在正确的编码环境下有效地并行运行,因此尽管这看起来不像SQL方法那样简单,但实际上它仍然非常有效。处理结果。

方法是不同的,但这又是此处哲学的一部分。 MongoDB将“联接”活动与应用程序体系结构的不同部分进行后退,以使其特定于服务器的特定操作更加有效,并且主要针对分片群集。 “联接”或此“哈希合并”是“代码”功能,可由数据库服务器以外的其他基础结构处理。

关于javascript - 返回零计数,其中不存在数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27679415/

相关文章:

javascript - 使用 php、ajax 和 javascript 循环仅执行最后一个数组元素

javascript - 为什么我收到 "Cannot find module ' jsonLogic'"错误?

node.js - nodeclipse:安装后在 Linux 上找不到命令

node.js - Mongoose 聚合、匹配、计数、分组

mongodb - 如何在mongoDB中加入数据库并获取所需结果

javascript - 空字符串是所有字符串的子串吗?

javascript - 使用 Javascript 或 jquery 动态显示多个图像

javascript - 将 tidy-html5 与内联样式一起使用

mongodb - 在合理的时间内使用 mongoDB 检索大量记录

mongodb - 验证 Mongoose 混合架构类型