我有一个mongodb集合,其中包含以下文档:
{
"_id" : ObjectId("safdsd435tdg54trgds"),
"startDate" : ISODate("2013-07-02T17:35:01.000Z"),
"endDate" : ISODate("2013-08-02T17:35:01.000Z"),
"active" : true,
"channels" : [
1, 2, 3, 4
],
}
我想把它转换成这样的东西:
{
"_id" : ObjectId("safdsd435tdg54trgds"),
"startDate" : ISODate("2013-07-02T17:35:01.000Z"),
"endDate" : ISODate("2013-08-02T17:35:01.000Z"),
"active" : true,
"channels" : [
1, 2, 3, 4
],
"tags" :[
{
"name": one
"type": channel
},
{
"name": two
"type": channel
},
{
"name": three
"type": channel
},
{
"name": four
"type": channel
}
]
}
我已经有1,2,3,4表示的映射。为了简单起见,我把它们作为字母格式。值可能不同,但它们是静态映射。
答案 0 :(得分:2)
你似乎试图在没有大量迭代的情况下进行此更新,所以你“可以”使用mapReduce做到这一点,虽然它采用非常“mapReduce方式”,因为它有自己的处理方式。
首先,您要定义一个封装当前文档的映射器:
var mapFunction = function (){
var key = this._id;
var value = {
startDate: this.startDate,
endDate: this.endDate,
active: this.active,
channels: this.channels
};
emit( key, value );
};
现在 reducer 实际上不会被调用,因为 mapper 中的所有键都是唯一的,当然是来自_id
的{{1}}值原始文档。但是为了让电话快乐:
var reduceFunction = function(){};
由于这是一对一,因此最终确定。它可以在映射器中,但为了清洁起见
var finalizeFunction = function (key, reducedValue) {
var tags = [
{ name: "one", type: "channel" },
{ name: "two", type: "channel" },
{ name: "three", type: "channel" },
{ name: "four", type: "channel" }
];
reducedValue.tags = [];
reducedValue.channels.forEach(function(channel) {
reducedValue.tags.push( tags[ channel -1 ] );
});
return reducedValue;
};
然后调用mapReduce:
db.docs.mapReduce(
mapFunction,
reduceFunction,
{
out: { replace: "newdocs" },
finalize: finalizeFunction
}
)
这样会输出到新集合,但是就像mapReduce那样,你就有了这个:
{
"_id" : ObjectId("53112b2d0ceb66905ae41259"),
"value" : {
"startDate" : ISODate("2013-07-02T17:35:01Z"),
"endDate" : ISODate("2013-08-02T17:35:01Z"),
"active" : true,
"channels" : [ 1, 2, 3, 4 ],
"tags" : [
{
"name" : "one",
"type" : "channel"
},
{
"name" : "two",
"type" : "channel"
},
{
"name" : "three",
"type" : "channel"
},
{
"name" : "four",
"type" : "channel"
}
]
}
}
因此,_id
以外的所有文档字段都位于value
字段下,因此这不是您想要的文档。但这是如何 mapReduce的工作原理。
如果你真的需要从退出监狱并愿意稍等一下,那么即将发布的2.6版本已经添加了一个$out
管道阶段。因此,您“可以”使用$project
转换新集合中的文档,如下所示:
db.newdocs.aggregate([
// Transform the document
{"$project": {
"startDate": "$value.startDate",
"endDate": "$value.endDate",
"active": "$value.active",
"channels": "$value.channels",
"tags": "$value.tags"
}},
// Output to new collection
{"$out": "fixeddocs" }
])
这样才对。但当然这不是你的原创系列。因此,要返回该状态,您将需要.drop()
个集合并使用.renameCollection()
:
db.newdocs.drop();
db.docs.drop();
db.fixeddocs.renameCollection("docs");
现在请仔细阅读阅读文档,有几个限制,当然你也必须重新创建索引。
所有这些,特别是最后一个阶段将导致磁盘抖动的很多,并且还要记住您在此处删除集合。几乎可以肯定的是,在执行此操作时离线访问您的数据库。
即便如此,危险在这里也足够真实,也许您可以使用任意JavaScript来运行迭代循环来更新文档。如果你真的必须这样做,你总是可以使用db.eval()
来完成所有操作。但如果,那么请仔细阅读文档。
但是为了完整性,即使我不提倡这个:
db.eval(function(){
db.docs.find().forEach(function(document) {
var tags = [
{ name: "one", type: "channel" },
{ name: "two", type: "channel" },
{ name: "three", type: "channel" },
{ name: "four", type: "channel" }
];
document.tags = [];
document.channels.forEach(function(channel) {
document.tags.push( tags[ channel -1 ] );
});
var id = document._id;
delete document._id;
db.docs.update({ "_id": id },document);
});
})