【问题标题】:How to alter a collection structure in mongodb?如何更改 mongodb 中的集合结构?
【发布时间】:2014-06-01 05:24:31
【问题描述】:

我有一个类似的 mongodb 集合

{
"_id" : 57,
"value" : {
    "user" : [ 
     {
            "fk_status_id"  : "0",
            "firstname"     : "Ajith",
            "lastname"      : "S",
            "city"          : "known",
            "State"         :"kerala",          
            "location" : {
                "lat" : 34.123456,
                "lon" : -95.123456
            },
     }

    ],
}}

拥有数百万个文档

我想像这样改变表结构

{
"_id" : 58,
    "user" : [ 
     {
            "fk_status_id"  : "0",
            "firstname"     : "Ajith",
            "lastname"      : "S",
            "city"          : "known",
            "State"         :"kerala",          
            "location" : {
                "lat" : 34.123456,
                "lon" : -95.123456
            },
     }

    ]}

我的意思是,我想从这个结构中省略“值{}”。

  • 数据应该在“值”之外。 请解决我的问题。

【问题讨论】:

  • 你已经分片了吗?您可以直接在数据库节点上连接到 shell 吗?
  • 不..它没有分片

标签: php mongodb collections alter-table


【解决方案1】:

嗯,你没有回答给出的评论,但它很重要,原因将被解释。

因此,如果您确实有“数百万个文档”需要立即更改并快速执行此操作,那么您将面临一个明显的问题。由于没有“原子”操作可以通过“引用”现有字段中的值来应用于更新来对文档进行操作,因此执行此操作的唯一方法是实际循环结果:

db.collection.find().forEach(function(doc) {
    var user = doc.value.user;

    delete doc.value;

   db.collection.update(
       { "_id": doc._id },
       { "$set": { "user": user } }
   );

})

这是基本过程,对于大型数据集来说有点可怕。

但是对于“一次性”转换,有一种方法可以做到这一点,尽管它不是一个非常好的方法,它被称为 db.eval() 方法。但请注意文档(来自手册页的 Big Rip):

警告

  • 默认情况下,db.eval() 在评估 JavaScript 函数之前采用全局写锁。因此,db.eval() 会在 db.eval() 操作运行时阻止对数据库的所有其他读写操作。在 eval 命令上将 nolock 设置为 true 以防止 eval 命令在评估 JavaScript 之前获取全局写锁。 nolock 不会影响 JavaScript 代码本身的操作是否需要写锁。

  • 不要将 db.eval() 用于长时间运行的操作,因为 db.eval() 会阻止所有其他操作。考虑使用other server side code execution options

  • 您不能将 db.eval() 与 sharded 数据一起使用。一般来说,您应该避免在sharded cluster 中使用db.eval();不过,可以将 db.eval() 与存储在 sharded cluster 中的非分片集合和数据库一起使用。

  • 启用身份验证后,如果您没有执行指定任务的权限,db.eval() 将在操作过程中失败。

在 2.4 版中更改:您必须拥有完全的管理员权限才能运行。

除此之外,如果这确实符合您的情况,那么我们不必成为鸵鸟(并将我们的头埋在沙子“神话”中)并实际做一些可以在服务器上运行的事情来做到这一点:

db.eval(function() {

    db.collection.find().forEach(function(doc) {
        var user = doc.value.user;

        delete doc.value;

        db.collection.update(
            { "_id": doc._id },
            { "$set": { "user": user } }
        );

    });

});

当然,正如建议的那样,还有其他方法可以解决这个问题。

  • mapReduce 可以在没有锁定问题的情况下做到这一点,但输出仍然需要大量重塑以及集合重命名和替换。

  • 实际在服务器上运行(或在网络上最接近的服务器),这可能是最好的选择。通过这种方式,您可以安全地编码并避免扩展锁定问题。

但是,如果您真的“陷入困境”,那么服务器上的 JavaScript 执行将解决您需要的特定问题。

【讨论】:

    猜你喜欢
    • 2019-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多