【问题标题】:MongoDB convert string type to float typeMongoDB 将字符串类型转换为浮点类型
【发布时间】:2025-11-25 16:25:01
【问题描述】:

按照此处MongoDB: How to change the type of a field? 的建议,我尝试更新我的集合以更改字段的类型及其值。

这里是更新查询

db.MyCollection.find({"ProjectID" : 44, "Cost": {$exists: true}}).forEach(function(doc){
    if(doc.Cost.length > 0){
        var newCost = doc.Cost.replace(/,/g, '').replace(/\$/g, '');
        doc.Cost =  parseFloat(newCost).toFixed(2);  
        db.MyCollection.save(doc);
        } // End of If Condition
    }) // End of foreach

完成上述查询后,当我运行以下命令时

db.MyCollection.find({"ProjectID" : 44},{Cost:1})

我仍然有 Cost 字段作为字符串。

{
    "_id" : ObjectId("576919b66bab3bfcb9ff0915"),
    "Cost" : "11531.23"
}

/* 7 */
{
    "_id" : ObjectId("576919b66bab3bfcb9ff0916"),
    "Cost" : "13900.64"
}

/* 8 */
{
    "_id" : ObjectId("576919b66bab3bfcb9ff0917"),
    "Cost" : "15000.86"
}

我在这里做错了什么?

这里是示例文档

/* 2 */
{
    "_id" : ObjectId("576919b66bab3bfcb9ff0911"),
    "Cost" : "$7,100.00"
}

/* 3 */
{
    "_id" : ObjectId("576919b66bab3bfcb9ff0912"),
    "Cost" : "$14,500.00"
}

/* 4 */
{
    "_id" : ObjectId("576919b66bab3bfcb9ff0913"),
    "Cost" : "$12,619.00"
}

/* 5 */
{
    "_id" : ObjectId("576919b66bab3bfcb9ff0914"),
    "Cost" : "$9,250.00"
}

【问题讨论】:

  • @chridam 请使用这个 { "_id" : ObjectId("576919b66bab3bfcb9ff0915"), "Cost" : "$11,531.23" }
  • @user3100115 更新了示例文档。

标签: mongodb mongo-shell


【解决方案1】:

问题是toFixed 返回一个String,而不是Number。那么你只是用一个新的、不同的String来更新文档。

Mongo Shell 示例:

> number = 2.3431
2.3431
> number.toFixed(2)
2.34
> typeof number.toFixed(2)
string

如果你想要一个 2 位小数,你必须用类似这样的东西再次解析它:

db.MyCollection.find({"ProjectID" : 44, "Cost": {$exists: true}}).forEach(function(doc){
  if(doc.Cost.length > 0){
    var newCost = doc.Cost.replace(/,/g, '').replace(/\$/g, '');
    var costString = parseFloat(newCost).toFixed(2);
    doc.Cost = parseFloat(costString);
    db.MyCollection.save(doc);
  } // End of If Condition
}) // End of foreach

【讨论】:

  • 感谢您的详细解答。
【解决方案2】:

按照此模式将字符串类型的货币字段转换为浮点数。您需要查询集合中具有 Cost 字段类型字符串的所有文档。为此,您需要利用 Bulk API 进行批量更新。这些提供了更好的性能,因为您将以 1000 个批量发送操作到服务器,这为您提供了更好的性能,因为您不是将每个请求都发送到服务器,而是每 1000 个请求发送一次。

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

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

db.MyCollection.find({ 
    "Cost": { "$exists": true, "$type": 2 } 
}).forEach(function (doc) {
    var newCost = Number(doc.Cost.replace(/[^0-9\.]+/g,"")); 
    bulk.find({ "_id": doc._id }).updateOne({ 
        "$set": { "Cost": newCost }
    });

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

下一个示例适用于新的 MongoDB 版本 3.2,该版本从 deprecated 开始具有 Bulk API,并使用 bulkWrite() 提供了一组更新的 api。

它使用与上面相同的游标,但使用相同的 forEach() 游标方法创建具有批量操作的数组,以将每个批量写入文档推送到数组。因为写入命令可以接受不超过 1000 次操作,所以您需要将操作分组为最多 1000 次操作,并在循环达到 1000 次迭代时重新初始化数组:

var cursor = db.MyCollection.find({ "Cost": { "$exists": true, "$type": 2 } }),
    bulkUpdateOps = [];

cursor.forEach(function(doc){ 
    var newCost = Number(doc.Cost.replace(/[^0-9\.]+/g,""));
    bulkUpdateOps.push({ 
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": { "$set": { "Cost": newCost } }
         }
    });

    if (bulkUpdateOps.length == 1000) {
        db.MyCollection.bulkWrite(bulkUpdateOps);
        bulkUpdateOps = [];
    }
});         

if (bulkUpdateOps.length > 0) { db.MyCollection.bulkWrite(bulkUpdateOps); }

【讨论】:

  • Number(...) 会保留十进制值吗?
  • 是的,Number() 是一个包装对象,允许您使用数值。
  • 如果我可以问“$type”的意义是什么:2
  • 这是非常棒的信息......谢谢
  • 这是最好的答案 - 对任何规模很大的数据集(>20,000 个文档)执行此查询都可能会失败。使用 BulkOps 是正确的方法。
最近更新 更多