Rails 3计算嵌入记录的年龄

时间:2013-06-10 16:56:18

标签: ruby-on-rails mongodb date-math

我正在使用mongodb,并且有一个模型可以将注释添加为嵌入式文档。

如何获得参赛作品的平均评论年龄? (相对例子,我的领域有所不同)

所以我可以对条目有很多评论,我需要找出评论的平均年龄或平均:cal_date。对于所有条目/评论或每个条目,其他指标非常适合收集,例如最大:cal_date ...

这有意义吗?需要更多细节?我很高兴有必要得到解决方案。我已经和日期计算混淆了一段时间了。

另一种想到这一点的方法是使用图书馆书籍模型:书籍很多,每本书都有很多签到/入口。我需要找到每本书检出的平均时间所有书籍的平均时间。同样,只是指标,但这些都是日期的事实令人困惑。

{
  _id: ObjectId("51b0d94c3f72fb89c9000014"),
  barcode: "H-131887",
  comments: [
    {
      _id: ObjectId("51b0d94c3f72fb89c9000015"),
      cal_date: ISODate("2013-07-03T16:04:57.893Z"),
      cal_date_due: ISODate("2013-07-03T16:04:57.894Z")
    },
    {
      _id: ObjectId("51b0e6053f72fbb27900001b"),
      cal_date: ISODate("2012-07-03T19:39:43.074Z"),
      cal_date_due: ISODate("2013-07-03T19:39:43.076Z"),
      updated_at: ISODate("2013-06-06T19:41:57.770Z"),
      created_at: ISODate("2013-06-06T19:41:57.770Z")
    }
  ],
  created_at: ISODate("2013-06-06T18:47:40.481Z"),
  creator_id: ObjectId("5170547c791e4b1a16000001"),
  description: "",
  maker: "MITUTOYO",
  model: "2046S",
  serial: "QEL228",
  status: "Out",
  updated_at: ISODate("2013-06-07T18:54:38.340Z")
}

还有一件事 如何使用$push在输出中包含其他字段?我可以让它工作,但它包括,比如条形码,在数组"barcode" => ["H-131887", "H-131887"]

中包含两次

1 个答案:

答案 0 :(得分:1)

你没有说出你想要年龄的时间单位,但我只是告诉你如何在几分钟内恢复它并相信你可以找出如何将其转换为任何其他时间的谷物。我将假设原始文档具有这样的模式:

{ _id: xxx,
  post_id: uniqueId,
  comments: [ { ..., date: ISODate() }, ..., { ... , date: ISODate() } ],
  ...
}

现在聚合:

// first you want to define some fixed point in time that you are calculating age from.
// I'm going to use a moment just before "now"
var now = new Date()-1
// unwind the comments array so you can work with individual comments
var unwind = {$unwind:"$comments"};
// calculate a new comment_age value
var project = {$project: {
       post_id:1, 
       comment_age: {
           $divide:[ 
               {$subtract:[now, "$comments.date"]},
               60000
           ]
       }
} };
// group back by post_id calculating average age of comments
var group = {$group: {
               _id: "$post_id",
               age: {$avg: "$comment_age"}
            } };
// now do the aggregation:

db.coll.aggregate( unwind, project, group )

您可以使用$ max,$ min和其他分组功能查找最早和最新的评论日期或最低/最高评论年龄。您可以按post_id进行分组,也可以按常量分组以查找整个集合的这些计算等。

*编辑* 使用您为“图书馆书籍”所包含的文档作为示例,这可能是为每本书计算当前“Out”已经用了多长时间的管道,假设“comments.cal_date”是签出时的那个,并且所有评论的最新cal_date代表当前的“结账”(旧的已退回):

 db.coll.aggregate( [
    { $match  : { status : "Out"  } },
    { $unwind : "$comments" },
    { $group  : { _id : "$_id", 
                  cal_date : { $max : "$comments.cal_date" } 
                } 
    },
    { $project : { outDuration : { $divide : [ 
                                     { $subtract : [ 
                                                     ISODate("2013-07-15"), 
                                                     "$cal_date" 
                                                   ] 
                                     },
                                     24*60*60*1000 
                                    ] 
                                  }
                  } 
    },
    { $group : { _id : 1, 
                 avgOut : { $avg : "$outDuration" } 
               } 
    } 
 ] )

步骤正在做什么:

  • 根据status过滤掉文档,以便仅对当前Out的图书进行计算。
  • $unwind展开“评论”数组,以便我们
  • 找到哪个条目是最新的cal_date $group$max
  • 使用此最大cal_date(代表书籍签出时)从今天的日期减去它,并将结果除以一天中的毫秒数,以获得本书出版的天数
  • $group将所有结果汇总在一起,以查找所有已检出书籍的平均天数。

*编辑* 我假设您了解Ruby并且只需要知道如何执行聚合框架命令来计算日期差异/平均值/等。以下是Ruby中使用“now”来比较cal_date的相同代码(您也可以使用常量日期值来执行此操作:

# get db collection from MongoClient into variable 'coll'
# see basic MongoDB Ruby driver tutorial for details
coll.aggregate([ 
   { "$match"  => {"status"=>"Out"} }, 
   { "$unwind" => "$comments"}, 
   { "$group"  => { "_id" => "$_id", "cal_date" => { "$max" => "$comments.cal_date" } } },
   { "$project"=> {
                    "outDuration" => { 
                       "$divide" => [ 
                            {"$subtract" => [ Time.now, "$cal_date" ] }, 
                            24*60*60*1000
                       ]
                    }
                  }
   },
   { "$group"  => {
          "_id"    => 1,
          "avgOut" => {"$avg"=>"$outDuration"}
     }
   }  
])

有关更多示例和解释,请参阅https://github.com/mongodb/mongo-ruby-driver/wiki/Aggregation-Framework-Examples

如果您希望在$group阶段保留其他字段,可以通过更改管道步骤添加更多字段,如下所示:

    { $group  : { _id : "$_id", 
                  barcode  : { $first : "$barcode" },
                  cal_date : { $max : "$comments.cal_date" } 
                } 
    } 

如果您不需要原始_id,您可以在第一行(即_id: "$barcode")中使用“$ barcode”而不是“$ _id”,但由于可能有多个字段你想要保留,$first技巧可以和你想要保留的数量一样。