MongoDB:如何更改字段的类型?

J. *_*tas 145 mongodb

Stackoverflow中已经存在一个问题,与我的问题非常相似.问题是这个问题的答案是针对Java驱动程序,我试图在shell中进行.

我这样做......

db.meta.update(
  {'fields.properties.default': { $type : 1 }}, 
  {'fields.properties.default': { $type : 2 }}
)
Run Code Online (Sandbox Code Playgroud)

这不行!

Gat*_* VP 199

更改$type数据的唯一方法是对数据具有正确类型的数据执行更新.

在这种情况下,看起来你正试图$type 将从1(双)改为2(字符串).

因此,只需从DB加载文档,执行cast(new String(x)),然后再次保存文档.

如果您需要以编程方式完全从shell执行此操作,则可以使用find(...).forEach(function(x) {})语法.


回应下面的第二条评论.将字段bad从数字更改为集合中的字符串foo.

db.foo.find( { 'bad' : { $type : 1 } } ).forEach( function (x) {   
  x.bad = new String(x.bad); // convert field to string
  db.foo.save(x);
});
Run Code Online (Sandbox Code Playgroud)

  • 在Int32-> String的情况下,`new String(x.bad)`创建具有0-index-item`x.bad`值的字符串集合.Simone描述的Variant"""+ x.bad"可以根据需要工作 - 创建String值而不是Int32 (23认同)
  • 面临此错误的`.save不是函数` (3认同)
  • 有一种情况,我需要转换 _id 字段并且不与其他索引冲突:`db.questions.find({_id:{$type:16}}).forEach(function (x) { db.questions. remove({_id:x._id},true); x._id = ""+x._id; db.questions.save(x); });` (2认同)

小智 150

将String字段转换为Integer:

db.db-name.find({field-name: {$exists: true}}).forEach(function(obj) { 
    obj.field-name = new NumberInt(obj.field-name);
    db.db-name.save(obj);
});
Run Code Online (Sandbox Code Playgroud)

将整数字段转换为字符串:

db.db-name.find({field-name: {$exists: true}}).forEach(function(obj) {
    obj.field-name = "" + obj.field-name;
    db.db-name.save(obj);
});
Run Code Online (Sandbox Code Playgroud)

  • 我跑了这个并得到错误:错误:无法将字符串转换为整数(shell):1 (3认同)

Xav*_*hot 41

开始Mongo 4.2db.collection.update()可以接受聚合管道,最后允许根据自己的值更新字段:

// { a: "45", b: "x" }
// { a:  53,  b: "y" }
db.collection.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $toString: "$a" } } }],
  { multi: true }
)
// { a: "45", b: "x" }
// { a: "53", b: "y" }
Run Code Online (Sandbox Code Playgroud)
  • 第一部分{ a : { $type: 1 } }是匹配查询:

    • 它过滤要更新的文档。
    • 在这种情况下,由于我们希望"a"在其值为 double 时转换为字符串,因此它匹配"a"类型为1(double) 的元素。
    • 提供了代表不同可能类型的代码。
  • 第二部分[{ $set: { a: { $toString: "$a" } } }]是更新聚合管道:

    • 请注意方括号表示此更新查询使用聚合管道。
    • $set是一个新的聚合运算符 ( Mongo 4.2),在这种情况下它会修改一个字段。
    • 这可以简单地读取为to convert"$set"的值。"a""$a""$toString"
    • 这里真正的新功能是Mongo 4.2在更新文档时能够引用文档本身: 的新值"a"基于 的现有值"$a"
    • 还要注意"$toString"哪个是 中引入的新聚合运算符Mongo 4.0
  • 不要忘记{ multi: true },否则只会更新第一个匹配的文档。


如果您的转换不是从 double 到 string,您可以选择不同的转换运算符,Mongo 4.0例如$toBool, $toInt, ...

And if there isn't a dedicated converter for your targeted type, you can replace { $toString: "$a" } with a $convert operation: { $convert: { input: "$a", to: 2 } } where the value for to can be found in this table:

db.collection.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $convert: { input: "$a", to: 2 } } } }],
  { multi: true }
)
Run Code Online (Sandbox Code Playgroud)

  • `db.collection.updateMany( { a : { $type: 1 } }, [{ $set: { a: { $toString: "$a" } } }] )` - 可以避免 `multi : true`使用“updateMany” (2认同)
  • 截至 2020 年,使用 $convert 应该是正确的方法,因为它应该更加高效(并且更易于启动)。 (2认同)
  • @ShashankAgrawal 将大的 double 值转换为字符串,避免使用科学记数法,首先必须将其转换为 Long (2认同)

Dav*_*han 37

用于字符串到int的转换.

db.my_collection.find().forEach( function(obj) {
    obj.my_value= new NumberInt(obj.my_value);
    db.my_collection.save(obj);
});
Run Code Online (Sandbox Code Playgroud)

用于字符串到双重转换.

    obj.my_value= parseInt(obj.my_value, 10);
Run Code Online (Sandbox Code Playgroud)

对于浮动:

    obj.my_value= parseFloat(obj.my_value);
Run Code Online (Sandbox Code Playgroud)

  • **注意**,我用Robomongo测试了这个,这导致了1型:双倍.不得不使用`new NumberInt()` (5认同)
  • 我建议还指定`radix` - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt (2认同)

小智 22

db.coll.find().forEach(function(data) {
    db.coll.update({_id:data._id},{$set:{myfield:parseInt(data.myfield)}});
})
Run Code Online (Sandbox Code Playgroud)


chr*_*dam 8

要将字符串类型的字段转换为日期字段,您需要find()使用该forEach()方法迭代该方法返回的游标,在循环中将该字段转换为 Date 对象,然后使用该$set运算符更新该字段。

利用Bulk API进行批量更新,它提供更好的性能,因为您将分批发送操作到服务器,例如 1000 次,这会为您提供更好的性能,因为您不是将每个请求发送到服务器,而是每次发送一次1000 个请求。

下面演示了这种方法,第一个示例使用 MongoDB 版本中可用的 Bulk API >= 2.6 and < 3.2。它通过将所有created_at字段更改为日期字段来更新集合中的所有文档:

var bulk = db.collection.initializeUnorderedBulkOp(),
    counter = 0;

db.collection.find({"created_at": {"$exists": true, "$type": 2 }}).forEach(function (doc) {
    var newDate = new Date(doc.created_at);
    bulk.find({ "_id": doc._id }).updateOne({ 
        "$set": { "created_at": newDate}
    });

    counter++;
    if (counter % 1000 == 0) {
        bulk.execute(); // Execute per 1000 operations and re-initialize every 1000 update statements
        bulk = db.collection.initializeUnorderedBulkOp();
    }
})
// Clean up remaining operations in queue
if (counter % 1000 != 0) { bulk.execute(); }
Run Code Online (Sandbox Code Playgroud)

下一个示例适用于新的 MongoDB 版本3.2,该版本已弃用 Bulk API并使用以下方法提供一组较新的 api bulkWrite()

var bulkOps = [];

db.collection.find({"created_at": {"$exists": true, "$type": 2 }}).forEach(function (doc) { 
    var newDate = new Date(doc.created_at);
    bulkOps.push(         
        { 
            "updateOne": { 
                "filter": { "_id": doc._id } ,              
                "update": { "$set": { "created_at": newDate } } 
            }         
        }           
    );     
})

db.collection.bulkWrite(bulkOps, { "ordered": true });
Run Code Online (Sandbox Code Playgroud)


use*_*725 7

到目前为止,所有答案都使用某些版本的forEach,在客户端迭代所有集合元素。

但是,您可以通过使用聚合管道和$ out阶段来使用MongoDB的服务器端处理:

$ out阶段用新的结果集合原子替换现有的集合。

例:

db.documents.aggregate([
         {
            $project: {
               _id: 1,
               numberField: { $substr: ['$numberField', 0, -1] },
               otherField: 1,
               differentField: 1,
               anotherfield: 1,
               needolistAllFieldsHere: 1
            },
         },
         {
            $out: 'documents',
         },
      ]);
Run Code Online (Sandbox Code Playgroud)

  • 我不知道为什么还不赞成这个。大数据集上的逐行操作对性能造成杀害 (2认同)