【问题标题】:Data type conversion in MongoDBMongoDB中的数据类型转换
【发布时间】:2014-09-30 08:39:28
【问题描述】:

我在 MongoDB 中有一个名为 Document 的集合。此集合中的文档有一个名为 CreationDate 的字段,以 ISO 日期类型存储。我的任务是统计每天创建的文档数量并按数量异步排序。输出格式必须是 [{_id:'yyyy-MM-dd', cnt:x}]。我尝试使用如下聚合框架。

db.Document.aggregate( 
    , {$project: {_id:1, Year:{$year:'$CreationDate'}, Month:{$month:'$CreationDate'}, Date:{$dayOfMonth:'$CreationDate'}}}
    , {$group: {_id:{$concat:['$Year', '-', '$Month', '-', '$Date']}, cnt:{$sum:1}}}
    , {$sort:{'cnt':-1}}
);

代码给我的错误如下:

$concat only supports strings, not NumberInt32

我理解这是因为 $year、$month 和 $dayOfMonth 都返回数字。可以将 _id 字段组合为一个对象,并在应用程序级别将其重新格式化为所需的格式。

但从技术角度来看,我有两个问题:

  1. 如何在 MongoDB shell 中将数字转换为字符串?在这种情况下,然后可以将 $year 的输出转换为字符串并在 $concat 中使用。

  2. 有没有更好的方法将 ISODate 输出格式化为各种日期格式?在许多情况下,我们只需要 ISODate 的某些部分,例如:日期部分或时间部分。是否有任何 MongoDb 内置运算符来实现这一点?

提前感谢您的任何建议。

【问题讨论】:

  • 是否有理由需要在 MongoDB 中完全格式化输出?我只需要编写一个小脚本来根据它接收到的文档来格式化一些字符串输出。否则,您应该查看MapReduce
  • 菲利普,没有理由!我只是在尝试探索。无论如何,谢谢。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

您可以使用$concat 执行此操作,但首先您需要通过$substr 转换为字符串,同时处理两位数的情况:

db.Document.aggregate([ 
    { "$group": { 
        "_id":{ 
            "$concat": [
                 { "$substr": [ { "$year": "$CreationDate" }, 0, 4 ] },
                 "-",
                 { "$cond": [
                     { "$gt": [ { "$month": "$CreationDate" }, 9 ] },
                     { "$substr": [ { "$month": "$CreationDate" }, 0, 2 ] },
                     { "$concat": [
                         "0",
                         { "$substr": [ { "$month": "$CreationDate" }, 0, 1 ] },
                     ]},
                 ]},
                 "-",
                 { "$cond": [
                     { "$gt": [ { "$dayOfMonth": "$CreationDate" }, 9 ] },
                     { "$substr": [ { "$dayOfMonth": "$CreationDate" }, 0, 2 ] },
                     { "$concat": [
                         "0",
                         { "$substr": [ { "$dayOfMonth": "$CreationDate" }, 0, 1 ] },
                     ]}
                 ]}
             ]
         },
         { "cnt": { "$sum": 1 } }
    }}
    { "$sort":{ "cnt" :-1 }}
]);

可能更好的是只使用日期数学,这会返回一个纪元时间戳值,但在后期处理中很容易处理日期对象:

db.Document.aggregate([
    { "$group": {
        "_id": {
            "$subtract": [
                { "$subtract": [ "$CreationDate", new Date("1970-01-01") ] },
                { "$mod": [
                    { "$subtract": [ "$CreationDate", new Date("1970-01-01") ] },
                    1000 * 60 * 60 * 24
                ]}
            ]
        },
        "cnt": { "$sum": 1 }
    }},
    { "$sort": { "cnt": -1 } }
])

【讨论】:

  • +1 表示$substr$concat 需要一个字符串,但 $substr 却没有……这有点奇怪……不过,这是一种有趣的“投射”方式。
  • 在您的 $cond 语句中,“$gt”应该是“$lte”,这样如果该值小于或等于 9,则计算真实值并导致在前面加上“0”字符串. docs.mongodb.com/manual/reference/operator/aggregation/cond "{ $cond: [ , , ] }"
【解决方案2】:

将 ISODate 转换为各种日期格式的另一种更简单的方法是使用 $dateToString aggregation operator

db.collection.aggregate([
    { $group: {
        _id: { $dateToString: { format: "%Y-%m-%d %H:%M", date: "$CreationDate" } },
        count: { $sum: 1 }
    }},
    { $sort : { count: -1 }}
])

【讨论】:

  • 谢谢,伙计。认为这是 2.6 及更高版本中的新功能。我之前使用的是 2.4。
  • 实际上它是 3.0 版中的新功能。 根据文档(答案中提供的链接)
【解决方案3】:

尼尔的回答是正确的,但有一个小错误。在条件句中(检查月数和日数是否需要在前面加上 0)不是$gt,而是$lte

对于$lte,只有一位数的月份和日期会被 0 前置。

例如:2014-10-03、2014-08-03.

【讨论】:

  • 这将更适合作为编辑或什至评论尼尔的答案...(我猜你没有足够的代表做这件事,对吧?)
【解决方案4】:

对于 MONGO >= 3.0(~2015 之后)

如果一些可怜的灵魂在 2017 年偶然发现这个问题,比如我: 从 Mongo 3.0 开始,现在有一个 dateToString 运算符可用

这意味着如果你有正确的 Date(),你应该能够简单地做:

db.Document.aggregate( 
    , {$project: {_id:1, CreationDate:1}
    , {$group: {
         _id : { $dateToString: { format: "%Y-%m-$d", date: "$CreationDate" } },
         cnt:{$sum:1}}}
    , {$sort:{'cnt':-1}}
);

对于我们这些碰巧将日期存储在非日期字段中的人(真高兴!),您可以在项目步骤中创建一个新的 Date() 对象。 在我的例子中,日期存储为毫秒数(整数),我将毫秒数添加到 0-Date。

db.Document.aggregate( 
    , {$project: {
         _id:1, 
         CreationDate: {"$add": [ new Date(0), "$CreatedOn" ]}
}
    , {$group: {
         _id : { $dateToString: { format: "%Y-%m-$d", date: "$CreationDate" } },
         cnt:{$sum:1}}}
    , {$sort:{'cnt':-1}}
);

【讨论】:

    【解决方案5】:

    将日期格式转换为“xxxx-xx-xx”

    db.getCollection('analytics').aggregate([
     {$project: {
         day: {$dayOfMonth: "$createdAt"},
         month: {$month: "$createdAt"},
         year: {$year: "$createdAt"},
         }
      },
      {$project: {
          day: {$concat:["0", {$substr:["$day",0, 2]}]},
         month: {$concat:["0", {$substr:["$month",0, 2]}]},
         year: {$substr:["$year",0, 4]},
          }
      },
      {$project: {date: {$concat: [{$substr:["$year",0, 4]},"-", {$substr:["$month",0, 2]},"-", {$substr:["$day",0, 2]}]}}},
    ]);
    

    【讨论】:

      猜你喜欢
      • 2021-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-18
      • 1970-01-01
      相关资源
      最近更新 更多