Mongodb 在查询中合并变更集文档
我在mongo数据库中记录了信息系统的更改。每次设置或更改一组值时,都会在mongo数据库中保存一条记录 更改集合的格式如下所示:Mongodb 在查询中合并变更集文档,mongodb,Mongodb,我在mongo数据库中记录了信息系统的更改。每次设置或更改一组值时,都会在mongo数据库中保存一条记录 更改集合的格式如下所示: { "user_id": 1, "timestamp": { "date" : "2010-09-22 09:28:02", "timezone_type" : 3, "timezone" : "Europe/Paris" } }, "changes: { "fieldA": "valueA", "fieldB": "valueB", "fieldC": "valu
{ "user_id": 1, "timestamp": { "date" : "2010-09-22 09:28:02", "timezone_type" : 3, "timezone" : "Europe/Paris" } }, "changes: { "fieldA": "valueA", "fieldB": "valueB", "fieldC": "valueC" } }
{ "user_id": 1, "timestamp": { "date" : "2010-09-24 19:01:52", "timezone_type" : 3, "timezone" : "Europe/Paris" } }, "changes: { "fieldA": "new_valueA", "fieldB": null, "fieldD": "valueD" } }
{ "user_id": 1, "timestamp": { "date" : "2010-10-01 11:11:02", "timezone_type" : 3, "timezone" : "Europe/Paris" } }, "changes: { "fieldD": "new_valueD" } }
当然,每个用户有数千条具有不同属性的记录,这些记录代表数百万条记录。我想做的是查看给定时间的用户状态。例如,2010-09-30的用户_id 1将是
fieldA: new_valueA
fieldC: valueC
fieldD: valueD
这意味着我需要将给定用户在给定日期之前的所有更改展平到一个记录中。我可以直接在mongo做吗
编辑:我使用的是mongodb的2.0版本,因此无法从聚合框架中获益
编辑:听起来我找到了问题的答案
var mapTimeAndChangesByUserId = function() {
var key = this.user_id;
var value = { timestamp: this.timestamp.date, changes: this.changes };
emit(key, value);
}
var reduceMergeChanges = function(user_id, changeset) {
var mergeFunction = function(a, b) { for (var attr in b) a[attr] = b[attr]; };
var result = {};
changeset.forEach(function(e) { mergeFunction(result, e.changes); });
return { timestamp: changeset.pop().timestamp, changes: result };
}
reduce函数按更改的顺序合并更改并返回结果
db.user_change.mapReduce(
mapTimeAndChangesByUserId,
reduceMergeChanges,
{
out: { inline: 1 },
query: { user_id: 1, "timestamp.date": { $lt: "2010-09-30" } },
sort: { "timestamp.date": 1 }
});
'results' : [
"_id": 1,
"value": {
"timestamp": "2010-09-24 19:01:52",
"changes": {
"fieldA": "new_valueA",
"fieldB": null,
"fieldC": "valueC",
"fieldD": "valueD"
}
}
]
这对我来说很好。你可以写一个MR来做这件事 由于字段非常类似于标记,您可以在此处修改一个不错的计算标记的烹饪书示例:当然,您不需要计算,而是希望为该字段应用最新的值(假设,因为您的问题中不清楚这一点) 让我们来看看地图功能:
map = function() {
if (!this.changes) {
// If there were not changes for some reason lets bail this record
return;
}
// We iterate the changes
for (index in this.changes) {
emit(index /* We emit the field name */, this.changes[index] /* We emit the field value */);
}
}
现在让我们谈谈我们的计划:
reduce = function(values){
// This part is dependant upon your input query. If you add a sort of
// date (ts) DESC then you will prolly want the first index (0) not the last as
// gathered here by values.length
return values[values.length];
}
这将为类型的每个字段更改输出一个文档:
{
_id: your_field_ie_fieldA,
value: whoop
}
然后,您可以迭代(最有可能)行内输出的末尾,然后,bam,您就有了更改
这当然是一种方式,并不是为了完全按照你的应用程序运行而设计的,但是这一切都取决于你处理的数据的大小;它可能跑得很近
我不确定group
和distinct
是否可以在此基础上运行,但它看起来可能会运行:不过我应该注意,group基本上是一个MR包装器,但您可以执行以下操作(未经测试,就像上面的MR一样):
但它确实需要您定义键,而不是通过编程进行迭代(也许是更好的方法)。您可以在需要的字段上使用聚合框架,使用$group,或者您可以将它们投影到顶级文档中,$unwind then,然后$ADD将它们设置到单个文档中,以创建单个返回文档,但是,前提是在该日期之前不会有太大的变化(可能是一百万?),如果有预先汇总的话,可能是更好的方法。请您发表评论。不幸的是,我使用的是mongodb的2.0版本,而聚合框架似乎只适用于>=2.2的版本。我已经用这句话更新了这个问题。我想在这里说map reduce,我不认为MongoDB的旧的distinct和group函数能够以这种方式工作,也就是说,如果你不能进行预聚合,我现在正在浏览map reduce文档,但是我对关系数据库比较放心。面向文档的数据库是一种不同的思维方式,我正在努力解决。。。我看到有一个
merge()
方法可以合并两个bson文档,但仍然找不到使其工作的方法。一个合并方法?嗯,我认为这可能是特定于驱动程序而不是服务器的,当然没有服务器端的方法来合并文档,除非你指的是map reduce输出的merge
。MapReduce实际上是一个非常简单的任务,它基本上只是一个JavaScript过程,它接收您的文档,发出一些值,然后对它们进行分组,我会给出一些示例代码,但我有点多任务。
db.col.group( {
key: { 'changes.fieldA': 1, // the rest of the fields },
cond: { 'timestamp.date': { $gt: new Date( '01/01/2012' ) } },
reduce: function ( curr, result ) { },
initial: { }
} )